Gazebo 각종 플러그인 적용하기

1. Gazebo Plugin이란?

Gazebo 자체는 물리 시뮬레이터입니다. 그런데 ROS 2와 데이터를 주고받으려면 중간 연결 계층이 필요합니다. 이 역할을 하는 것이 Gazebo ROS Plugin입니다.

예를 들어 Gazebo 안에서 LiDAR가 빛을 쏘고 거리값을 계산하더라도, ROS 2에서 /scan 토픽으로 받으려면 Gazebo 센서 데이터를 ROS 2 메시지로 변환해 주는 플러그인이 필요합니다.

Gazebo ROS 플러그인은 URDF 또는 SDF 내부에 XML 형태로 추가합니다. Gazebo 플러그인은 URDF 모델에 기능을 추가하고, ROS 메시지 및 서비스와 연결할 수 있습니다.

관련 패키지는 이미 설치되어 있습니다.

2. 예제 패키지 만들기

cd ~/gazebo_ws/src

ros2 pkg create mobile_robot_sensors --build-type ament_cmake

폴더 구조는 다음처럼 만듭니다.

mobile_robot_sensors/
├── CMakeLists.txt
├── package.xml
├── launch/
│   └── gazebo_sensor_demo.launch.py
├── urdf/
│   └── mobile_robot.urdf.xacro
└── worlds/
    └── empty.world

폴더 생성:

cd ~/gazebo_ws/src/mobile_robot_sensors
mkdir launch urdf worlds

3. 기본 모바일 로봇 URDF 만들기

urdf/mobile_robot.urdf.xacro 파일을 만듭니다.

touch urdf/mobile_robot.urdf.xacro

<?xml version="1.0"?>
<robot name="mobile_robot" xmlns:xacro="http://www.ros.org/wiki/xacro">

  <!-- ========================= -->
  <!-- 기본 파라미터 -->
  <!-- ========================= -->
  <xacro:property name="base_length" value="0.45"/>
  <xacro:property name="base_width"  value="0.32"/>
  <xacro:property name="base_height" value="0.12"/>

  <!-- ========================= -->
  <!-- base_link -->
  <!-- ========================= -->
  <link name="base_link">
    <visual>
      <origin xyz="0 0 0.06" 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.06" rpy="0 0 0"/>
      <geometry>
        <box size="${base_length} ${base_width} ${base_height}"/>
      </geometry>
    </collision>

    <inertial>
      <origin xyz="0 0 0.06" rpy="0 0 0"/>
      <mass value="5.0"/>
      <inertia
        ixx="0.05" ixy="0.0" ixz="0.0"
        iyy="0.08" iyz="0.0"
        izz="0.10"/>
    </inertial>
  </link>

  <!-- ========================= -->
  <!-- Gazebo 색상 -->
  <!-- ========================= -->
  <gazebo reference="base_link">
    <material>Gazebo/Blue</material>
  </gazebo>

</robot>

아직 바퀴는 넣지 않았습니다. 먼저 센서 토픽을 발행하는 구조를 만들어 보겠습니다.

4. Gazebo LiDAR Plugin 적용

1) LiDAR의 역할

모바일 로봇에서 LiDAR는 주변 장애물과 벽까지의 거리를 측정합니다. ROS 2에서는 보통 /scan 토픽으로 sensor_msgs/LaserScan 메시지를 발행합니다.

Nav2 공식 문서에 따르면 sensor_msgs/LaserScan은 평면 레이저 거리 센서의 한 번의 스캔을 표현하며, slam_toolbox, nav2_amcl, nav2_costmap_2d 등에서 사용됩니다.

2) LiDAR 링크 추가

mobile_robot.urdf.xacro의 </robot> 위에 다음 내용을 추가합니다.

  <!-- ========================= -->
<!-- LiDAR Link -->
<!-- ========================= -->
<link name="lidar_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<cylinder radius="0.045" length="0.04"/>
</geometry>
<material name="black">
<color rgba="0.02 0.02 0.02 1.0"/>
</material>
</visual>

<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<cylinder radius="0.045" length="0.04"/>
</geometry>
</collision>

<inertial>
<mass value="0.2"/>
<inertia
ixx="0.0001" ixy="0.0" ixz="0.0"
iyy="0.0001" iyz="0.0"
izz="0.0001"/>
</inertial>
</link>

<joint name="lidar_joint" type="fixed">
<parent link="base_link"/>
<child link="lidar_link"/>
<origin xyz="0.15 0 0.19" rpy="0 0 0"/>
</joint>

<gazebo reference="lidar_link">
<material>Gazebo/Black</material>
</gazebo>

좌표 의미:

x = 0.15  → 로봇 앞쪽
y = 0     → 중앙
z = 0.18  → base_link 위쪽

5. LiDAR Gazebo Plugin 추가

같은 파일의 </robot> 위에 이어서 추가합니다.

  <!-- ========================= -->
<!-- Gazebo LiDAR Sensor Plugin -->
<!-- ========================= -->
<gazebo reference="lidar_link">
<sensor name="lidar_sensor" type="ray">
<always_on>true</always_on>
<visualize>true</visualize>
<update_rate>10</update_rate>

<ray>
<scan>
<horizontal>
<samples>400</samples>
<resolution>0.9</resolution>
<min_angle>0.0</min_angle>
<max_angle>6.283184</max_angle>
</horizontal>
</scan>

<range>
<min>0.05</min>
<max>10.0</max>
<resolution>0.01</resolution>
</range>

<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.01</stddev>
</noise>
</ray>

<plugin name="lidar_plugin" filename="libgazebo_ros_ray_sensor.so">
<ros>
<remapping>~/out:=scan</remapping>
</ros>
<output_type>sensor_msgs/LaserScan</output_type>
<frame_name>lidar_link</frame_name>
</plugin>
</sensor>
</gazebo>

GazeboRosRaySensor 문서에 따르면 이 플러그인은 Gazebo의 ray 또는 gpu_ray 센서에 붙어서 데이터를 ROS로 발행합니다. 출력 타입은 sensor_msgs/PointCloud2, sensor_msgs/PointCloud, sensor_msgs/LaserScan, sensor_msgs/Range 중 선택할 수 있습니다.

주요 파라미터는 아래와 같습니다.

