ROS 2 강의용 블로그: 모바일 로봇 시뮬레이션에서 Friction과 Inertia 설정하기

1. 강의 목표

이번 글에서는 ROS 2 모바일 로봇 시뮬레이션에서 반드시 이해해야 하는 두 가지 물리 설정을 다룹니다.

  1. Friction 설정
    • 바퀴가 바닥에서 미끄러지는 이유
    • mu, mu2, slip1, slip2의 의미
    • Gazebo에서 모바일 로봇 바퀴 마찰을 설정하는 방법
  2. Inertia 설정
    • 로봇 링크에 질량과 관성 모멘트를 넣어야 하는 이유
    • URDF/Xacro에서 <inertial> 태그 작성법
    • 박스형 바디와 원통형 바퀴의 관성 계산법

2. 왜 Friction과 Inertia가 중요한가?

ROS 2에서 로봇 모델을 만들 때 단순히 모양만 보이게 하는 것은 어렵지 않습니다. visual 태그만 있어도 RViz에서는 로봇이 그럴듯하게 보입니다. 하지만 Gazebo 같은 물리 시뮬레이터에서는 이야기가 다릅니다.

Gazebo에서 로봇이 정상적으로 움직이려면 각 링크에 다음 정보가 필요합니다.

  • 충돌 형상: <collision>
  • 질량: <mass>
  • 관성 모멘트: <inertia>
  • 바닥과 접촉하는 부분의 마찰: <friction>

URDF를 Gazebo에서 사용하려면 각 링크에 <inertia> 요소가 제대로 지정되어야 합니다. 또한 URDF 자체는 마찰 같은 시뮬레이션 전용 속성을 충분히 표현하지 못하기 때문에 Gazebo 확장 태그나 SDF 설정을 함께 사용합니다.

즉, 모바일 로봇이 다음처럼 이상하게 움직인다면 대부분 Friction 또는 Inertia 설정 문제입니다.

  • 바퀴가 도는데 로봇이 앞으로 안 감
  • 제자리에서 계속 미끄러짐
  • 가만히 있어도 로봇이 덜덜 떨림
  • 회전 명령을 줬는데 이상하게 직진함
  • 바퀴가 땅에 닿았는데 접지력이 없음
  • 로봇이 너무 쉽게 뒤집힘
  • 시뮬레이션이 불안정하게 튐

3. 기본 구조: URDF, Xacro, Gazebo, SDF 관계

ROS 2 모바일 로봇 모델은 보통 URDF 또는 Xacro로 작성합니다.

robot_description/
 ├── urdf/
 │   └── mobile_robot.urdf.xacro
 ├── launch/
 │   └── display.launch.py
 └── package.xml

URDF는 ROS에서 로봇의 링크와 조인트를 표현하는 표준 형식입니다. 하지만 Gazebo는 내부적으로 SDF 형식을 사용합니다. Gazebo Classic 문서에 따르면 URDF를 Gazebo에서 사용할 때 Gazebo가 내부적으로 URDF를 SDF로 변환합니다.

정리하면 다음과 같습니다.

Xacro
  ↓ 변환
URDF
  ↓ Gazebo가 내부 변환
SDF
  ↓
Gazebo 물리 시뮬레이션

4. Inertia란?

Inertia는 한국어로 관성 또는 관성 모멘트라고 합니다.

쉽게 말하면 다음과 같습니다.

질량 mass물체가 직선 운동 변화를 얼마나 버티는가
관성 모멘트 inertia물체가 회전 운동 변화를 얼마나 버티는가

예를 들어 같은 5kg 물체라도 질량이 중심에 몰려 있으면 회전하기 쉽고, 질량이 바깥쪽에 퍼져 있으면 회전하기 어렵습니다. 이것을 수치로 표현한 것이 관성 모멘트입니다.

Gazebo 물리 엔진은 로봇의 움직임을 계산할 때 질량과 관성 모멘트를 사용합니다. 모바일 로봇의 바디, 바퀴, 센서 링크에 관성값이 없거나 말이 안 되는 값이 들어가면 시뮬레이션이 불안정해집니다.

5. URDF의 <inertial> 기본 형식

URDF에서 관성 정보는 각 <link> 안에 작성합니다.

<link name="base_link">

  <visual>
    ...
  </visual>

  <collision>
    ...
  </collision>

  <inertial>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <mass value="5.0"/>
    <inertia
      ixx="0.1" ixy="0.0" ixz="0.0"
      iyy="0.1" iyz="0.0"
      izz="0.1"/>
  </inertial>

