AEGLOSLAB ROS 2 Jazzy 대전

제10부 시뮬레이션

10.4 gz_ros2_control과 시뮬 하드웨어

10.4.1 이 장에서 배우는 것 / 선수 지식

6.1.7에서 하드웨어 플러그인만 바꾸면 시뮬과 실기가 같은 코드로 실행된다고 했다. 이 장이 그 시뮬 쪽 플러그인이다.

이것이 ros2_control 아키텍처의 가장 큰 실무적 이득이다. 컨트롤러·MoveIt 설정·Nav2 설정이 그대로 재사용되고, 시뮬에서 검증한 것이 실기로 넘어간다. 5.6.6의 "단일 xacro 분기"가 여기서 완성된다.

다만 완전히 같지는 않다. 시뮬 관절은 이상적이고, PID가 다르게 동작하며, 마찰·유격이 없다. 이 장은 그 차이도 함께 다룬다.

배우는 것:

  • 구조 — 시뮬 하드웨어 플러그인의 위치.
  • 설정 — 같은 URDF, 다른 플러그인.
  • 시뮬 관절의 성질 — 이상적 서보와 그 함의.
  • PID와 동역학 — 시뮬과 실기의 차이.
  • 무엇이 이전되고 무엇이 안 되는가 — 게인·타이밍.
  • mimic 관절 — 그리퍼의 흔한 문제.
  • 진단 — 시뮬에서 제어가 안 될 때.

선수 지식: 6.1~6.5(ros2_control 전반), 6.2.8(mock 하드웨어), 5.6.6(xacro 분기), 10.2(Gazebo), 5.3.5(관성).

정직: gz_ros2_control의 패키지명·플러그인명·파라미터는 Gazebo·ROS 배포판 조합에 따라 다르다. Classic용(gazebo_ros2_control)과 신 Gazebo용이 별개다(10.2.2).

실행 확인(2026-08-16): ros-jazzy-gz-ros2-control 1.2.19를 설치해 이 장이 대는 이름 셋을 실물과 대조했다 — 전부 일치한다.

이 장의 표기실물
SDF filename="gz_ros2_control-system"libgz_ros2_control-system.so ✅
SDF name="gz_ros2_control::GazeboSimROS2ControlPlugin"라이브러리 심볼로 확인 ✅
하드웨어 <plugin>gz_ros2_control/GazeboSimSystem</plugin>플러그인 XML에 name="gz_ros2_control/GazeboSimSystem" 슬래시 조회 이름 그대로 등록 ✅

덤으로 하나 더 있다 — 같은 XML에 name="ign_ros2_control/IgnitionSystem"이 하위 호환용으로 함께 등록돼 있다("For backward compatibility with old ign* plugin names"). Ignition 시절 설정을 물려받았다면 그 이름도 여전히 동작한다는 뜻이다.

다만 파라미터·실제 제어 동작은 검증하지 않았다(확인 필요) — 헤드리스 컨테이너에서 시뮬레이터를 구동하지 않았다(목차 3).

10.4.2 구조

state_interface 관절 상태 컨트롤러 joint_trajectory_controller 등 Resource Manager 6.3 gz_ros2_control 시뮬 하드웨어 컴포넌트 Gazebo 관절 인터페이스 물리 엔진

핵심: gz_ros2_control은 하드웨어 컴포넌트다(6.2). 실기의 시리얼·CAN 드라이버 자리에 들어가며, read()·write()가 Gazebo 관절 API를 호출한다.

층실기시뮬
컨트롤러동일동일
Resource Manager동일동일
하드웨어 컴포넌트자체 드라이버(6.2)gz_ros2_control
아래시리얼·CAN(6.7)Gazebo 물리

견해: 이 구조가 제6부·제9부·제10부를 잇는다. 컨트롤러 설정(6.5)·MoveIt 설정(9.1)·Nav2 설정(8.1)이 시뮬과 실기에서 동일하므로, 시뮬에서 튜닝한 대부분이 실기로 넘어간다. 예외는 10.4.5에서 다룬다.

Gazebo 시스템 플러그인으로 로드된다(10.2.6):