type=”ray”2D/3D 레이 기반 거리 센서
samples한 번 스캔할 때 쏘는 레이 개수
min_angle, max_angle스캔 각도 범위
range/min최소 감지 거리
range/max최대 감지 거리
update_rate초당 데이터 발행 횟수
output_typeROS 2 메시지 타입
frame_name메시지 header의 frame id

실습에서는 360도 2D LiDAR처럼 구성했습니다.

<min_angle>-3.14159</min_angle>
<max_angle>3.14159</max_angle>

즉, -π부터 +π까지 스캔합니다.

LiDAR 링크에 Gazebo 센서 연결
<gazebo reference="lidar_link">

reference="lidar_link"는 Gazebo 센서를 URDF의 lidar_link에 연결한다는 의미입니다.

따라서 URDF 안에는 다음과 같은 링크가 미리 정의되어 있어야 합니다.

<link name="lidar_link">
  ...
</link>

LiDAR 센서의 위치와 방향은 lidar_link가 부모 링크에 연결된 조인트의 위치와 자세에 따라 결정됩니다.

Ray 센서 정의
<sensor name="lidar_sensor" type="ray">

센서 이름은 lidar_sensor이며 센서 타입은 ray입니다.

ray 센서는 여러 개의 가상 광선을 방사한 뒤 장애물과 충돌하는 지점까지의 거리를 계산합니다. 실제 2D LiDAR와 유사한 거리 데이터를 만들 때 사용합니다.

센서 자동 실행
<always_on>true</always_on>

시뮬레이션이 실행되는 동안 LiDAR 센서를 항상 활성화합니다.

값을 false로 설정하면 센서가 자동으로 계속 동작하지 않을 수 있습니다.

Gazebo 화면에 레이저 표시
<visualize>true</visualize>

Gazebo 화면에 LiDAR의 측정 광선을 시각적으로 표시합니다.

센서가 어느 방향으로 스캔하는지 확인할 때 유용하지만, 많은 센서를 사용할 경우 그래픽 처리 부하가 증가할 수 있습니다.

성능이 중요하다면 다음처럼 비활성화할 수 있습니다.

<visualize>false</visualize>

센서 갱신 주기

<update_rate>10</update_rate>

LiDAR 데이터를 초당 10회 갱신합니다.

즉, 약 0.1초마다 새로운 스캔 데이터가 생성됩니다.

주기 = 1 / 10 Hz = 0.1초

갱신 주기를 높이면 장애물을 더 빠르게 감지할 수 있지만, 시뮬레이션 연산량과 ROS 2 메시지 전송량도 증가합니다.

수평 스캔 설정
<scan>
  <horizontal>
    <samples>360</samples>
    <resolution>1</resolution>
    <min_angle>-3.14159</min_angle>
    <max_angle>3.14159</max_angle>
  </horizontal>
</scan>

이 부분은 LiDAR의 수평 스캔 범위를 설정합니다.

samples
<samples>360</samples>

한 번 스캔할 때 360개의 거리 측정값을 생성합니다.

전체 스캔 범위가 360도이므로 대략 1도 간격으로 하나의 거리값을 측정합니다.

resolution
<resolution>1</resolution>

기본 샘플 수에 적용되는 해상도 배율입니다.

현재 값이 1이므로 설정한 360개의 샘플을 그대로 사용합니다.

최소 및 최대 각도
<min_angle>-3.14159</min_angle>
<max_angle>3.14159</max_angle>

Gazebo의 각도 단위는 라디안입니다.

-3.14159 rad ≈ -180도
 3.14159 rad ≈  180도

따라서 센서는 로봇 주변 전체인 360도를 스캔합니다.

전체 스캔 범위는 다음과 같습니다.

-180도 ~ +180도

거리 측정 범위

<range>
  <min>0.12</min>
  <max>8.0</max>
  <resolution>0.01</resolution>
</range>

LiDAR가 측정할 수 있는 최소 거리와 최대 거리를 정의합니다.

최소 측정 거리
<min>0.12</min>

센서로부터 0.12m보다 가까운 물체는 정상적인 거리값으로 측정하지 않습니다.

실제 LiDAR도 센서 바로 앞에는 측정이 어려운 사각지대가 존재합니다.

최대 측정 거리
<max>8.0</max>

최대 측정 거리는 8m입니다.

8m보다 먼 장애물은 측정 범위를 벗어난 것으로 처리됩니다.

거리 해상도
<resolution>0.01</resolution>

거리 측정 해상도는 0.01m, 즉 1cm입니다.

이는 LiDAR가 거리를 1cm 단위로 구분해 표현하도록 설정한 것입니다.

측정 노이즈 추가
<noise>
  <type>gaussian</type>
  <mean>0.0</mean>
  <stddev>0.01</stddev>
</noise>

실제 센서는 항상 완벽한 값을 출력하지 않기 때문에 시뮬레이션 데이터에도 측정 오차를 추가합니다.

노이즈 타입
<type>gaussian</type>

가우시안 분포를 따르는 랜덤 노이즈를 사용합니다.

평균값
<mean>0.0</mean>

노이즈 평균은 0m입니다.

따라서 측정값이 특정 방향으로 지속해서 치우치지 않도록 설정되어 있습니다.

표준편차
<stddev>0.01</stddev>

노이즈의 표준편차는 0.01m, 즉 1cm입니다.

측정값은 실제 거리 주변에서 약간씩 흔들리게 됩니다.

노이즈를 추가하면 실제 센서와 유사한 환경에서 SLAM이나 장애물 감지 알고리즘을 테스트할 수 있습니다.

ROS 2 LiDAR 플러그인
<plugin
  name="lidar_plugin"
  filename="libgazebo_ros_ray_sensor.so">

libgazebo_ros_ray_sensor.so 플러그인은 Gazebo의 Ray 센서 데이터를 ROS 2 메시지로 변환해 발행합니다.

센서 자체는 Gazebo 내부에서 거리값을 계산하고, 이 플러그인이 계산된 값을 ROS 2 토픽으로 전달합니다.

ROS 2 토픽 이름 설정
<ros>
  <remapping>~/out:=scan</remapping>
</ros>

플러그인의 기본 출력 토픽인 ~/outscan으로 변경합니다.

일반적으로 최종 토픽은 다음과 같이 사용됩니다.

/scan

토픽이 정상적으로 발행되는지는 다음 명령으로 확인할 수 있습니다.

ros2 topic list
ros2 topic echo /scan

토픽 발행 주기는 다음 명령으로 확인할 수 있습니다.

ros2 topic hz /scan