</link>

각 항목의 의미는 다음과 같습니다.

태그설명

<origin>질량 중심 위치. 보통 링크 중심과 같게 둠
<mass>링크 질량, 단위 kg
ixxx축 회전에 대한 관성 모멘트
iyyy축 회전에 대한 관성 모멘트
izzz축 회전에 대한 관성 모멘트
ixy, ixz, iyz관성곱. 대칭 형상에서는 보통 0

모바일 로봇 강의에서는 처음부터 복잡한 관성곱을 다루지 않아도 됩니다. 대부분의 박스형 바디, 원통형 바퀴는 중심축에 맞춰 모델링하면 ixy, ixz, iyz를 0으로 둘 수 있습니다.

관성계산식 정리 자료 :

6. 박스형 로봇 바디의 관성 계산

모바일 로봇의 본체를 직육면체라고 가정합니다.

가로 x = 0.40 m
세로 y = 0.30 m
높이 z = 0.15 m
질량 m = 5.0 kg

직육면체의 중심 기준 관성 모멘트는 다음과 같습니다.

Ixx = (1/12) * m * (y² + z²)
Iyy = (1/12) * m * (x² + z²)
Izz = (1/12) * m * (x² + y²)

계산하면 다음과 같습니다.

Ixx = (1/12) * 5.0 * (0.30² + 0.15²)
    = 0.046875

Iyy = (1/12) * 5.0 * (0.40² + 0.15²)
    = 0.0760417

Izz = (1/12) * 5.0 * (0.40² + 0.30²)
    = 0.104167

URDF/Xacro에서는 다음처럼 작성합니다.

<link name="base_link">
  <visual>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <geometry>
      <box size="0.40 0.30 0.15"/>
    </geometry>
  </visual>

  <collision>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <geometry>
      <box size="0.40 0.30 0.15"/>
    </geometry>
  </collision>

  <inertial>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <mass value="5.0"/>
    <inertia
      ixx="0.046875" ixy="0.0" ixz="0.0"
      iyy="0.0760417" iyz="0.0"
      izz="0.104167"/>
  </inertial>
</link>

7. 원통형 바퀴의 관성 계산

모바일 로봇 바퀴는 보통 원통형으로 모델링합니다.

예시 조건:

바퀴 반지름 r = 0.05 m
바퀴 폭 L = 0.03 m
질량 m = 0.2 kg

바퀴가 y축을 중심으로 회전한다고 가정하면 원통의 관성은 다음과 같이 둘 수 있습니다.

Iyy = (1/2) * m * r²
Ixx = Izz = (1/12) * m * (3r² + L²)

계산하면 다음과 같습니다.

Iyy = 0.5 * 0.2 * 0.05²
    = 0.00025

Ixx = Izz = (1/12) * 0.2 * (3 * 0.05² + 0.03²)
          = 0.00014

URDF 예시는 다음과 같습니다.

<link name="left_wheel_link">
  <visual>
    <origin xyz="0 0 0" rpy="1.5708 0 0"/>
    <geometry>
      <cylinder radius="0.05" length="0.03"/>
    </geometry>
  </visual>

  <collision>
    <origin xyz="0 0 0" rpy="1.5708 0 0"/>
    <geometry>
      <cylinder radius="0.05" length="0.03"/>
    </geometry>
  </collision>

  <inertial>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <mass value="0.2"/>
    <inertia
      ixx="0.00014" ixy="0.0" ixz="0.0"
      iyy="0.00025" iyz="0.0"
      izz="0.00014"/>
  </inertial>
</link>

주의할 점은 바퀴의 회전축입니다. URDF에서 cylinder 기본 축은 z축입니다. 하지만 모바일 로봇 바퀴는 보통 y축 또는 x축을 기준으로 회전해야 하므로 visual, collision, joint axis 방향을 반드시 맞춰야 합니다.

8. Xacro 매크로로 Inertia 재사용하기

강의에서는 같은 구조를 반복해서 쓰지 않는 것이 좋습니다. Xacro 매크로를 쓰면 훨씬 깔끔합니다.

1) 박스 관성 매크로