<!-- URDF의 <gazebo> 태그 안 -->
<gazebo>
  <plugin filename="gz_ros2_control-system"
          name="gz_ros2_control::GazeboSimROS2ControlPlugin">
    <parameters>$(find my_robot_bringup)/config/controllers.yaml</parameters>
    <controller_manager_name>controller_manager</controller_manager_name>
  </plugin>
</gazebo>

정직: 플러그인 파일명·클래스명은 버전 의존이다(확인 필요).

이 플러그인이 controller_manager를 시뮬 안에서 실행한다 — 별도 ros2_control_node를 실행하지 않는다.

함정: 실기용 launch에서 ros2_control_node를 실행하고, 시뮬에서는 실행하지 않아야 한다. 둘 다 기동되면 컨트롤러가 중복되어 충돌한다. launch를 조건부로 구성해야 한다.

10.4.3 설정

5.6.6·6.1.7의 xacro 분기가 여기서 실체를 갖는다.

<ros2_control name="MyRobotSystem" type="system">

  <hardware>
    <xacro:if value="${use_mock}">
      <plugin>mock_components/GenericSystem</plugin>
    </xacro:if>
    <xacro:if value="${use_sim}">
      <plugin>gz_ros2_control/GazeboSimSystem</plugin>
    </xacro:if>
    <xacro:unless value="${use_mock or use_sim}">
      <plugin>my_robot_driver/MyRobotHardware</plugin>
      <param name="device">/dev/robot_arm</param>
    </xacro:unless>
  </hardware>

  <!-- ★ 관절 정의는 완전히 공통 -->
  <joint name="joint1">
    <command_interface name="position">
      <param name="min">-3.14</param>
      <param name="max">3.14</param>
    </command_interface>
    <state_interface name="position"/>
    <state_interface name="velocity"/>
    <state_interface name="effort"/>
  </joint>
  <!-- ... -->

</ros2_control>

세 모드가 하나의 파일에서 나온다(6.2.8):

모드하드웨어물리용도
mock없음없음로직·CI(빠름)
sim없음Gazebo동역학 검증
real실제실제최종

견해: 세 모드의 관절·인터페이스 정의가 동일한 것이 핵심 가치다. 컨트롤러 YAML(6.5.9)이 그대로 재사용되고, 5.7.9에서 검증한 "시뮬·실기 관절 이름 일치" 가 구조적으로 보장된다.

초기 관절 위치:

<joint name="joint1">
  <command_interface name="position"/>
  <state_interface name="position">
    <param name="initial_value">0.0</param>     <!-- 시뮬 시작 자세 -->
  </state_interface>
</joint>

함정: 초기 위치를 설정하지 않으면 로봇이 0 자세로 시작한다. 팔이 접힌 자세가 자연스러운데 0이 뻗은 자세면, 시작 순간 중력으로 떨어지거나 바닥과 충돌한다. initial_value로 안전한 자세를 지정하라.

10.4.4 시뮬 관절의 성질

Gazebo 관절은 실제 액추에이터와 다르다.

항목실제시뮬
위치 제어서보가 내부 제어엔진이 직접 위치 설정 또는 PID
백래시·유격있음없음
마찰복잡·비선형단순 모델
탄성·유연성있음대개 강체
토크 한계물리적설정값
엔코더 분해능유한무한(연속값)
지연통신·제어거의 없음
온도 영향있음없음

위치 제어 방식이 특히 중요하다:

방식동작
직접 설정(kinematic)명령한 위치로 즉시 이동 — 물리 무시
PID 제어힘·토크로 목표 추종 — 물리 반영

정직: gz_ros2_control이 어느 방식을 쓰는지, 설정 가능한지는 버전 의존이다(확인 필요). 이것이 시뮬 결과 해석에 결정적이므로 반드시 확인해야 한다.

견해: "직접 설정" 방식이면 동역학 검증이 무의미하다. 관절이 명령대로 완벽히 움직이므로 관성·중력·토크 한계가 반영되지 않는다. 로직 검증에는 충분하지만(10.1.4의 ①·② 사이), 제어 튜닝이나 물리 검증에는 PID 방식이 필요하다.