설정한 값에 따라 약 10Hz가 출력되어야 합니다.

출력 메시지 형식
<output_type>sensor_msgs/LaserScan</output_type>

LiDAR 데이터를 ROS 2의 sensor_msgs/msg/LaserScan 메시지 형식으로 발행합니다.

LaserScan 메시지에는 다음과 같은 정보가 포함됩니다.

  • 최소 및 최대 스캔 각도
  • 각도 간격
  • 최소 및 최대 측정 거리
  • 각 방향의 거리 측정값
  • 센서 데이터 생성 시간
  • 센서 좌표 프레임

Nav2와 SLAM Toolbox는 일반적으로 이 LaserScan 메시지를 사용합니다.

LiDAR 좌표 프레임
<frame_name>lidar_link</frame_name>

발행되는 LaserScan 메시지의 좌표 프레임을 lidar_link로 설정합니다.

ROS 2에서 LiDAR 데이터를 정상적으로 사용하려면 TF 트리에 다음과 같은 연결이 존재해야 합니다.

base_link
   └── lidar_link

예를 들어 URDF에서는 다음과 같이 고정 조인트를 사용할 수 있습니다.

<joint name="lidar_joint" type="fixed">
  <parent link="base_link"/>
  <child link="lidar_link"/>
  <origin xyz="0.15 0 0.18" rpy="0 0 0"/>
</joint>

이 설정은 LiDAR를 base_link 기준으로 전방 0.15m, 위쪽 0.15m 위치에 배치합니다.

6. Gazebo IMU Plugin 적용

1) IMU의 역할

IMU는 보통 다음 데이터를 제공합니다.

orientation             자세
angular_velocity        각속도
linear_acceleration     선형가속도

모바일 로봇에서는 다음 용도로 자주 씁니다.

바퀴 오도메트리 보정
로봇 yaw 추정
robot_localization 센서 퓨전
경사면 주행 상태 추정

Gazebo ROS IMU 플러그인 문서에 따르면 GazeboRosImuSensor는 Gazebo IMU 센서에 붙어서 ROS 메시지로 출력을 발행하는 센서 플러그인입니다. 예제에서도 <sensor type=”imu”> 안에 libgazebo_ros_imu_sensor.so를 넣는 구조를 사용합니다.

2) IMU 링크 추가

</robot> 위에 다음 내용을 추가합니다.

  <!-- ========================= -->
  <!-- IMU Link -->
  <!-- ========================= -->
  <link name="imu_link">
    <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <box size="0.04 0.03 0.01"/>
      </geometry>
      <material name="green">
        <color rgba="0.0 0.8 0.2 1.0"/>
      </material>
    </visual>

    <collision>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <box size="0.04 0.03 0.01"/>
      </geometry>
    </collision>

    <inertial>
      <mass value="0.05"/>
      <inertia
        ixx="0.00001" ixy="0.0" ixz="0.0"
        iyy="0.00001" iyz="0.0"
        izz="0.00001"/>
    </inertial>
  </link>

  <joint name="imu_joint" type="fixed">
    <parent link="base_link"/>
    <child link="imu_link"/>
    <origin xyz="0 0 0.14" rpy="0 0 0"/>
  </joint>

  <gazebo reference="imu_link">
    <material>Gazebo/Green</material>
  </gazebo>

IMU는 보통 로봇 중심 근처에 둡니다. 실제 로봇에서도 IMU가 로봇 중심에서 너무 멀리 떨어지면 회전 운동에 의한 가속도 영향이 커질 수 있습니다.

3) IMU Gazebo Plugin 추가

  <!-- ========================= -->
  <!-- Gazebo IMU Sensor Plugin -->
  <!-- ========================= -->
  <gazebo reference="imu_link">
    <sensor name="imu_sensor" type="imu">
      <always_on>true</always_on>
      <update_rate>100</update_rate>
      <visualize>true</visualize>

      <imu>
        <angular_velocity>
          <x>
            <noise type="gaussian">
              <mean>0.0</mean>
              <stddev>0.0002</stddev>
            </noise>
          </x>
          <y>
            <noise type="gaussian">
              <mean>0.0</mean>
              <stddev>0.0002</stddev>
            </noise>
          </y>
          <z>
            <noise type="gaussian">
              <mean>0.0</mean>
              <stddev>0.0002</stddev>
            </noise>
          </z>
        </angular_velocity>

        <linear_acceleration>
          <x>
            <noise type="gaussian">
              <mean>0.0</mean>
              <stddev>0.017</stddev>
            </noise>
          </x>
          <y>
            <noise type="gaussian">
              <mean>0.0</mean>
              <stddev>0.017</stddev>
            </noise>
          </y>
          <z>
            <noise type="gaussian">
              <mean>0.0</mean>
              <stddev>0.017</stddev>
            </noise>
          </z>
        </linear_acceleration>
      </imu>

      <plugin name="imu_plugin" filename="libgazebo_ros_imu_sensor.so">
        <ros>
          <namespace>/imu</namespace>
          <remapping>~/out:=data</remapping>
        </ros>
        <frame_name>imu_link</frame_name>
      </plugin>
    </sensor>
  </gazebo>

위 설정에서는 ROS 2 토픽이 다음처럼 나옵니다.

/imu/data
IMU 링크에 센서 연결
<gazebo reference="imu_link">

reference="imu_link"는 Gazebo IMU 센서를 URDF의 imu_link에 연결한다는 의미입니다.

따라서 URDF 내부에는 다음과 같은 링크가 미리 정의되어 있어야 합니다.

<link name="imu_link">
  ...
</link>

일반적으로 imu_link는 고정 조인트를 이용해 base_link에 연결합니다.

<joint name="imu_joint" type="fixed">
  <parent link="base_link"/>
  <child link="imu_link"/>
  <origin xyz="0 0 0.1" rpy="0 0 0"/>
</joint>

이 경우 IMU 센서는 로봇 본체 중심에서 Z축 방향으로 0.1m 위에 배치됩니다.

IMU 센서 정의
<sensor name="imu_sensor" type="imu">

센서 이름은 imu_sensor이며, 센서 종류는 imu입니다.

Gazebo의 IMU 센서는 일반적으로 다음 값을 제공합니다.

  • 로봇의 자세
  • X, Y, Z축 각속도
  • X, Y, Z축 선형가속도

ROS 2에서는 보통 sensor_msgs/msg/Imu 메시지 형식으로 전달됩니다.