<xacro:macro name="box_inertia" params="m x y z">
  <inertial>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <mass value="${m}"/>
    <inertia
      ixx="${(1.0/12.0) * m * (y*y + z*z)}"
      ixy="0.0"
      ixz="0.0"
      iyy="${(1.0/12.0) * m * (x*x + z*z)}"
      iyz="0.0"
      izz="${(1.0/12.0) * m * (x*x + y*y)}"/>
  </inertial>
</xacro:macro>

사용 예:

<link name="base_link">
  ...
  <xacro:box_inertia m="5.0" x="0.40" y="0.30" z="0.15"/>
</link>

2) 원통 관성 매크로

<xacro:macro name="cylinder_inertia_y_axis" params="m r l">
  <inertial>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <mass value="${m}"/>
    <inertia
      ixx="${(1.0/12.0) * m * (3*r*r + l*l)}"
      ixy="0.0"
      ixz="0.0"
      iyy="${0.5 * m * r*r}"
      iyz="0.0"
      izz="${(1.0/12.0) * m * (3*r*r + l*l)}"/>
  </inertial>
</xacro:macro>

사용 예:

<link name="left_wheel_link">
  ...
  <xacro:cylinder_inertia_y_axis m="0.2" r="0.05" l="0.03"/>
</link>

9. Friction이란?

Friction은 마찰입니다. 모바일 로봇에서는 특히 바퀴와 바닥 사이의 마찰이 중요합니다.

마찰이 너무 낮으면 다음 문제가 생깁니다.

바퀴는 도는데 로봇이 앞으로 안 감
회전 시 로봇이 미끄러짐
오도메트리와 실제 위치가 크게 달라짐

마찰이 너무 높으면 다음 문제가 생깁니다.

회전이 너무 뻑뻑함
메카넘/옴니휠의 횡방향 움직임이 막힘
접촉 힘이 과하게 커져 시뮬레이션이 튐

Gazebo 물리 튜토리얼은 ODE 물리 엔진 기준으로 solver, constraint, friction 같은 물리 파라미터를 조정하는 방법을 설명합니다. Gazebo Classic의 기본 ODE 설정에서는 max_step_size, real_time_update_rate, solver 반복 횟수 같은 값도 마찰 안정성에 영향을 줍니다.

10. mu와 mu2의 의미

Gazebo/SDF에서 바퀴 마찰을 설정할 때 자주 쓰는 값은 다음입니다.

<surface>
  <friction>
    <ode>
      <mu>1.0</mu>
      <mu2>1.0</mu2>
    </ode>
  </friction>
</surface>

파라미터의미

mu첫 번째 접선 방향의 마찰 계수
mu2첫 번째 방향과 수직인 두 번째 접선 방향의 마찰 계수
fdir1첫 번째 마찰 방향 지정
slip1첫 번째 방향의 인공 미끄러짐
slip2두 번째 방향의 인공 미끄러짐

mu와 mu2는 단순히 “앞뒤 마찰”, “좌우 마찰”로 고정되는 값이 아닙니다. 접촉면의 접선 방향 두 개에 대한 마찰 계수입니다. fdir1을 명시하면 첫 번째 마찰 방향을 지정할 수 있습니다. fdir1은 첫 번째 마찰 방향을 정의하는 벡터이며 접촉 법선에 수직인 접선 방향이어야 합니다.

두 물체가 접촉하면 물리 엔진은 먼저 다음 정보를 계산합니다.

접촉점 p
접촉 법선 n
첫 번째 접선 방향 t1
두 번째 접선 방향 t2

이 세 방향은 서로 직교하는 접촉 좌표계를 구성합니다.

                 n: 접촉 법선
                 ↑
                 │
                 │
                 ●────→ t1: 첫 번째 마찰 방향
                /
               /
              t2: 두 번째 마찰 방향

각 방향의 역할은 다음과 같습니다.

n  : 두 물체가 서로 파고들지 않도록 반발력이 작용하는 방향
t1 : mu가 적용되는 첫 번째 마찰 방향
t2 : mu2가 적용되는 두 번째 마찰 방향

ODE는 접촉 법선에 수직인 두 방향으로 마찰력을 적용합니다. fdir1은 첫 번째 방향인 t1을 지정하고, 두 번째 방향은 접촉 법선과 fdir1에 모두 수직이 되도록 계산됩니다.

11. URDF에서 Gazebo 마찰 설정하기