PID 설정:

# 시뮬 관절 PID (확인 필요: 설정 위치·형식)
gazebo_ros2_control:
  ros__parameters:
    joint1:
      p: 100.0
      i: 0.01
      d: 10.0

함정: 시뮬 PID 게인은 실기와 무관하다. 시뮬 게인은 Gazebo 물리 엔진과 관성 모델에 맞춘 값이고, 실기 게인은 실제 액추에이터에 맞춘 값이다. 둘을 혼동하면 안 된다 — 시뮬에서 튜닝한 게인을 실기에 그대로 넣으면 대개 불리하다.

10.4.5 무엇이 이전되고 무엇이 안 되는가

이것이 이 장의 핵심 판단이다.

항목이전
컨트롤러 설정 구조✅ 그대로
관절 이름·인터페이스✅ 그대로
궤적 생성 로직(MoveIt·Nav2)✅ 그대로
상위 상태 기계·BT✅ 그대로
허용오차 구조✅ 구조는, 값은 조정
속도·가속 한계⚠️ 실제 사양 기준이어야(9.1.4)
제어 게인❌ 다시 튜닝
궤적 추종 오차❌ 시뮬이 더 좋다
사이클 타임❌ 근사만
진동·공진❌ 시뮬에 없음
실시간 타이밍❌ 시뮬은 다르다(6.6)
통신 오류❌ 없음(6.7)

견해: "구조는 이전되고 수치는 안 된다"가 요약이다. 컨트롤러 종류·관절 매핑·인터페이스 구성은 그대로 쓰지만, 게인·허용오차·타이밍은 실기에서 다시 정해야 한다.

견해: 그럼에도 이전되는 것이 많다는 점이 큰 가치다. 컨트롤러 설정이 틀렸거나, 관절 이름이 안 맞거나, MoveIt 궤적이 비정상이거나, BT 로직이 잘못된 것은 시뮬에서 전부 검출된다. 실기에서 검출하면 위험하고 느리다.

시뮬에서 검증할 것 / 실기에서 할 것:

시뮬에서실기에서
컨트롤러 로드·활성화(6.3)게인 튜닝
관절 매핑 정확성추종 오차 측정
궤적 생성·실행 흐름허용오차 결정(6.5.5)
충돌·도달성(9.3)진동·공진 확인
상태 전이·복구실시간 주기(6.6.6)
안전 로직 흐름통신 안정성(6.7.9)

10.4.6 mimic 관절

그리퍼에서 흔한 문제다(5.3.3·5.5.8).

<joint name="right_finger" type="prismatic">
  <mimic joint="left_finger" multiplier="-1.0" offset="0.0"/>
</joint>
소비자mimic 처리
robot_state_publisher✅ 처리(5.5.8)
RViz2✅ (RSP를 통해)
ros2_control⚠️ 지원 여부·방식 확인 필요
Gazebo 물리⚠️ 별도 처리 필요(확인 필요)
MoveIt⚠️(확인 필요)

정직: mimic 관절의 시뮬 처리는 이 장에서 확신이 낮은 부분이다. 물리 엔진은 mimic 개념이 없으므로, 플러그인이나 추가 관절 제약으로 구현해야 한다. 버전에 따라 지원 방식이 다르며(확인 필요), 지원하지 않으면 손가락 하나만 움직인다.

대안:

방법설명
두 관절 모두 제어mimic 없이 컨트롤러가 둘 다 명령
시뮬 전용 제약Gazebo 관절 제약 플러그인
단순화시뮬에서는 그리퍼를 한 관절로

견해: 파지 자체를 시뮬로 검증하기는 어렵다. 접촉 물리(마찰·변형·미끄러짐)가 시뮬에서 부정확해, 물체가 손에서 빠지거나 반대로 붙어 버린다. 파지 성공 여부보다 "파지 전후의 로직 흐름"을 검증하는 데 시뮬을 쓰는 것이 현실적이다(9.5.2의 단계 흐름).

흔한 우회: 시뮬에서 파지 시 물체를 그리퍼에 부착(attach) 시키는 플러그인을 쓴다(확인 필요: 제공 여부). 물리적으로 파지하는 대신 논리적으로 붙여, 이송·배치 로직을 검증한다.