센서 상시 동작 설정
<always_on>true</always_on>

시뮬레이션이 실행되는 동안 IMU 센서를 항상 활성화합니다.

값을 true로 설정하면 별도의 활성화 명령 없이 지속적으로 센서 데이터를 생성합니다.

센서 갱신 주기
<update_rate>100</update_rate>

IMU 데이터를 초당 100회 갱신합니다.

즉, 데이터 생성 주기는 약 0.01초입니다.

주기 = 1 / 100Hz = 0.01초

IMU는 로봇의 빠른 움직임과 진동을 측정해야 하므로 LiDAR나 카메라보다 높은 주기를 사용하는 경우가 많습니다.

갱신 주기가 높을수록 움직임을 세밀하게 측정할 수 있지만, 시뮬레이션 연산량과 ROS 2 메시지 전송량도 증가합니다.

센서 시각화 설정
<visualize>true</visualize>

Gazebo GUI에서 센서 시각화를 활성화하는 설정입니다.

다만 IMU는 LiDAR처럼 광선이나 카메라 영상이 존재하지 않으므로, visualize를 활성화해도 화면에서 뚜렷한 변화가 없을 수 있습니다.

필수 설정은 아니므로 다음과 같이 비활성화해도 센서 데이터 발행에는 문제가 없습니다.

<visualize>false</visualize>
IMU 측정값 설정
<imu>

imu 태그 안에서는 IMU의 각속도와 선형가속도 측정 특성을 설정합니다.

현재 코드에서는 실제 센서처럼 측정값이 완벽하지 않도록 가우시안 노이즈를 추가합니다.

각속도 노이즈
<angular_velocity>

각속도는 로봇이 각 축을 기준으로 얼마나 빠르게 회전하는지를 나타냅니다.

측정 단위는 일반적으로 다음과 같습니다.

rad/s

각 축은 다음 회전을 의미합니다.

  • X축: 롤 회전
  • Y축: 피치 회전
  • Z축: 요 회전
X축 각속도
<x>
  <noise type="gaussian">
    <mean>0.0</mean>
    <stddev>0.0002</stddev>
  </noise>
</x>

X축 각속도 측정값에 가우시안 노이즈를 추가합니다.

Y축 각속도
<y>
  <noise type="gaussian">
    <mean>0.0</mean>
    <stddev>0.0002</stddev>
  </noise>
</y>

Y축 각속도에도 동일한 노이즈를 적용합니다.

Z축 각속도
<z>
  <noise type="gaussian">
    <mean>0.0</mean>
    <stddev>0.0002</stddev>
  </noise>
</z>

Z축 각속도에도 같은 크기의 노이즈를 적용합니다.

모바일 로봇에서는 주로 Z축 각속도가 회전 속도 계산에 사용됩니다.

각속도 노이즈 값
<mean>0.0</mean>
<stddev>0.0002</stddev>

mean은 노이즈의 평균값입니다.

평균이 0.0이므로 측정값이 특정 방향으로 계속 치우치지 않도록 설정되어 있습니다.

stddev는 표준편차입니다.

0.0002 rad/s

따라서 실제 각속도 주변에서 작은 오차가 랜덤하게 추가됩니다.

선형가속도 노이즈
<linear_acceleration>

선형가속도는 로봇이 각 축 방향으로 얼마나 빠르게 속도를 변화시키는지를 나타냅니다.

측정 단위는 일반적으로 다음과 같습니다.

m/s²
X축 선형가속도
<x>
  <noise type="gaussian">
    <mean>0.0</mean>
    <stddev>0.017</stddev>
  </noise>
</x>

X축 방향 가속도에 가우시안 노이즈를 추가합니다.

모바일 로봇에서는 전진과 후진 가속도 측정에 주로 사용됩니다.

Y축 선형가속도
<y>
  <noise type="gaussian">
    <mean>0.0</mean>
    <stddev>0.017</stddev>
  </noise>
</y>

Y축 방향 가속도에도 동일한 노이즈를 적용합니다.

Z축 선형가속도
<z>
  <noise type="gaussian">
    <mean>0.0</mean>
    <stddev>0.017</stddev>
  </noise>
</z>

Z축 방향 가속도에도 같은 노이즈를 적용합니다.

Z축 가속도에는 로봇의 움직임뿐 아니라 중력의 영향도 나타날 수 있습니다.

선형가속도 노이즈 값
<mean>0.0</mean>
<stddev>0.017</stddev>

선형가속도 노이즈 평균은 0.0이고 표준편차는 0.017m/s²입니다.

이 설정으로 인해 측정값은 실제 값 주변에서 약간씩 변동합니다.

노이즈를 추가하면 실제 IMU와 비슷한 환경에서 필터나 센서 융합 알고리즘을 시험할 수 있습니다.

ROS 2 IMU 플러그인
<plugin
  name="imu_plugin"
  filename="libgazebo_ros_imu_sensor.so">

libgazebo_ros_imu_sensor.so는 Gazebo Classic의 IMU 측정값을 ROS 2 메시지로 변환해 발행하는 플러그인입니다.

Gazebo 내부에서는 IMU 센서가 물리적인 자세와 가속도를 계산하고, 이 플러그인이 계산된 결과를 ROS 2 토픽으로 전달합니다.

ROS 2 네임스페이스 설정
<ros>
  <namespace>/imu</namespace>
  <remapping>~/out:=data</remapping>
</ros>

네임스페이스를 /imu로 설정합니다.

<namespace>/imu</namespace>

플러그인의 기본 출력 토픽인 ~/outdata로 변경합니다.

<remapping>~/out:=data</remapping>

이 설정을 적용하면 일반적으로 최종 토픽 이름은 다음과 같습니다.

/imu/data

실제 생성된 토픽은 다음 명령으로 확인할 수 있습니다.

ros2 topic list

IMU 메시지 내용을 확인하려면 다음 명령을 사용합니다.

ros2 topic echo /imu/data

토픽 발행 주기는 다음과 같이 확인합니다.

ros2 topic hz /imu/data

설정한 값에 따라 약 100Hz로 출력되어야 합니다.

IMU 좌표 프레임
<frame_name>imu_link</frame_name>

발행되는 IMU 메시지의 기준 좌표 프레임을 imu_link로 설정합니다.

ROS 2 메시지의 header.frame_id에는 일반적으로 다음 값이 들어갑니다.

imu_link