URDF 자체는 마찰 설정 표현이 제한적입니다. 그래서 Gazebo 확장 태그를 사용합니다. Gazebo 문서도 URDF에 Gazebo 전용 <gazebo> 요소를 추가해 SDF에 있는 시뮬레이션 속성을 지정할 수 있다고 설명합니다.

예를 들어 left_wheel_link에 마찰을 주려면 다음처럼 작성합니다.

<gazebo reference="left_wheel_link">
  <mu1>1.0</mu1>
  <mu2>1.0</mu2>
  <kp>1000000.0</kp>
  <kd>100.0</kd>
  <minDepth>0.001</minDepth>
  <maxVel>1.0</maxVel>
</gazebo>

Gazebo Classic URDF 확장에서는 보통 mu1, mu2 형식으로 많이 사용합니다. SDF 원문에서는 mu, mu2 구조가 쓰입니다.

12. SDF 형식의 Friction 예시

SDF에서는 다음처럼 더 명확하게 작성합니다.

<collision name="wheel_collision">
  <geometry>
    <cylinder>
      <radius>0.05</radius>
      <length>0.03</length>
    </cylinder>
  </geometry>

  <surface>
    <friction>
      <ode>
        <mu>1.0</mu>
        <mu2>1.0</mu2>
        <slip1>0.0</slip1>
        <slip2>0.0</slip2>
      </ode>
    </friction>
  </surface>
</collision>

SDFormat는 로봇, 물체, 환경, 물리 엔진, 조명, 센서 등을 표현하기 위한 XML 형식입니다. Gazebo 계열 시뮬레이터에서 시뮬레이션용 모델을 표현할 때 핵심 형식으로 사용됩니다.

13. 모바일 로봇 바퀴별 추천 Friction 시작값

처음 강의 실습에서는 아래 값을 시작점으로 쓰면 좋습니다. 단, 실제 값은 로봇 크기, 질량, 바퀴 반지름, 바닥 재질, 물리 엔진 설정에 따라 조정해야 합니다.

로봇 종류 mu1 mu2 설명

일반 2륜 차동구동1.0 ~ 2.01.0 ~ 2.0기본적인 고무 바퀴 느낌
4륜 차동구동1.0 ~ 2.01.0 ~ 2.0직진 안정성 우선
캐스터 포함 로봇구동 바퀴 1.0 이상캐스터는 낮게캐스터가 끌리지 않게 조정
메카넘 휠방향별로 다르게 조정방향별로 다르게 조정횡방향 미끄러짐 허용 필요
옴니휠일부 방향 낮게일부 방향 높게롤러 방향 특성 반영

초보 강의에서는 먼저 mu1=1.0, mu2=1.0으로 시작하고, 로봇이 미끄러지면 값을 올리는 방식이 좋습니다.

14. ROS 2 모바일 로봇에 Inertia와 Friction 넣기

1) 패키지 생성

cd ~/gazebo_ws/src

ros2 pkg create --build-type ament_cmake mobile_robot_description

폴더를 만듭니다.

cd ~/gazebo_ws/src/mobile_robot_description
mkdir urdf launch rviz

2) package.xml 확인

package.xml에 최소한 다음 의존성을 넣습니다.

<depend>robot_state_publisher</depend>
<depend>joint_state_publisher</depend>
<depend>joint_state_publisher_gui</depend>
<depend>rviz2</depend>
<depend>xacro</depend>

<export>
  <build_type>ament_cmake</build_type>
</export>

ament_cmake 패키지는 package.xml의 <export> 안에 다음이 있어야 합니다.

<build_type>ament_cmake</build_type>

없으면 빌드나 패키지 인식에서 문제가 생길 수 있습니다.

3) CMakeLists.txt 설정

cmake_minimum_required(VERSION 3.8)
project(mobile_robot_description)

find_package(ament_cmake REQUIRED)

install(
  DIRECTORY urdf launch rviz
  DESTINATION share/${PROJECT_NAME}
)

ament_package()

4) 모바일 로봇 Xacro 전체 예제

파일 생성:

touch urdf/mobile_robot.urdf.xacro

urdf/mobile_robot.urdf.xacro에 다음 코드를 입력합니다.

<?xml version="1.0"?>