10.4.7 관성과 물리 안정성

5.3.5에서 "관성을 부정확하게 넣으면 시뮬이 발산한다"고 했다. 여기가 그 무대다.

증상원인
로봇이 튀어 날아감관성 0·질량 0(5.3.5)
관절이 진동관성 대비 게인 과대·스텝 과대
팔이 중력에 처짐토크 부족·게인 부족
바닥을 뚫음물리 스텝 과대(10.2.8)
로봇이 미끄러짐마찰 계수
제자리에서 떨림접촉 불안정·관성 부적절

진단 순서:

아니오 예 시뮬 불안정 관성 검증 5.7.4 삼각 부등식 · 0 아님? URDF 수정 물리 스텝 축소
아니오 아니오 물리 스텝 축소 개선? PID 게인 조정 개선? 충돌 기하 · 접촉 검토
(계속 — 물리 스텝 축소 이후)

견해: "시뮬에서 로봇이 가만히 있는가"가 물리 모델의 통합 검사다(5.7.11의 반복). 아무 명령도 안 줬는데 떨리거나 움직이면 관성·마찰·게인 중 무언가가 잘못됐다. 이것을 먼저 해결하지 않고 제어를 시험하면 원인이 뒤섞인다.

함정: 관절 마찰·감쇠를 0으로 두면 진동하기 쉽다.

<joint name="joint1" type="revolute">
  <dynamics damping="0.1" friction="0.05"/>   <!-- 5.3.3 -->
</joint>

실제 로봇에는 항상 마찰이 있으므로, 약간의 감쇠를 주는 것이 안정성에 도움이 된다.

10.4.8 진단

# 컨트롤러가 로드됐는가 (6.3.8)
ros2 control list_hardware_components
ros2 control list_hardware_interfaces
ros2 control list_controllers

# 관절 상태가 나오는가 (5.5)
ros2 topic hz /joint_states
ros2 topic echo /joint_states --once

# 명령이 전달되는가
ros2 topic echo /joint_trajectory_controller/controller_state --once

# Gazebo 쪽 확인 (10.2.10)
gz topic -l | grep joint
gz model --list

증상별 원인:

증상원인
하드웨어 컴포넌트 없음플러그인 로드 실패·이름 오류
/joint_states 없음joint_state_broadcaster 미활성(6.5.3)
컨트롤러 활성화 실패인터페이스 불일치(6.3.2)
명령해도 안 움직임PID 게인·토크 한계·초기 자세
관절이 처짐중력 대비 게인·토크
한 손가락만 움직임mimic 미지원(10.4.6)
로봇이 불안정관성·스텝(10.4.7)
컨트롤러 중복ros2_control_node도 실행 중

견해: 시뮬 제어 문제의 절반은 "실기와 같은 문제"다(6.3.8의 진단표가 그대로 적용된다). 나머지 절반이 시뮬 특유(PID·물리·mimic)다. 먼저 ros2_control 일반 진단을 하고, 그래도 안 되면 시뮬 특유를 본다.

10.4.9 launch 구성

def generate_launch_description():
    use_sim = LaunchConfiguration('use_sim')

    robot_desc = ParameterValue(
        Command(['xacro ', xacro_file, ' use_sim:=', use_sim]), value_type=str)

    rsp = Node(package='robot_state_publisher', executable='robot_state_publisher',
               parameters=[{'robot_description': robot_desc, 'use_sim_time': use_sim}])

    # ★ 실기에서만 ros2_control_node (시뮬은 플러그인이 실행)
    control_node = Node(
        package='controller_manager', executable='ros2_control_node',
        parameters=[robot_desc, controllers_yaml],
        condition=UnlessCondition(use_sim),        # 조건부
    )

    # 스폰 (시뮬만)
    spawn = Node(
        package='ros_gz_sim', executable='create',
        arguments=['-topic', 'robot_description', '-name', 'my_robot', '-z', '0.15'],
        condition=IfCondition(use_sim),
    )

    # 컨트롤러 spawner — 양쪽 공통 (6.3.6)
    jsb = Node(package='controller_manager', executable='spawner',
               arguments=['joint_state_broadcaster'])
    jtc = Node(package='controller_manager', executable='spawner',
               arguments=['joint_trajectory_controller'])

    return LaunchDescription([
        DeclareLaunchArgument('use_sim', default_value='false'),
        rsp, control_node, spawn,
        RegisterEventHandler(OnProcessExit(target_action=spawn, on_exit=[jsb])),
        RegisterEventHandler(OnProcessExit(target_action=jsb, on_exit=[jtc])),
    ])