IMU 데이터를 정상적으로 사용하려면 TF 트리에 다음 연결이 존재해야 합니다.

base_link
   └── imu_link

imu_link가 TF 트리에 연결되지 않으면 RViz나 robot_localization 같은 노드에서 좌표 변환 오류가 발생할 수 있습니다.

ROS 2 IMU 메시지 구성

이 플러그인은 일반적으로 다음 메시지 형식을 사용합니다.

sensor_msgs/msg/Imu

주요 데이터는 다음과 같습니다.

orientation
angular_velocity
linear_acceleration

orientation은 로봇의 자세를 쿼터니언으로 나타냅니다.

orientation.x
orientation.y
orientation.z
orientation.w

angular_velocity는 각 축의 회전 속도를 나타냅니다.

angular_velocity.x
angular_velocity.y
angular_velocity.z

linear_acceleration은 각 축 방향의 선형가속도를 나타냅니다.

linear_acceleration.x
linear_acceleration.y
linear_acceleration.z

4) IMU 확인 명령

ros2 topic list

출력에 다음이 있어야 합니다.

/imu/data

데이터 확인:

ros2 topic echo /imu/data

메시지 타입 확인:

ros2 topic info /imu/data

예상 타입:

sensor_msgs/msg/Imu

7. Gazebo Camera Plugin 적용

1) Camera의 역할

모바일 로봇에서 카메라는 다음 용도로 씁니다.

주행 화면 확인
객체 인식
라인 트레이싱
AR 마커 인식
딥러닝 비전 처리
원격 조종 화면

Gazebo ROS Camera 플러그인은 일반 카메라 센서의 raw image와 camera info를 발행합니다. 문서상 GazeboRosCamera는 generic camera sensor의 raw image와 camera info를 publish하며, depth camera에서는 depth image, point cloud, camera info도 설정할 수 있습니다.

2) Camera 링크 추가

  <!-- ========================= -->
  <!-- Camera Link -->
  <!-- ========================= -->
  <link name="camera_link">
    <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <box size="0.04 0.08 0.04"/>
      </geometry>
      <material name="red">
        <color rgba="0.8 0.1 0.1 1.0"/>
      </material>
    </visual>

    <collision>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <box size="0.04 0.08 0.04"/>
      </geometry>
    </collision>

    <inertial>
      <mass value="0.1"/>
      <inertia
        ixx="0.0001" ixy="0.0" ixz="0.0"
        iyy="0.0001" iyz="0.0"
        izz="0.0001"/>
    </inertial>
  </link>

  <joint name="camera_joint" type="fixed">
    <parent link="base_link"/>
    <child link="camera_link"/>
    <origin xyz="0.23 0 0.16" rpy="0 0 0"/>
  </joint>

  <gazebo reference="camera_link">
    <material>Gazebo/Red</material>
  </gazebo>

카메라는 로봇 전방에 둡니다.

x = 0.23  → 로봇 앞쪽 끝부분
z = 0.16  → base_link보다 높은 위치

3) Camera Gazebo Plugin 추가

  <!-- ========================= -->
  <!-- Gazebo Camera Sensor Plugin -->
  <!-- ========================= -->
  <gazebo reference="camera_link">
    <sensor name="front_camera" type="camera">
      <always_on>true</always_on>
      <visualize>true</visualize>
      <update_rate>30</update_rate>

      <camera>
        <horizontal_fov>1.3962634</horizontal_fov>

        <image>
          <width>640</width>
          <height>480</height>
          <format>R8G8B8</format>
        </image>

        <clip>
          <near>0.02</near>
          <far>20.0</far>
        </clip>

        <noise>
          <type>gaussian</type>
          <mean>0.0</mean>
          <stddev>0.007</stddev>
        </noise>
      </camera>

      <plugin name="camera_plugin" filename="libgazebo_ros_camera.so">
        <ros>
          <namespace>/camera</namespace>
          <remapping>image_raw:=image_raw</remapping>
          <remapping>camera_info:=camera_info</remapping>
        </ros>
        <camera_name>front_camera</camera_name>
        <frame_name>camera_link</frame_name>
      </plugin>
    </sensor>
  </gazebo>

출력 토픽:

/camera/image_raw
/camera/camera_info
카메라 링크에 Gazebo 센서 연결
<gazebo reference="camera_link">

reference="camera_link"는 Gazebo 카메라 센서를 URDF의 camera_link에 연결한다는 의미입니다.

따라서 URDF 내부에는 camera_link가 미리 정의되어 있어야 합니다.

<link name="camera_link">
  ...
</link>

카메라의 실제 위치와 방향은 camera_link를 부모 링크에 연결하는 조인트의 origin 설정으로 결정됩니다.

예를 들어 다음과 같이 카메라를 로봇 앞쪽에 배치할 수 있습니다.

<joint name="camera_joint" type="fixed">
  <parent link="base_link"/>
  <child link="camera_link"/>
  <origin xyz="0.18 0 0.12" rpy="0 0 0"/>
</joint>

이 설정은 카메라를 base_link 기준으로 전방 0.18m, 위쪽 0.12m 위치에 고정합니다.

카메라 센서 정의
<sensor name="front_camera" type="camera">

센서 이름은 front_camera이고 센서 타입은 camera입니다.

Gazebo는 이 설정을 이용해 로봇 전방의 가상 영상을 생성합니다.

센서 상시 동작
<always_on>true</always_on>

시뮬레이션이 실행되는 동안 카메라 센서를 계속 활성화합니다.

true로 설정하면 별도의 센서 활성화 명령 없이 지속적으로 이미지가 생성됩니다.

Gazebo 화면에서 카메라 영상 표시
<visualize>true</visualize>

Gazebo GUI에서 카메라 센서가 촬영하는 영상을 확인할 수 있도록 설정합니다.

카메라 방향과 영상 출력 상태를 점검할 때 유용하지만, 시뮬레이션 성능이 중요하다면 다음과 같이 비활성화할 수 있습니다.

<visualize>false</visualize>

ROS 2 이미지 토픽 발행 여부와 visualize 설정은 직접적인 관계가 없습니다.

영상 갱신 주기
<update_rate>30</update_rate>

카메라 영상을 초당 30번 생성합니다.

즉, 일반적인 30 FPS 카메라와 같은 주기로 동작합니다.

1 / 30초 ≈ 0.033초