<robot
  name="simple_mobile_robot"
  xmlns:xacro="http://www.ros.org/wiki/xacro">

  <!-- ======================================== -->
  <!-- Parameters                               -->
  <!-- ======================================== -->

  <xacro:property name="base_length" value="0.40"/>
  <xacro:property name="base_width"  value="0.30"/>
  <xacro:property name="base_height" value="0.15"/>
  <xacro:property name="base_mass"   value="5.0"/>

  <xacro:property name="wheel_radius" value="0.05"/>
  <xacro:property name="wheel_width"  value="0.03"/>
  <xacro:property name="wheel_mass"   value="0.2"/>

  <xacro:property name="wheel_y" value="0.18"/>
  <xacro:property name="wheel_z" value="-0.05"/>

  <!-- ======================================== -->
  <!-- Inertia Macros                           -->
  <!-- ======================================== -->

  <xacro:macro name="box_inertia" params="m x y z">
    <inertial>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <mass value="${m}"/>

      <inertia
        ixx="${(1.0 / 12.0) * m * (y*y + z*z)}"
        ixy="0.0"
        ixz="0.0"
        iyy="${(1.0 / 12.0) * m * (x*x + z*z)}"
        iyz="0.0"
        izz="${(1.0 / 12.0) * m * (x*x + y*y)}"/>
    </inertial>
  </xacro:macro>

  <xacro:macro
    name="cylinder_inertia_y_axis"
    params="m r l">

    <inertial>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <mass value="${m}"/>

      <inertia
        ixx="${(1.0 / 12.0) * m * (3*r*r + l*l)}"
        ixy="0.0"
        ixz="0.0"
        iyy="${0.5 * m * r*r}"
        iyz="0.0"
        izz="${(1.0 / 12.0) * m * (3*r*r + l*l)}"/>
    </inertial>
  </xacro:macro>

  <!-- ======================================== -->
  <!-- Base Link                                -->
  <!-- ======================================== -->

  <link name="base_link">

    <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>

      <geometry>
        <box
          size="${base_length}
                ${base_width}
                ${base_height}"/>
      </geometry>

      <material name="blue">
        <color rgba="0.1 0.2 0.8 1.0"/>
      </material>
    </visual>

    <collision>
      <origin xyz="0 0 0" rpy="0 0 0"/>

      <geometry>
        <box
          size="${base_length}
                ${base_width}
                ${base_height}"/>
      </geometry>
    </collision>

    <xacro:box_inertia
      m="${base_mass}"
      x="${base_length}"
      y="${base_width}"
      z="${base_height}"/>

  </link>

  <!-- ======================================== -->
  <!-- Wheel Macro                              -->
  <!-- ======================================== -->

  <xacro:macro name="wheel" params="name y_pos">

    <link name="${name}_wheel_link">

      <visual>
        <!-- Cylinder 기본 축 z를 y축 방향으로 회전 -->
        <origin xyz="0 0 0" rpy="1.5708 0 0"/>

        <geometry>
          <cylinder
            radius="${wheel_radius}"
            length="${wheel_width}"/>
        </geometry>

        <material name="black">
          <color rgba="0.02 0.02 0.02 1.0"/>
        </material>
      </visual>

      <collision>
        <origin xyz="0 0 0" rpy="1.5708 0 0"/>

        <geometry>
          <cylinder
            radius="${wheel_radius}"
            length="${wheel_width}"/>
        </geometry>
      </collision>

      <xacro:cylinder_inertia_y_axis
        m="${wheel_mass}"
        r="${wheel_radius}"
        l="${wheel_width}"/>

    </link>

    <joint
      name="${name}_wheel_joint"
      type="continuous">

      <parent link="base_link"/>
      <child link="${name}_wheel_link"/>

      <origin
        xyz="0 ${y_pos} ${wheel_z}"
        rpy="0 0 0"/>

      <axis xyz="0 1 0"/>

    </joint>

    <!-- Gazebo wheel friction -->
    <gazebo reference="${name}_wheel_link">
      <mu1>1.0</mu1>
      <mu2>1.0</mu2>

      <kp>1000000.0</kp>
      <kd>100.0</kd>

      <minDepth>0.001</minDepth>
      <maxVel>1.0</maxVel>
    </gazebo>

  </xacro:macro>

  <!-- ======================================== -->
  <!-- Left and Right Wheels                    -->
  <!-- ======================================== -->

  <xacro:wheel
    name="left"
    y_pos="${wheel_y}"/>

  <xacro:wheel
    name="right"
    y_pos="${-wheel_y}"/>

  <!-- ======================================== -->
  <!-- Caster                                   -->
  <!-- ======================================== -->

  <link name="caster_link">

    <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>

      <geometry>
        <sphere radius="0.025"/>
      </geometry>

      <material name="gray">
        <color rgba="0.5 0.5 0.5 1.0"/>
      </material>
    </visual>

    <collision>
      <origin xyz="0 0 0" rpy="0 0 0"/>

      <geometry>
        <sphere radius="0.025"/>
      </geometry>
    </collision>

    <inertial>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <mass value="0.05"/>

      <inertia
        ixx="0.0000125"
        ixy="0.0"
        ixz="0.0"
        iyy="0.0000125"
        iyz="0.0"
        izz="0.0000125"/>
    </inertial>

  </link>

  <joint name="caster_joint" type="fixed">
    <parent link="base_link"/>
    <child link="caster_link"/>

    <origin
      xyz="-0.15 0 -0.075"
      rpy="0 0 0"/>
  </joint>

  <!-- 캐스터는 낮은 마찰 사용 -->
  <gazebo reference="caster_link">
    <mu1>0.1</mu1>
    <mu2>0.1</mu2>

    <kp>1000000.0</kp>
    <kd>100.0</kd>
  </gazebo>

  <!-- ======================================== -->
  <!-- Gazebo ROS Differential Drive Plugin     -->
  <!-- ======================================== -->

  <gazebo>

    <plugin
      name="gazebo_ros_diff_drive"
      filename="libgazebo_ros_diff_drive.so">

      <ros>
        <remapping>cmd_vel:=/cmd_vel</remapping>
        <remapping>odom:=/odom</remapping>
      </ros>

      <update_rate>50.0</update_rate>

      <left_joint>
        left_wheel_joint
      </left_joint>

      <right_joint>
        right_wheel_joint
      </right_joint>

      <wheel_separation>
        ${2.0 * wheel_y}
      </wheel_separation>

      <wheel_diameter>
        ${2.0 * wheel_radius}
      </wheel_diameter>

      <max_wheel_torque>
        20.0
      </max_wheel_torque>

      <max_wheel_acceleration>
        1.0
      </max_wheel_acceleration>

      <!--
        1: Gazebo 실제 위치 기반
        0: 바퀴 회전 적분 기반
      -->
      <odometry_source>1</odometry_source>

      <odometry_frame>odom</odometry_frame>
      <robot_base_frame>base_link</robot_base_frame>

      <publish_odom>true</publish_odom>
      <publish_odom_tf>true</publish_odom_tf>
      <publish_wheel_tf>true</publish_wheel_tf>

    </plugin>

  </gazebo>