조건부 노드가 핵심이다:

노드시뮬실기
robot_state_publisher✅✅
ros2_control_node❌ 플러그인이 실행✅
create(스폰)✅❌
컨트롤러 spawner✅✅
브리지✅❌

견해: 하나의 launch에서 조건부로 구성하는 것과, 시뮬·실기 launch를 나누는 것 중 후자가 읽기 쉬울 수 있다. 조건이 많아지면 launch가 복잡해진다. 공통 부분을 별도 launch로 빼고 시뮬·실기가 각각 include하는 구조가 절충안이다(14.4).

함정: 시뮬에서 컨트롤러 spawner 타이밍이 중요하다. 로봇이 스폰되고 플러그인이 controller_manager를 실행한 뒤에 spawner가 연결돼야 한다. OnProcessExit 연쇄나 지연이 필요하다(6.3.6).

10.4.10 함정 카탈로그

#함정증상대처
1시뮬에서 ros2_control_node도 실행컨트롤러 중복·충돌조건부 launch
2초기 관절 위치 미설정시작 시 낙하·충돌initial_value
3시뮬 PID 게인을 실기에낮은 성능별도 튜닝
4시뮬 추종 오차로 허용오차 결정실기에서 중단 빈발실기 실측(6.5.5)
5위치 제어 방식 미확인동역학 검증이 무의미방식 확인
6mimic 시뮬 미지원손가락 하나만두 관절 제어·대안
7파지를 물리로 검증 시도불안정·비현실부착 플러그인·로직 검증
8관성 부실시뮬 발산(5.3.5)5.7.4 검증
9감쇠·마찰 0진동약간의 값 부여
10물리 스텝 과대관통·불안정1~5 ms(10.2.8)
11spawner 타이밍 오류컨트롤러 로드 실패연쇄 실행
12시뮬 사이클 타임을 실기 예측으로계획 오류근사만
13시뮬 특유 문제부터 의심진단 지연일반 진단 먼저(6.3.8)

10.4.11 체크포인트

  • gz_ros2_control이 ros2_control 구조에서 어느 자리인가. 실기와 무엇이 같고 무엇이 다른가.
  • 세 모드(mock·sim·real)를 하나의 xacro에서 만드는 구조와, 그 핵심 가치는?
  • 초기 관절 위치를 설정하지 않으면 무슨 일이 생기는가.
  • 시뮬 관절이 실제와 다른 점 여덟 가지를 말할 수 있는가.
  • 위치 제어의 두 방식과, "직접 설정"이면 무엇이 무의미해지는가.
  • 시뮬 PID 게인을 실기에 쓰면 안 되는 이유는?
  • "구조는 이전되고 수치는 안 된다"를 구체적 항목으로 설명할 수 있는가.
  • 시뮬에서 검증할 것과 실기에서 할 것을 각각 세 가지 들 수 있는가.
  • mimic 관절의 소비자별 지원 차이와, 시뮬에서의 대안 세 가지는?
  • 파지를 시뮬로 검증하기 어려운 이유와, 현실적 활용법은?
  • 시뮬 물리 불안정의 원인 여섯 가지와 진단 순서를 말할 수 있는가.
  • 시뮬·실기 launch 구성에서 조건부로 다뤄야 할 노드는?
  • 시뮬 제어 문제 진단에서 먼저 해야 할 것은?

다음 장 10.5은 Gazebo 외의 선택지 — Isaac Sim 등 대안 시뮬레이터를 다룬다. 무엇이 있고, 어떤 축으로 비교하며, 언제 다른 것을 검토하는가.