영상 해상도와 갱신 주기가 높을수록 더 부드러운 영상을 얻을 수 있지만 CPU와 GPU 사용량도 증가합니다.

카메라 시야각 설정
<horizontal_fov>1.3962634</horizontal_fov>

카메라의 수평 시야각을 라디안 단위로 설정합니다.

1.3962634 rad ≈ 80도

따라서 이 카메라는 좌우 약 80도의 범위를 촬영합니다.

시야각이 넓으면 더 넓은 영역을 볼 수 있지만 물체가 작게 보이고 영상 왜곡이 커질 수 있습니다. 시야각이 좁으면 먼 물체를 크게 볼 수 있지만 촬영 범위가 줄어듭니다.

영상 해상도
<image>
  <width>640</width>
  <height>480</height>
  <format>R8G8B8</format>
</image>

카메라 영상의 크기와 픽셀 형식을 설정합니다.

영상 크기
<width>640</width>
<height>480</height>

출력 영상 해상도는 다음과 같습니다.

640 × 480

VGA 해상도로, 기본적인 객체 인식과 영상 처리 실습에 적합합니다.

해상도를 높이면 더 세밀한 영상을 얻을 수 있지만 데이터 크기와 연산 부하도 증가합니다.

픽셀 형식
<format>R8G8B8</format>

각 픽셀을 RGB 3개 채널로 표현합니다.

  • R: 빨간색 8비트
  • G: 초록색 8비트
  • B: 파란색 8비트

각 채널이 8비트이므로 한 픽셀은 총 24비트 색상 정보를 가집니다.

카메라 측정 거리
<clip>
  <near>0.02</near>
  <far>20.0</far>
</clip>

카메라가 렌더링할 최소 거리와 최대 거리를 설정합니다.

최소 거리
<near>0.02</near>

카메라로부터 0.02m, 즉 2cm보다 가까운 물체는 정상적으로 렌더링되지 않습니다.

너무 작은 값을 사용하면 깊이 버퍼 정밀도가 떨어질 수 있으므로 센서 특성에 맞는 값을 사용하는 것이 좋습니다.

최대 거리
<far>20.0</far>

카메라로부터 최대 20m까지 렌더링합니다.

20m보다 먼 물체는 카메라 영상에서 제외될 수 있습니다.

영상 노이즈 설정
<noise>
  <type>gaussian</type>
  <mean>0.0</mean>
  <stddev>0.007</stddev>
</noise>

실제 카메라처럼 영상에 작은 측정 오차를 추가합니다.

노이즈 종류
<type>gaussian</type>

가우시안 분포를 따르는 랜덤 노이즈를 사용합니다.

평균
<mean>0.0</mean>

노이즈의 평균값이 0이므로 밝기값이 한쪽 방향으로 계속 치우치지 않습니다.

표준편차
<stddev>0.007</stddev>

영상 픽셀값에 작은 크기의 랜덤 오차가 추가됩니다.

이 설정은 완전히 깨끗한 시뮬레이션 영상보다 실제 카메라에 가까운 환경에서 영상 알고리즘을 시험하는 데 도움이 됩니다.

ROS 2 카메라 플러그인
<plugin name="camera_plugin"
        filename="libgazebo_ros_camera.so">

libgazebo_ros_camera.so는 Gazebo Classic의 카메라 영상을 ROS 2 메시지로 변환해 발행하는 플러그인입니다.

Gazebo 카메라 센서가 영상을 생성하면 플러그인이 이를 일반적으로 다음 메시지 형식으로 전달합니다.

sensor_msgs/msg/Image
sensor_msgs/msg/CameraInfo
ROS 2 네임스페이스
<namespace>/camera</namespace>

카메라 관련 토픽을 /camera 네임스페이스 아래에 생성합니다.

따라서 일반적으로 다음 토픽이 만들어집니다.

/camera/image_raw
/camera/camera_info

여러 카메라를 사용하는 경우 네임스페이스를 분리하면 토픽을 체계적으로 관리할 수 있습니다.

예를 들어 다음과 같이 나눌 수 있습니다.

/front_camera/image_raw
/rear_camera/image_raw
토픽 리매핑
<remapping>image_raw:=image_raw</remapping>
<remapping>camera_info:=camera_info</remapping>

카메라 이미지와 카메라 보정 정보를 발행할 토픽 이름을 지정합니다.

현재 설정은 원래 이름과 변경할 이름이 같기 때문에 실제로는 토픽 이름이 바뀌지 않습니다.

따라서 이 두 줄은 생략해도 일반적으로 동일하게 동작합니다.

토픽 이름을 변경하려면 다음과 같이 사용할 수 있습니다.

<remapping>image_raw:=front/image_raw</remapping>
<remapping>camera_info:=front/camera_info</remapping>
카메라 이름
<camera_name>front_camera</camera_name>

ROS 2에서 카메라를 식별할 때 사용할 이름을 front_camera로 설정합니다.

카메라 보정 정보나 여러 카메라 구성에서 센서를 구분하는 데 사용됩니다.

카메라 좌표 프레임
<frame_name>camera_link</frame_name>

발행되는 이미지와 카메라 정보 메시지의 기준 프레임을 camera_link로 설정합니다.

메시지의 header.frame_id에는 일반적으로 다음 값이 들어갑니다.

camera_link

TF 트리에는 다음과 같은 연결이 있어야 합니다.

base_link
   └── camera_link
ROS 카메라 좌표계 주의사항

ROS에서는 카메라 영상 처리용 좌표계로 camera_optical_frame을 사용하는 것이 일반적입니다.

일반 로봇 링크 좌표계는 다음 방향을 사용합니다.

X축: 전방
Y축: 왼쪽
Z축: 위쪽

카메라 광학 좌표계는 다음 방향을 사용합니다.

X축: 오른쪽
Y축: 아래쪽
Z축: 전방

영상 처리 패키지나 Visual SLAM 패키지를 사용할 경우 다음과 같이 광학 프레임을 별도로 만드는 편이 좋습니다.

<link name="camera_optical_frame"/>

<joint name="camera_optical_joint" type="fixed">
  <parent link="camera_link"/>
  <child link="camera_optical_frame"/>
  <origin xyz="0 0 0"
          rpy="-1.5708 0 -1.5708"/>
</joint>

그리고 플러그인의 프레임을 다음과 같이 변경할 수 있습니다.

<frame_name>camera_optical_frame</frame_name>