</robot>

5) Xacro 변환 확인

cd ~/gazebo_ws

ros2 run xacro xacro src/mobile_robot_description/urdf/mobile_robot.urdf.xacro

파일로 저장해서 확인하려면:

ros2 run xacro xacro \
  src/mobile_robot_description/urdf/mobile_robot.urdf.xacro \
  > /tmp/mobile_robot.urdf

URDF 문법 검사:

check_urdf /tmp/mobile_robot.urdf

6) RViz 확인용 Launch 파일

파일 생성:

touch launch/display.launch.py

내용:

import os

from launch import LaunchDescription
from launch_ros.actions import Node
from launch.substitutions import Command
from launch_ros.substitutions import FindPackageShare
from launch_ros.parameter_descriptions import ParameterValue


def generate_launch_description():
    pkg_share = FindPackageShare('mobile_robot_description').find('mobile_robot_description')
    xacro_file = os.path.join(pkg_share, 'urdf', 'mobile_robot.urdf.xacro')

    robot_description = ParameterValue(
        Command(['xacro ', xacro_file]),
        value_type=str
    )

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

    joint_state_publisher_gui = Node(
        package='joint_state_publisher_gui',
        executable='joint_state_publisher_gui'
    )

    rviz2 = Node(
        package='rviz2',
        executable='rviz2',
        output='screen'
    )

    return LaunchDescription([
        robot_state_publisher,
        joint_state_publisher_gui,
        rviz2
    ])

빌드:

cd ~/gazebo_ws
colcon build --packages-select mobile_robot_description
source install/setup.bash

실행:

ros2 launch mobile_robot_description display.launch.py

7) Gazebo 실행용 Launch 파일

파일을 생성합니다.

cd ~/gazebo_ws/src/mobile_robot_description

touch launch/gazebo.launch.py

