3.6 ROS 2 JointState 브리지
3.6.1 토픽 계약
이 장의 예제 토픽은 다음과 같다.
| 토픽 | 타입 | 발행자 | 구독자 | 의미 |
|---|---|---|---|---|
/leader/joint_states | sensor_msgs/msg/JointState | Leader 드라이버 | 안전 브리지 | 측정 관절 상태 |
/follower/joint_command | sensor_msgs/msg/JointState | 안전 브리지 | 교육용 드라이버 어댑터 | 제한된 목표 위치 |
/follower/joint_states | sensor_msgs/msg/JointState | Follower 드라이버 | 시각화/Isaac Sim | 실측 상태 |
표준 JointState는 상태 표현이다. 실제 제어기에서는 trajectory_msgs/msg/JointTrajectory, control_msgs/action/FollowJointTrajectory, ros2_control command interface 또는 장치 전용 API가 더 적절할 수 있다. 여기서 명령 토픽에 JointState를 쓰는 것은 메시지 정렬과 안전 필터를 학습하기 위한 교육용 계약이며, 생산 시스템의 표준 명령 인터페이스라고 주장하지 않는다.
3.6.2 QoS 선택
관절 상태는 최신 값이 중요하므로 작은 큐 깊이와 best-effort를 고려할 수 있다. 반면 명령 손실이 위험한 시스템은 reliable을 선택하되 지연 증가와 backpressure를 측정한다. 시뮬레이터, DDS 구현, 네트워크에 따라 결과가 다르므로 QoS를 하드코딩된 정답이 아니라 인터페이스 계약으로 문서화한다.
code/ros2_ws/src/so101_training_bridge/는 두 노드를 제공한다.
safe_joint_bridge.py: Leader 상태를 이름으로 재정렬하고 위치·속도·timeout을 제한해 교육용 명령 토픽으로 발행joint_state_monitor.cpp: Follower 상태의 관절 누락, 비유한 값, 오래된 타임스탬프를 감시
3.6.3 robot_state_publisher와 TF
robot_state_publisher는 URDF와 측정 joint_states로 링크 변환을 계산한다. 고정 관절은 시작 시 /tf_static에, 움직이는 관절은 상태 갱신에 따라 /tf에 발행한다. 이 노드는 위치 목표를 생성하거나 모터를 구동하지 않는다. 실물과 시뮬레이터가 같은 TF 버퍼에 동시에 같은 child 프레임을 발행하면 충돌하므로 명시적인 프레임 접두사를 둔다.
- 실물:
real/base_link,real/gripper_frame_link - 시뮬레이터:
sim/base_link,sim/gripper_frame_link
서로 다른 ROS domain으로 그래프를 격리할 수도 있다. namespace만 바꾸면 메시지 안의 frame_id 문자열은 자동으로 바뀌지 않는다. frame_prefix 또는 모델의 프레임 이름·모든 관련 센서의 header.frame_id를 일관되게 설정하고, TF 토픽을 공유할지 별도 버퍼로 나눌지 정한다. 격리된 그래프를 브리지할 때도 프레임 계약을 다시 확인한다. 정적 카메라 외부 파라미터와 동적 관절 TF를 중복 발행하지 않는다.
3.6.4 Isaac Sim 미러링
실물 측정값을 가상 articulation에 적용하는 기본 흐름은 다음과 같다.
/follower/joint_states를 구독한다.- 이름 배열을 USD articulation의 DoF 이름으로 매핑한다.
- radian/degree와 그리퍼 단위를 변환한다.
- 시뮬레이션 시간에 맞춰 최신 상태를 적용한다.
- 측정값을 시각화할 때는 물리 드라이브 목표와 순간 상태 설정의 차이를 구분한다.
센서 기반 디지털 트윈은 “완벽한 동시성”이 아니라 타임스탬프가 있는 두 상태의 정렬 문제다. 네트워크 지연을 숨기기 위해 미래를 추정하면 모니터링 화면은 부드러워지지만 실제 측정과 달라질 수 있으므로 추정 상태를 별도 색상/프레임으로 표시한다.