단순 영상 확인에는 camera_link도 사용할 수 있지만, ROS 표준 좌표계를 따르는 비전 패키지와 연동할 때는 camera_optical_frame 사용을 권장합니다.

ROS 2 토픽 확인

카메라 관련 토픽은 다음 명령으로 확인할 수 있습니다.

ros2 topic list | grep camera

이미지 토픽의 발행 주기는 다음과 같이 확인합니다.

ros2 topic hz /camera/image_raw

설정이 정상이라면 약 30Hz로 출력됩니다.

카메라 정보는 다음 명령으로 확인합니다.

ros2 topic echo /camera/camera_info
rqt_image_view에서 영상 확인

ROS 2의 rqt_image_view를 사용하면 카메라 영상을 간단하게 확인할 수 있습니다.

ros2 run rqt_image_view rqt_image_view

프로그램이 실행되면 영상 토픽으로 다음 항목을 선택합니다.

/camera/image_raw

정상적으로 설정되었다면 Gazebo 카메라가 촬영하는 전방 영상이 표시됩니다.

RViz2에서 카메라 확인

RViz2에서도 카메라 영상을 확인할 수 있습니다.

rviz2

RViz2에서 다음과 같이 설정합니다.

  1. Add 버튼을 클릭합니다.
  2. Image 디스플레이를 추가합니다.
  3. Image Topic을 /camera/image_raw로 설정합니다.
  4. 필요하면 Camera 디스플레이를 추가합니다.
  5. Camera Info Topic을 /camera/camera_info로 설정합니다.

TF 프레임과 카메라 정보가 정상적으로 연결되어 있어야 올바르게 표시됩니다.

8. Launch 파일 작성

launch/gazebo_sensor_demo.launch.py 파일을 만듭니다.

touch launch/gazebo_sensor_demo.launch.py

import os

from launch import LaunchDescription
from launch.actions import ExecuteProcess
from launch_ros.actions import Node
from launch.substitutions import Command
from ament_index_python.packages import get_package_share_directory


def generate_launch_description():
    pkg_name = 'mobile_robot_sensors'
    pkg_share = get_package_share_directory(pkg_name)

    urdf_file = os.path.join(pkg_share, 'urdf', 'mobile_robot.urdf.xacro')
    world_file = os.path.join(pkg_share, 'worlds', 'empty.world')

    robot_description = Command(['xacro ', urdf_file])

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

    gazebo = ExecuteProcess(
        cmd=[
            'gazebo',
            '--verbose',
            world_file,
            '-s', 'libgazebo_ros_factory.so'
        ],
        output='screen'
    )

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

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

    return LaunchDescription([
        robot_state_publisher,
        gazebo,
        spawn_robot,
        rviz
    ])

필요한 모듈 불러오기
import os

from launch import LaunchDescription
from launch.actions import ExecuteProcess
from launch_ros.actions import Node
from launch.substitutions import Command
from ament_index_python.packages import get_package_share_directory

각 모듈의 역할은 다음과 같습니다.

  • ExecuteProcess: Gazebo와 같은 외부 프로그램을 실행합니다.
  • Command: 외부 명령을 실행한 결과를 Launch 파라미터로 사용합니다.
패키지 경로 가져오기
pkg_name = 'mobile_robot_sensors'
pkg_share = get_package_share_directory(pkg_name)

mobile_robot_sensors 패키지의 설치 경로를 가져옵니다.

일반적으로 빌드 후 패키지는 다음 위치에 설치됩니다.

install/mobile_robot_sensors/share/mobile_robot_sensors

절대 경로를 직접 작성하지 않고 패키지 경로를 검색하므로 다른 작업공간이나 컴퓨터에서도 코드를 재사용하기 쉽습니다.

Xacro와 월드 파일 경로 설정
urdf_file = os.path.join(
    pkg_share,
    'urdf',
    'mobile_robot.urdf.xacro'
)

world_file = os.path.join(
    pkg_share,
    'worlds',
    'empty.world'
)

로봇 모델과 Gazebo 월드 파일의 경로를 설정합니다.

패키지 구조는 다음과 같은 형태를 가정합니다.

mobile_robot_sensors/
├── launch/
│   └── simulation.launch.py
├── urdf/
│   └── mobile_robot.urdf.xacro
├── worlds/
│   └── empty.world
├── CMakeLists.txt
└── package.xml
Xacro 파일을 URDF로 변환하기
robot_description = Command([
    'xacro ',
    urdf_file
])

Command 치환 기능을 이용해 다음과 같은 명령을 실행합니다.

xacro mobile_robot.urdf.xacro

Xacro 파일은 변수, 매크로, 조건문 등을 사용할 수 있는 확장된 URDF 형식입니다.

Xacro 명령의 출력 결과는 완성된 URDF 문자열이며, 이 문자열이 robot_description에 저장됩니다.

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

robot_state_publisher는 URDF에 정의된 링크와 조인트 구조를 읽어 로봇의 TF 좌표계를 발행합니다.

예를 들어 로봇 모델에 다음 프레임이 있다면:

base_link
├── left_wheel
├── right_wheel
├── lidar_link
├── imu_link
└── camera_link

각 링크 사이의 좌표 관계가 /tf 또는 /tf_static 토픽으로 발행됩니다.

robot_description 파라미터에는 앞에서 Xacro를 변환한 URDF 문자열이 전달됩니다.

Gazebo Classic 실행
gazebo = ExecuteProcess(
    cmd=[
        'gazebo',
        '--verbose',
        world_file,
        '-s',
        'libgazebo_ros_factory.so'
    ],
    output='screen'
)

ExecuteProcess를 이용해 Gazebo Classic을 직접 실행합니다.

실제로 다음과 비슷한 명령이 실행됩니다.

gazebo --verbose empty.world \
-s libgazebo_ros_factory.so
gazebo

Gazebo Classic의 서버와 GUI를 함께 실행합니다.

--verbose

Gazebo의 상세 로그를 터미널에 출력합니다.

플러그인 로딩 실패, 월드 파일 오류, 센서 오류 등을 확인할 때 유용합니다.

world_file

실행할 Gazebo 월드 파일을 지정합니다.

현재 예제에서는 다음 파일을 사용합니다.

worlds/empty.world
libgazebo_ros_factory.so
'-s', 'libgazebo_ros_factory.so'

Gazebo 안에서 ROS 2 요청을 이용해 모델을 동적으로 생성할 수 있도록 하는 시스템 플러그인입니다.