launch/gazebo.launch.py를 다음과 같이 작성합니다.

import os

from ament_index_python.packages import (
    get_package_share_directory
)
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import (
    PythonLaunchDescriptionSource
)
from launch.substitutions import Command
from launch_ros.actions import Node
from launch_ros.parameter_descriptions import ParameterValue


def generate_launch_description():

    package_name = 'mobile_robot_description'

    package_share = get_package_share_directory(
        package_name
    )

    gazebo_share = get_package_share_directory(
        'gazebo_ros'
    )

    xacro_file = os.path.join(
        package_share,
        'urdf',
        'mobile_robot.urdf.xacro'
    )

    robot_description = ParameterValue(
        Command([
            'xacro',
            ' ',
            xacro_file
        ]),
        value_type=str
    )

    gazebo = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(
            os.path.join(
                gazebo_share,
                'launch',
                'gazebo.launch.py'
            )
        )
    )

    robot_state_publisher = Node(
        package='robot_state_publisher',
        executable='robot_state_publisher',
        output='screen',
        parameters=[{
            'robot_description': robot_description,
            'use_sim_time': True
        }]
    )

    spawn_robot = Node(
        package='gazebo_ros',
        executable='spawn_entity.py',
        output='screen',
        arguments=[
            '-topic',
            'robot_description',

            '-entity',
            'simple_mobile_robot',

            '-x',
            '0.0',

            '-y',
            '0.0',

            '-z',
            '0.11'
        ]
    )

    return LaunchDescription([
        gazebo,
        robot_state_publisher,
        spawn_robot
    ])

-z 0.11은 로봇을 지면보다 약간 위에서 생성하기 위한 값입니다.

이 모델에서 바퀴와 캐스터의 최저점은 base_link 기준 약 -0.10m이므로 0.11m 위치에서 생성하면 지면 위로 자연스럽게 내려앉습니다.

8) Gazebo 빌드 및 실행

터미널 1에서 다음 명령을 실행합니다.

cd ~/gazebo_ws

colcon build --symlink-install --packages-select mobile_robot_description

source install/setup.bash

ros2 launch mobile_robot_description gazebo.launch.py

정상적으로 실행되면 다음 상태가 됩니다.

  • Gazebo 실행
  • 바닥 평면 표시
  • simple_mobile_robot 모델 생성
  • robot_state_publisher 실행
  • DiffDrive 플러그인 로드
  • /cmd_vel 구독
  • /odom 발행
  • /tf 발행
  • /clock 발행

9) 키보드로 로봇 조종하기

다음 명령을 실행합니다.

source turtlebot3_ws/install/setup.bash
ros2 run turtlebot3_teleop teleop_keyboard 

키보드 입력으로 직진, 후진, 회전 및 정지 동작을 시험할 수 있습니다.

15. 실험 1: 마찰을 매우 낮게 설정하기

바퀴의 Gazebo 설정을 다음과 같이 변경합니다.

<gazebo reference="${name}_wheel_link">
  <mu1>0.01</mu1>
  <mu2>0.01</mu2>

  <kp>1000000.0</kp>
  <kd>100.0</kd>

  <minDepth>0.001</minDepth>
  <maxVel>1.0</maxVel>
</gazebo>

예상 결과는 다음과 같습니다.

  • 바퀴가 쉽게 미끄러짐
  • 바퀴 회전력이 바닥에 충분히 전달되지 않음
  • 로봇이 명령한 거리만큼 이동하지 않음
  • 회전 중 자세가 불안정함
  • 바퀴 회전과 실제 이동 거리의 차이가 커짐

직진 명령으로 확인합니다.

ros2 topic pub \
  --rate 10 \
  /cmd_vel \
  geometry_msgs/msg/Twist \
  "{
    linear: {
      x: 0.2,
      y: 0.0,
      z: 0.0
    },
    angular: {
      x: 0.0,
      y: 0.0,
      z: 0.0
    }
  }"

마찰이 너무 낮으면 모터가 회전해도 접지력이 부족하므로 로봇이 정상적으로 움직이지 않습니다.

16. 실험 2: 일반적인 마찰값 사용하기

바퀴 마찰을 다음과 같이 설정합니다.

<gazebo reference="${name}_wheel_link">
  <mu1>1.0</mu1>
  <mu2>1.0</mu2>

  <kp>1000000.0</kp>
  <kd>100.0</kd>

  <minDepth>0.001</minDepth>
  <maxVel>1.0</maxVel>
