차체 속도와 관절 목표의 계층을 구분한다
모바일 로봇에 '앞으로 0.2 m/s, 왼쪽으로 초당 0.5 rad 회전'을 주는 것은 바퀴 관절에 '0.2 rad로 가라'는 것과 다르다. 차체의 선속도/회전속도를 바퀴 각속도로 바꾸는 기구학과 그 관절을 움직이는 구동 경로가 필요하다. 모델에 바퀴/관절이 보인다는 사실만으로 구동 system이 그 관절을 제어하는 것은 아니다. joint 이름/축·반지름/간격과 모드를 연결한다.
위치 목표는 rad 같은 현재/목표 각도, 속도 목표는 rad/s 같은 시간당 변화다. 토크 명령은 N·m이며 서로 대체할 수 없다. Sim 8 JointController와 JointPositionController는 다른 역할/설정을 갖는다. 내부에 force 기반 제어가 있어도 입력 목표의 의미를 먼저 확인한다. 이름에 controller가 들어갔다고 모든 mode를 같은 숫자로 쓸 수 있는 것은 아니다.
차동 system과 별도 joint controller가 같은 바퀴를 동시에 지배하면 어떤 명령이 실제 적용되는지 모호해질 수 있다. 누가 관절 명령을 소유하고 전환/중단·제한과 피드백을 관리하는지 정한다. 이 단위의 기본 경로는 하나의 DiffDrive system이 두 바퀴를 구동하는 가상 구성이다. 실제 토크/주행을 실행하지 않는다.
두 바퀴의 양의 방향을 정하고 계산한다
평면에서 차체 x는 전방, y는 왼쪽, z는 위로 정하고 양의 yaw는 위에서 볼 때 반시계다. 왼쪽/오른쪽 바퀴의 양의 각속도는 각각 로봇을 앞으로 굴리는 방향으로 정의한다. 실제 모델의 joint 축/부호가 이 정의와 일치하는지는 별도 확인한다. 바퀴 반지름 r(m)>0, 좌우 바퀴의 유효 간격 L(m)>0, 차체 전방 속도 v(m/s), yaw rate Ω(rad/s)다. 바퀴 각속도는 ω_L/ω_R(rad/s)로 쓴다.
이상적인 평면 비미끄럼 구름과 같은 반지름의 두 바퀴를 가정하면 각 바퀴의 선속도 rω가 차체 속도와 회전에 따른 차이를 만든다. 따라서 다음 관계를 사용한다.
r=0.1 m/L=0.4 m, v=0.2 m/s/Ω=0.5 rad/s면 LΩ/2=0.1 m/s다. 왼쪽은 (0.2−0.1)/0.1=1 rad/s, 오른쪽은 (0.2+0.1)/0.1=3 rad/s다. 역으로 합/차를 넣으면 v=0.2/Ω=0.5가 된다. 오른쪽 바퀴가 더 빨라 왼쪽으로 도는 부호도 확인한다.
같은 바퀴 속도를 r=0.05 m로 해석하면 v=0.1/Ω=0.25가 된다. 같은 r=0.1에서 L을 0.8 m로 잘못 넣으면 v는 0.2지만 Ω는 0.25다. 이 변형은 어떤 항목이 어떤 추정/명령에 영향을 주는지 보여 준다. 실제 바퀴/모형의 반지름과 controller/odom의 설정값 중 어디가 잘못되었는지는 별도 기록으로 구별한다. 미끄럼/접촉·구동 포화가 있으면 이상 구름식과 실제 궤적이 다를 수 있다.
system plugin은 설정/모델에 연결된 동작 코드다
plugin은 실행 중 로드되는 동작 코드이고 system plugin은 Gazebo의 갱신 과정에서 모델/월드 상태와 상호작용한다. Sim 8 DiffDrive 공식 문서는 left_joint/right_joint, wheel_radius/wheel_separation과 명령/odom 토픽 등을 설명한다. 기본값에 기대지 않고 이 과제의 r=0.1/L=0.4와 정확한 joint 이름을 명시한다.
그림 M09.02-1. 각 칸은 서로 다른 역할/근거다. 아래 방향은 자료/처리의 연결이며 실제 성공을 자동으로 보장하지 않는다. 직접 제작한 개념도이며 본문의 조건과 함께 읽는다.
가상 모델 이름은 mobile_demo이고 명령 Transport 토픽을 /model/mobile_demo/cmd_vel, odom을 /model/mobile_demo/odometry로 설정했다고 하자. bridge/ROS의 이름이 다르면 양쪽 이름을 함께 기록한다. plugin 로드 오류/다른 entity 연결·관절 축/이름 오류는 메시지가 존재해도 로봇을 기대처럼 움직이지 못하게 한다. pause/physics 상태와 모델이 static인지도 확인한다.
bridge는 메시지 형식을 연결하며 제어를 대신하지 않는다
ROS 2와 Gazebo Transport는 다른 통신 계층이다. Harmonic 통합 문서와 ros_gz_bridge Jazzy README의 지원 형식/방향을 확인한다. bridge는 지원하는 메시지/필드를 변환해 전달하며 물리 제어·기구학/SLAM을 대신하는 기능이 아니다. 메시지가 변환되었다고 frame/단위·시각/의미가 올바른지도 자동 증명되지 않는다.
다음은 이 가상 토픽 쌍을 위한 완전한 bridge YAML 구성 예다. 로봇/월드를 만드는 파일은 별도이며 이 구성만으로 시뮬레이션이 실행되는 것은 아니다. 기본 실습에서는 내용을 읽고 방향/형식만 판정한다.
- ros_topic_name: /cmd_vel
gz_topic_name: /model/mobile_demo/cmd_vel
ros_type_name: geometry_msgs/msg/Twist
gz_type_name: gz.msgs.Twist
direction: ROS_TO_GZ
- ros_topic_name: /odom
gz_topic_name: /model/mobile_demo/odometry
ros_type_name: nav_msgs/msg/Odometry
gz_type_name: gz.msgs.Odometry
direction: GZ_TO_ROS
명령은 ROS→Gazebo이고 odom은 Gazebo→ROS다. /cmd_vel을 반대로 연결하면 ROS publisher가 있어도 기대한 Transport 구독자로 전달되지 않는다. 이 예의 Twist는 header가 없는 형식이므로 관측/발행 시각·만료/출처를 추적할 별도 계약이 필요하다. 다른 구성의 TwistStamped 지원과 형식/시각 보존도 해당 버전/설정으로 확인한다.
Odometry는 위치/방향·속도와 frame/child_frame 등의 상태 정보를 담는 형식이다. bridge로 /odom이 생겼다고 독립 위치 기준과 일치하거나 odom→base_link TF까지 올바르게 연결되었다고 확정하지 않는다. 어떤 시스템이 odom을 계산/발행하고 TF를 소유하는지·시각/축/공분산과 별도 기준을 확인한다. frame의 자세한 역할은 M09.04에서 다룬다.
경로를 한 구간씩 관찰해 원인을 좁힌다
합성 로그가 'ROS /cmd_vel 발행, Transport 명령 수신 없음, 바퀴 상태/odom 미상'이면 ROS 발행까지만 확인되었다. bridge의 두 이름/형식/방향·구독 연결과 실행 상태부터 구별한다. 반대로 Transport 수신은 있는데 바퀴가 멈췄다면 plugin/joint·mode/physics/제한·현재 상태 경로를 조사한다. 아직 없는 증거로 bridge/모터 고장을 단정하지 않는다.
바퀴가 돌지만 odom 부호가 반대라면 좌우 이름/축·frame·수치 설정과 명령/상태의 시점을 점검한다. 두 바퀴/차체 속도·기준 pose/clock을 같은 ID/구간으로 비교한다. 기존 3.6 JointState 브리지와 6.2 정량 진단은 관절/시간·경로 추적을 연결하는 보충이다. 해당 예제 API/팔 단위를 Gazebo 차동 구동에 그대로 복사하지 않는다.