이 플러그인이 로드되어야 spawn_entity.py를 이용해 Gazebo 월드에 로봇을 추가할 수 있습니다.

Gazebo 월드에 로봇 생성
spawn_robot = Node(
    package='gazebo_ros',
    executable='spawn_entity.py',
    arguments=[
        '-topic', 'robot_description',
        '-entity', 'mobile_robot'
    ],
    output='screen'
)

spawn_entity.py는 URDF 또는 SDF 모델을 Gazebo 월드에 생성하는 ROS 2 프로그램입니다.

로봇 모델 정보 가져오기
'-topic', 'robot_description'

robot_description을 통해 전달되는 URDF 정보를 사용해 로봇을 생성합니다.

Gazebo 모델 이름
'-entity', 'mobile_robot'

Gazebo 월드 안에 생성되는 모델의 이름을 mobile_robot으로 설정합니다.

같은 이름의 모델이 이미 월드에 존재하면 중복 생성 오류가 발생할 수 있습니다.

생성 위치를 별도로 지정하지 않았으므로 기본적으로 월드 원점에 생성됩니다.

X = 0
Y = 0
Z = 0

로봇이 바닥과 겹쳐 생성된다면 다음과 같이 Z축 위치를 지정할 수 있습니다.

arguments=[
    '-topic', 'robot_description',
    '-entity', 'mobile_robot',
    '-x', '0.0',
    '-y', '0.0',
    '-z', '0.1'
]
RViz2 실행
rviz = Node(
    package='rviz2',
    executable='rviz2',
    output='screen'
)

RViz2를 실행해 로봇 모델, TF, LiDAR, 카메라, 오도메트리 등의 ROS 2 데이터를 확인합니다.

RViz2에서 일반적으로 다음 디스플레이를 추가할 수 있습니다.

  • RobotModel
  • TF
  • LaserScan
  • Image
  • Camera
  • Odometry

로봇 모델을 표시하려면 RobotModel 디스플레이의 Description Topic을 일반적으로 다음과 같이 설정합니다.

/robot_description
실행 항목 등록
return LaunchDescription([
    robot_state_publisher,
    gazebo,
    spawn_robot,
    rviz
])

앞에서 정의한 노드와 프로세스를 하나의 LaunchDescription에 등록합니다.

Launch 파일을 실행하면 다음 항목들이 함께 시작됩니다.

robot_state_publisher
Gazebo Classic
spawn_entity.py
RViz2
시뮬레이션 시간 사용

Gazebo 시뮬레이션에서는 실제 컴퓨터 시간보다 Gazebo가 발행하는 /clock 시간을 사용하는 것이 일반적입니다.

따라서 robot_state_publisher와 RViz2에 다음 설정을 추가하는 편이 좋습니다.

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

RViz2에도 시뮬레이션 시간을 적용할 수 있습니다.

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

9. World 파일 작성

worlds/empty.world 파일을 만듭니다.

touch worlds/empty.world

<?xml version="1.0" ?>
<sdf version="1.6">
  <world name="empty_world">

    <include>
      <uri>model://ground_plane</uri>
    </include>

    <include>
      <uri>model://sun</uri>
    </include>

    <model name="box_obstacle">
      <static>true</static>
      <pose>1.5 0 0.25 0 0 0</pose>
      <link name="link">
        <collision name="collision">
          <geometry>
            <box>
              <size>0.5 0.5 0.5</size>
            </box>
          </geometry>
        </collision>
        <visual name="visual">
          <geometry>
            <box>
              <size>0.5 0.5 0.5</size>
            </box>
          </geometry>
        </visual>
      </link>
    </model>

  </world>
</sdf>

장애물 박스를 하나 넣은 이유는 LiDAR와 Camera가 볼 대상이 있어야 실습이 직관적이기 때문입니다.

10. CMakeLists.txt 수정

CMakeLists.txt에 설치 규칙을 추가합니다.

cmake_minimum_required(VERSION 3.8)
project(mobile_robot_sensors)

find_package(ament_cmake REQUIRED)

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

ament_package()

11. package.xml 확인

package.xml은 최소한 다음 의존성을 포함하는 것이 좋습니다.

<?xml version="1.0"?>
<package format="3">
  <name>mobile_robot_sensors</name>
  <version>0.0.1</version>
  <description>Mobile robot Gazebo sensor plugin demo for ROS 2</description>
  <maintainer email="user@example.com">user</maintainer>
  <license>Apache-2.0</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <exec_depend>gazebo_ros</exec_depend>
  <exec_depend>gazebo_plugins</exec_depend>
  <exec_depend>robot_state_publisher</exec_depend>
  <exec_depend>xacro</exec_depend>
  <exec_depend>rviz2</exec_depend>

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

12. 빌드 및 실행

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

실행:

ros2 launch mobile_robot_sensors gazebo_sensor_demo.launch.py

13. 토픽 확인

실행 후 새 터미널에서:

source ~/ros2_ws/install/setup.bash
ros2 topic list

정상이라면 다음과 비슷하게 나옵니다.

/camera/camera_info
/camera/image_raw
/clock
/imu/data
/parameter_events
/robot_description
/rosout
/scan
/tf
/tf_static

각 토픽 확인:

ros2 topic echo /scan --once
ros2 topic echo /imu/data --once
ros2 topic echo /camera/front_camera/camera_info --once

이미지 데이터는 echo로 보면 너무 많으므로 rqt_image_view를 쓰는 것이 낫습니다.

ros2 run rqt_image_view rqt_image_view

14. RViz2에서 센서 확인

RViz2에서 다음을 추가합니다.

1) TF 확인

Add → TF

확인할 프레임:

base_link
lidar_link
imu_link
camera_link

2) LiDAR 확인

Add → LaserScan
Topic → /scan
Fixed Frame → base_link 또는 lidar_link

장애물 박스가 로봇 앞에 있으므로 LiDAR 점들이 보이면 정상입니다.

3) Camera 확인

Add → Image
Topic → /camera/image_raw
Reliability Policy → Best Effort

화면에 Gazebo 월드가 보이면 정상입니다.

4) IMU 확인

RViz에서 IMU는 바로 직관적으로 보기 어렵습니다. 우선 명령어로 확인합니다.

ros2 topic echo /imu/data

로봇이 움직이지 않으면 angular velocity는 거의 0에 가깝고, linear acceleration의 z축에는 중력 성분이 반

Leave a Comment