</gazebo>

예상 결과는 다음과 같습니다.

  • 일반적인 차동구동 로봇처럼 이동함
  • 직진 주행이 비교적 안정적임
  • 제자리 회전이 가능함
  • 바퀴의 회전력이 지면에 전달됨

초기 시험에서는 mu1=1.0, mu2=1.0부터 시작하는 것이 좋습니다.

그다음 다음 요소에 따라 조정합니다.

  • 로봇 질량
  • 바퀴 반지름
  • 바퀴 폭
  • 지면 재질
  • 목표 가속도
  • 물리 엔진 설정

17. 실험 3: 마찰을 지나치게 높이기

바퀴 마찰을 다음과 같이 변경합니다.

<gazebo reference="${name}_wheel_link">
  <mu1>100.0</mu1>
  <mu2>100.0</mu2>

  <kp>1000000.0</kp>
  <kd>100.0</kd>

  <minDepth>0.001</minDepth>
  <maxVel>1.0</maxVel>
</gazebo>

예상 결과는 다음과 같습니다.

  • 접지력은 강해짐
  • 제자리 회전이 부자연스러울 수 있음
  • 접촉 힘이 커질 수 있음
  • 로봇이 떨리거나 튈 수 있음
  • 물리 계산이 불안정해질 수 있음

마찰 계수는 높다고 무조건 좋은 것이 아닙니다.

실제 로봇과 유사한 움직임을 만들 수 있는 범위에서 조정해야 합니다.

18. 실험 4: 질량을 너무 작게 설정하기

바디 질량을 다음과 같이 변경합니다.

<xacro:property
  name="base_mass"
  value="0.001"/>

또는 직접 다음과 같이 설정할 수 있습니다.

<mass value="0.001"/>

예상되는 문제는 다음과 같습니다.

  • 작은 충돌에도 로봇이 튀어 오름
  • 바퀴 접촉이 불안정해짐
  • 가속도가 비정상적으로 커짐
  • 충돌 시 로봇이 멀리 날아감
  • 수치 계산이 불안정해짐

링크 질량은 실제 로봇의 예상 질량과 비슷하게 설정해야 합니다.

19. 실험 5: 관성값을 너무 작게 설정하기

바디 관성값을 다음과 같이 변경합니다.

<inertia
  ixx="0.000001"
  ixy="0.0"
  ixz="0.0"
  iyy="0.000001"
  iyz="0.0"
  izz="0.000001"/>

예상되는 문제는 다음과 같습니다.

  • 작은 힘에도 급격히 회전함
  • 자세가 계속 떨림
  • 충돌 시 각속도가 크게 증가함
  • 시뮬레이션이 불안정해짐

관성값은 임의로 작은 값을 입력하는 것이 아니라 링크의 질량과 형상을 이용해 계산해야 합니다.

20. 실험 6: 형상 기반 관성값 사용하기

박스 바디에는 다음 매크로를 사용합니다.

<xacro:box_inertia
  m="${base_mass}"
  x="${base_length}"
  y="${base_width}"
  z="${base_height}"/>

바퀴에는 다음 매크로를 사용합니다.

<xacro:cylinder_inertia_y_axis
  m="${wheel_mass}"
  r="${wheel_radius}"
  l="${wheel_width}"/>

예상 결과는 다음과 같습니다.

  • 로봇의 움직임이 자연스러워짐
  • 회전 반응이 안정됨
  • 충돌 후 비정상적인 진동이 감소함
  • 바퀴 접촉이 안정됨
  • 물리 엔진의 계산이 안정적임

21. 마찰 실험 결과 기록표

실험 결과를 다음과 같이 기록할 수 있습니다.

실험mu1mu2직진 상태회전 상태진동미끄러짐
낮은 마찰0.010.01불안정불안정낮음 또는 불규칙매우 큼
일반 마찰1.01.0안정비교적 안정낮음적음
높은 마찰100.0100.0강한 접지회전이 뻑뻑함발생 가능매우 적음

22. 관성 실험 결과 기록표

실험바디 질량관성값예상 결과
지나치게 작은 질량0.001kg기존값충돌과 가속에 과민 반응
지나치게 작은 관성5.0kg0.000001회전과 진동이 불안정
형상 기반 관성5.0kg수식 계산비교적 안정적인 물리 운동

Leave a Comment