ROS 2 Humble에서 Gazebo 사용법 완전 정복

1. Gazebo란 무엇인가요?

Gazebo는 로봇을 실제 하드웨어 없이 가상 환경에서 테스트할 수 있는 로봇 시뮬레이터입니다. ROS 2와 함께 사용하면 다음을 실습할 수 있습니다.

                         기능                                                                           설명

로봇 모델 시각화URDF, Xacro, SDF 기반 로봇 표시
물리 시뮬레이션중력, 충돌, 마찰, 관성 적용
센서 시뮬레이션LiDAR, 카메라, IMU, GPS 등
구동기 시뮬레이션바퀴, 조인트, 모터 제어
ROS 2 연동토픽, 서비스, TF, ros2_control 연결
자율주행 테스트SLAM, Navigation2, 경로 추종

ROS 2 공식 문서도 Gazebo 튜토리얼을 통해 로봇 시뮬레이션 설정, URDF 사용, 센서 데이터 시각화, diff-drive 로봇 이동 실습을 안내합니다.

2. 먼저 알아야 할 핵심: Gazebo Classic vs Gazebo Sim/Hamonic

ROS 2 Humble에서 가장 많이 헷갈리는 부분입니다.

현재 Gazebo 계열은 크게 두 종류가 있습니다.

             구분                             Gazebo Classic                       Gazebo Sim

예전 이름GazeboIgnition Gazebo → Gazebo
대표 명령어gazebogz sim
ROS 연동gazebo_ros_pkgsros_gz, ros_gz_bridge
Humble 호환성많이 사용됨사용 가능
Jazzy 이후지원 약화/불가권장
상태2025년 1월 EOL현재 권장 방향

중요한 점은 Gazebo Classic은 2025년 1월에 EOL(En이 되었고, gazebo_ros2_control도 더 이상 업데이트되지 않는다고 명시되어 있습니다. 따라서 ROS 2 Humble 학습자는 기존 자료가 많은 Gazebo Classic으로 기초를 익히되, 신규 프로젝트는 가능하면 Gazebo Sim + ros_gz 방향을 같이 보셔야 합니다.

3. 실습 환경

이 글의 기준 환경입니다.

                              항목                                                                    값

OSUbuntu 22.04 LTS
ROS 2Humble Hawksbill
Gazebo ClassicGazebo 11 계열
Gazebo SimFortress/Garden/Harmonic 계열 가능
Shellbash
권장 PCRAM 8GB 이상, GPU 있으면 좋음

ROS 2 Humble은 Ubuntu 22.04 Jammy에서 사용하는 것이 가장 안정적입니다. ROS 2 공식 설치 문서도 Humble의 Ubuntu deb 설치 절차를 제공합니다. ROS2 Humble과 Gazebo Classic은 이미 설치되어 있다고 가정하겠습니다.

4. Gazebo Classic 단독 실행

gazebo

Gazebo의 Server는 실제 시뮬레이션 계산을 담당합니다.

여기서 처리하는 대표적인 작업은 다음과 같습니다.

항목설명

물리 계산중력, 충돌, 관성, 마찰, 조인트 동작 계산
모델 상태 관리로봇, 벽, 바닥, 장애물 위치 관리
센서 계산라이다, 카메라, IMU, 접촉 센서 데이터 생성
플러그인 실행이동 로봇 제어, 센서 출력, 사용자 명령 처리

Gazebo의 World는 모델, 링크, 조인트, 센서, 플러그인 등을 담는 시뮬레이션 컨테이너 역할을 합니다. Gazebo Classic 문서에서도 World는 시뮬레이션 환경 안의 모든 모델과 구성요소를 포함하는 컨테이너로 설명됩니다. 최신 Gazebo에서도 SDF World 개념은 동일하게 중요합니다.

GUI Client는 사용자가 보는 화면입니다.

GUI에서 할 수 있는 작업은 다음과 같습니다.

기능설명

3D View월드, 로봇, 장애물 확인
Entity Tree월드 안의 모델, 링크, 센서 구조 확인
Inspector선택한 객체의 위치, 자세, 속성 확인
Resource Spawner모델 삽입
View Control카메라 시점 이동, 확대, 회전

즉, Server가 “계산 담당”이라면 GUI Client는 “화면 표시와 사용자 조작 담당”입니다.

또는 서버와 클라이언트를 분리해서 실행할 수 있습니다.

gzserver

다른 터미널:

gzclient

                   명령어                                                            의미

gazebo서버와 GUI를 함께 실행
gzserver물리 시뮬레이션 서버만 실행
gzclientGUI 클라이언트만 실행

실전에서는 GUI 없이 시뮬레이션만 돌릴 때 gzserver를 사용합니다.

5. ROS 2에서 Gazebo를 실행하는 기본 구조

ROS 2에서는 보통 launch 파일에서 Gazebo를 실행합니다.

Gazebo Classic에서는 gazebo_ros 패키지의 launch 파일을 자주 씁니다.

ros2 launch gazebo_ros gazebo.launch.py

빈 월드가 실행됩니다.

토픽 확인:

ros2 topic list

시뮬레이션 시간이 켜져 있으면 다음 토픽이 보일 수 있습니다.

/clock
/parameter_events
/performance_metrics
/rosout

Gazebo 시뮬레이션에서는 실제 PC 시간이 아니라 /clock을 기준으로 동작해야 하는 경우가 많습니다. 그래서 SLAM, Navigation2, 센서 처리에서는 보통 다음 옵션을 씁니다.

use_sim_time:=True

6. Gazebo에서 중요한 파일 형식

1) URDF

URDF는 ROS에서 많이 사용하는 로봇 모델 형식입니다.

주로 다음을 표현합니다.

                        요소                                                                   설명

link로봇의 몸체, 바퀴, 센서 등
jointlink 사이의 연결 관계
visual화면에 보이는 모양
collision충돌 계산용 형상
inertial질량, 관성
gazebo tagGazebo 전용 확장 설정

간단한 link 예시:

<link name="base_link">
  <visual>
    <geometry>
      <box size="0.4 0.3 0.1"/>
    </geometry>
  </visual>

  <collision>
    <geometry>
      <box size="0.4 0.3 0.1"/>
    </geometry>
  </collision>

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

2) Xacro

Xacro는 URDF를 더 편하게 작성하기 위한 매크로 시스템입니다.

반복되는 바퀴, 센서, 링크를 함수처럼 만들 수 있습니다.

예:

<xacro:macro name="wheel" params="name x y">
  <link name="${name}_link">
    <visual>
      <geometry>
        <cylinder radius="0.05" length="0.03"/>
      </geometry>
    </visual>
  </link>
</xacro:macro>

실제 로봇 패키지는 대부분 .urdf.xacro를 사용합니다.

3) SDF

Gazebo World와 로봇 모델은 보통 SDF, 즉 Simulation Description Format으로 작성합니다. SDF는 XML 기반 형식이며, 로봇 시뮬레이터에서 환경과 객체를 설명하기 위한 포맷입니다. SDFormat은 로봇 시뮬레이터를 위한 객체와 환경 설명 XML 형식으로 정의됩니다.

SDF는 다음을 표현할 수 있습니다.

SDF
├── world
│   ├── physics
│   ├── scene
│   ├── light
│   ├── model
│   ├── include
│   └── plugin
│
└── model
    ├── link
    ├── joint
    ├── collision
    ├── visual
    ├── sensor
    └── plugin

ROS 2에서 로봇 형상은 URDF/Xacro로 많이 만들고, Gazebo 환경은 SDF World 파일로 구성하는 경우가 많습니다. World는 SDF로봇 모델은 URDF/Xacro 또는 SDF라고 이해하면 됩니다.

7. Gazebo 월드 파일 구조

Gazebo의 World 파일은 시뮬레이션 환경 전체를 정의하는 파일입니다.

예를 들어 모바일 로봇 실습에서 World 파일에는 다음 요소가 들어갑니다.

mobile_robot_world.sdf
├── 바닥
├── 벽
├── 장애물
├── 조명
├── 중력
├── 물리 엔진 설정
├── 로봇 모델 include
├── 라이다 테스트용 구조물
└── Gazebo 시스템 플러그인

SDF 명세에서 <world> 요소는 모델, scene, physics, plugins 등을 포함하는 전체 월드 설명 컨테이너입니다. 즉, 월드 파일은 단순 배경 파일이 아니라 시뮬레이션 조건 전체를 결정하는 핵심 설정 파일입니다.

월드 파일은 .world 또는 .sdf 형식을 사용합니다.

로보티즈에서 제공하는 turtlebot3_simulation 패키지를 다운로드 합니다.

cd ~/turtlebot3_ws/src/
git clone -b humble https://github.com/ROBOTIS-GIT/turtlebot3_simulations.git
cd ~/turtlebot3_ws
colcon build

가장 기본적인 World 파일은 다음 구조를 가집니다.

<?xml version="1.0" ?>
<sdf version="1.10">
  <world name="mobile_robot_world">

    <!-- 1. 물리 엔진 설정 -->
    <physics name="1ms" type="ignored">
      <max_step_size>0.001</max_step_size>
      <real_time_factor>1.0</real_time_factor>
    </physics>

    <!-- 2. 기본 Gazebo 시스템 플러그인 -->
    <plugin
      filename="gz-sim-physics-system"
      name="gz::sim::systems::Physics">
    </plugin>

    <plugin
      filename="gz-sim-user-commands-system"
      name="gz::sim::systems::UserCommands">
    </plugin>

    <plugin
      filename="gz-sim-scene-broadcaster-system"
      name="gz::sim::systems::SceneBroadcaster">
    </plugin>

    <!-- 3. 조명 -->
    <light type="directional" name="sun">
      <cast_shadows>true</cast_shadows>
      <pose>0 0 10 0 0 0</pose>
      <diffuse>0.8 0.8 0.8 1</diffuse>
      <specular>0.2 0.2 0.2 1</specular>
      <direction>-0.5 0.1 -0.9</direction>
    </light>

    <!-- 4. 바닥 -->
    <model name="ground_plane">
      <static>true</static>
      <link name="link">
        <collision name="collision">
          <geometry>
            <plane>
              <normal>0 0 1</normal>
              <size>20 20</size>
            </plane>
          </geometry>
        </collision>

        <visual name="visual">
          <geometry>
            <plane>
              <normal>0 0 1</normal>
              <size>20 20</size>
            </plane>
          </geometry>
          <material>
            <ambient>0.6 0.6 0.6 1</ambient>
            <diffuse>0.6 0.6 0.6 1</diffuse>
          </material>
        </visual>
      </link>
    </model>

  </world>
</sdf>

이 파일은 “빈 공간 + 바닥 + 조명 + 기본 시스템 플러그인” 정도만 있는 최소 실습용 월드입니다. Gazebo 공식 SDF World 튜토리얼도 SDF로 World를 만들고 모델을 추가하는 방식으로 설명합니다.

1) <sdf>

<sdf version="1.10">

SDF 파일의 최상위 태그입니다.

여기서 version은 SDF 문법 버전입니다. 최신 강의에서는 Gazebo Sim 버전에 맞는 SDF 버전을 사용하면 됩니다. 너무 오래된 예제에서는 1.6, 1.7 등이 보일 수 있습니다.

2) <world>

<world name="mobile_robot_world">

하나의 시뮬레이션 공간을 정의합니다.

name은 월드 이름입니다.

예:

<world name="warehouse_world">
<world name="indoor_navigation_world">
<world name="slam_test_world">

모바일 로봇 강의에서는 보통 다음과 같은 월드 이름을 씁니다.

월드 이름용도

empty_world기본 조작 실습
obstacle_world장애물 회피 실습
maze_world경로 계획 실습
slam_worldSLAM 실습
warehouse_world물류 로봇 실습

3) <physics>

<physics name="1ms" type="ignored">
<max_step_size>0.001</max_step_size>
<real_time_factor>1.0</real_time_factor>
</physics>

물리 시뮬레이션 설정입니다.

태그의미

max_step_size한 번에 계산하는 시뮬레이션 시간 간격
real_time_factor실제 시간 대비 시뮬레이션 속도
real_time_update_rate초당 업데이트 목표 횟수

예를 들어 max_step_size가 0.001이면 1ms 단위로 물리 계산을 하겠다는 뜻입니다. 값이 작을수록 정밀하지만 CPU 부담이 커집니다.

모바일 로봇 실습에서는 보통 다음 정도면 충분합니다.

<physics name="1ms" type="ignored">
<max_step_size>0.001</max_step_size>
<real_time_factor>1.0</real_time_factor>
</physics>

4) <plugin>

<plugin
filename="gz-sim-physics-system"
name="gz::sim::systems::Physics">
</plugin>

Gazebo의 기능을 추가하는 시스템입니다.

Gazebo에서는 동작 기능이 System Plugin으로 구현됩니다. Gazebo의 동작은 시스템이 엔티티와 컴포넌트를 조작하는 방식으로 구현되며, 시스템은 SDF 파일이나 Server API 등을 통해 로드될 수 있다고 설명합니다.

기본 World에서 자주 쓰는 플러그인은 다음과 같습니다.

플러그인역할

Physics물리 시뮬레이션 실행
UserCommandsGUI 또는 명령으로 모델 삽입/삭제/변경
SceneBroadcasterGUI에 월드 장면 전달
Contact충돌/접촉 정보 처리
Sensors센서 데이터 업데이트

5) <light>

<light type="directional" name="sun">
<pose>0 0 10 0 0 0</pose>
<diffuse>0.8 0.8 0.8 1</diffuse>
<direction>-0.5 0.1 -0.9</direction>
</light>

조명을 정의합니다.

모바일 로봇 실습에서 조명은 단순히 보기 좋은 화면을 만드는 용도만은 아닙니다. 카메라 기반 인식 실습을 할 때는 조명 방향과 밝기에 따라 영상 품질이 달라집니다.

조명 종류는 보통 다음을 사용합니다.

타입설명

directional태양광처럼 일정 방향에서 비추는 조명
point전구처럼 한 점에서 퍼지는 조명
spot특정 방향으로 비추는 스포트라이트

라이다 중심 수업이면 조명은 단순하게 두어도 됩니다. 카메라 수업이면 조명 설정을 더 신경 써야 합니다.

6) <model>

<model name="box_obstacle">
<static>true</static>
...
</model>

월드 안에 들어가는 물체입니다.

모바일 로봇 관점에서는 다음이 모두 모델입니다.

모델예

로봇차동구동 로봇, AMR, TurtleBot 계열
바닥ground plane
실내 주행 테스트용 벽
장애물박스, 원통, 팔레트
구조물복도, 방, 창고 랙

SDF 명세에서 <model>은 로봇 또는 물리 객체 전체를 정의하는 요소입니다.

7) <static>

<static>true</static>

모델이 움직이지 않는 고정 물체인지 설정합니다.

값의미

true움직이지 않음
false물리 계산에 따라 움직일 수 있음

바닥, 벽, 건물, 고정 장애물은 보통 true입니다.

<model name="wall">
<static>true</static>
</model>

반대로 로봇, 굴러가는 박스, 밀 수 있는 물체는 false로 둡니다.

8 )<link>

<link name="link">

모델을 구성하는 물리 단위입니다.

예를 들어 모바일 로봇 모델은 다음 링크들로 구성될 수 있습니다.

mobile_robot
├── base_link
├── left_wheel_link
├── right_wheel_link
├── caster_link
├── lidar_link
└── camera_link

World 안의 단순 장애물도 최소 하나의 link를 가져야 합니다.

9) <collision>

<collision name="collision">
<geometry>
<box>
<size>1 1 1</size>
</box>
</geometry>
</collision>

물리 충돌 계산에 쓰이는 형상입니다.

중요한 점은 보이는 모양과 충돌 모양은 다를 수 있다는 것입니다.

예를 들어 실제로는 복잡한 창고 랙 mesh를 보여주더라도, 충돌 계산은 단순 box 여러 개로 처리하는 게 효율적입니다.

visual     → 사람이 보는 모양
collision → 물리 엔진이 충돌 계산에 쓰는 모양

모바일 로봇 실습에서는 충돌 형상을 너무 복잡하게 만들면 시뮬레이션이 느려집니다. 그래서 벽, 박스, 원통 위주로 구성하는 것이 좋습니다.

10) <visual>

<visual name="visual">
<geometry>
<box>
<size>1 1 1</size>
</box>
</geometry>
</visual>

화면에 보이는 형상입니다.

visual은 예쁘게 보여주기 위한 것이고, collision은 물리 계산용입니다.

Gazebo를 처음시작하는 초보자는 다음처럼 단순 도형만 사용해도 충분합니다.

형상용도

box벽, 박스 장애물
cylinder기둥, 원형 장애물
sphere공, 마커
plane바닥
mesh실제 CAD/3D 모델

11) <pose>

<pose>1 2 0.5 0 0 0</pose>

객체의 위치와 자세를 나타냅니다.

순서는 다음과 같습니다.

x y z roll pitch yaw

값의미

x앞뒤 위치
y좌우 위치
z높이
rollx축 회전
pitchy축 회전
yawz축 회전

예를 들어 다음은 장애물을 x=2m, y=0m, z=0.5m 위치에 둔다는 뜻입니다.

<pose>2 0 0.5 0 0 0</pose>

크기가 1 1 1인 박스는 중심이 가운데에 있으므로, 바닥 위에 올리려면 z를 0.5로 둡니다.

12) <include>

<include>
<uri>model://my_mobile_robot</uri>
<pose>0 0 0.1 0 0 0</pose>
</include>

외부 모델을 월드에 불러올 때 사용합니다.

Gazebo Fuel 또는 로컬 모델 경로에 있는 모델을 가져올 수 있습니다. Fuel 모델을 다운로드한 뒤 로컬 모델 디렉터리에 두고 GZ_SIM_RESOURCE_PATH를 설정하거나, SDF snippet을 World 파일에 붙여넣는 방식을 사용합니다.

모바일 로봇 실습에서는 보통 다음처럼 사용합니다.

<include>
<uri>model://turtlebot3_burger</uri>
<pose>0 0 0.01 0 0 0</pose>
</include>

단, 실제 모델 이름과 경로는 설치한 패키지에 따라 다릅니다.

8. ROS 2 패키지로 Gazebo 월드 실행하기

1) 패키지 생성

mkdir -p ~/gazebo_ws/src
cd ~/gazebo_ws/src
ros2 pkg create mobile_robot_gazebo --build-type ament_cmake

폴더 구조를 만듭니다.

cd ~/gazebo_ws/src/mobile_robot_gazebo
mkdir worlds launch

최종 구조는 다음처럼 만듭니다.

mobile_robot_gazebo/
├── CMakeLists.txt
├── package.xml
├── launch/
│   └── mobile_world.launch.py
└── worlds/
    └── basic_mobile_world.sdf

2) World 파일 작성

worlds/basic_mobile_world.sdf 파일을 만듭니다.

touch worlds/basic_mobile_world.sdf

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

<!-- Gazebo Classic 물리 엔진 -->
<physics name="default_physics" type="ode">
<max_step_size>0.001</max_step_size>
<real_time_factor>1.0</real_time_factor>
<real_time_update_rate>1000</real_time_update_rate>
</physics>

<!-- 태양광 -->
<light type="directional" name="sun">
<cast_shadows>true</cast_shadows>
<pose>0 0 10 0 0 0</pose>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.2 0.2 0.2 1</specular>
<direction>-0.5 0.1 -0.9</direction>
</light>

<!-- 바닥 -->
<model name="ground_plane">
<static>true</static>

<link name="link">
<collision name="collision">
<geometry>
<plane>
<normal>0 0 1</normal>
<size>20 20</size>
</plane>
</geometry>
</collision>

<visual name="visual">
<geometry>
<plane>
<normal>0 0 1</normal>
<size>20 20</size>
</plane>
</geometry>

<material>
<ambient>0.5 0.5 0.5 1</ambient>
<diffuse>0.5 0.5 0.5 1</diffuse>
</material>
</visual>
</link>
</model>

<!-- 첫 번째 장애물 -->
<model name="box_obstacle_1">
<static>true</static>
<pose>2 0 0.5 0 0 0</pose>

<link name="link">
<collision name="collision">
<geometry>
<box>
<size>1 1 1</size>
</box>
</geometry>
</collision>

<visual name="visual">
<geometry>
<box>
<size>1 1 1</size>
</box>
</geometry>

<material>
<ambient>0.8 0.1 0.1 1</ambient>
<diffuse>0.8 0.1 0.1 1</diffuse>
</material>
</visual>
</link>
</model>

<!-- 두 번째 장애물 -->
<model name="box_obstacle_2">
<static>true</static>
<pose>-2 1 0.5 0 0 0</pose>

<link name="link">
<collision name="collision">
<geometry>
<box>
<size>0.8 0.8 1.0</size>
</box>
</geometry>
</collision>

<visual name="visual">
<geometry>
<box>
<size>0.8 0.8 1.0</size>
</box>
</geometry>

<material>
<ambient>0.1 0.1 0.8 1</ambient>
<diffuse>0.1 0.1 0.8 1</diffuse>
</material>
</visual>
</link>
</model>

</world>
</sdf>

이 월드는 다음 요소를 포함합니다.

요소설명

바닥20m × 20m 평면
조명태양광 역할
박스 장애물 1로봇 주행 테스트용 장애물
박스 장애물 2추가 장애물
a. SDF 버전과 월드 정의
<sdf version="1.6">
  <world name="basic_mobile_world">

SDF 버전은 1.6을 사용하며, 시뮬레이션 월드의 이름은 basic_mobile_world로 설정되어 있습니다. 하나의 SDF 파일 안에는 여러 월드를 정의할 수 있지만, 이 파일에서는 하나의 월드만 정의합니다.

b. 물리 시뮬레이션 설정
<physics name="default_physics" type="ode">
  <max_step_size>0.001</max_step_size>
  <real_time_factor>1.0</real_time_factor>
</physics>

물리 엔진의 시간 진행 조건을 설정하는 부분입니다.

  • max_step_size는 물리 시뮬레이션의 한 스텝 크기를 의미합니다.
  • 0.001초로 설정되어 있으므로 물리 계산은 최대 1ms 간격으로 수행됩니다.
  • real_time_factor1.0이므로 이상적인 조건에서는 실제 시간과 동일한 속도로 시뮬레이션이 진행됩니다.
  • type="ignored"는 특정 물리 엔진을 명시적으로 고정하지 않고 Gazebo Sim의 기본 처리 방식에 맡기는 설정입니다.

작은 스텝 크기를 사용하면 충돌이나 로봇 움직임을 더 정밀하게 계산할 수 있지만, 컴퓨터 연산 부하가 증가할 수 있습니다.

c. 태양광 설정
<light type="directional" name="sun">

월드 전체를 비추는 방향성 광원을 정의합니다. 방향성 광원은 태양광처럼 매우 먼 거리에서 일정한 방향으로 빛이 들어오는 형태입니다.

<cast_shadows>true</cast_shadows>

객체의 그림자를 활성화합니다.

<pose>0 0 10 0 0 0</pose>

광원의 위치와 자세를 지정합니다. 위치는 X, Y, Z 순서로 0, 0, 10이며, 회전값은 모두 0입니다. 방향성 광원에서는 위치보다 빛의 방향이 더 중요합니다.

<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.2 0.2 0.2 1</specular>

diffuse는 물체 표면에 넓게 반사되는 빛의 색과 세기를 의미하고, specular는 물체 표면에서 반짝이는 정반사 성분을 의미합니다. 각 값은 빨강, 초록, 파랑, 투명도 순서의 RGBA 형식입니다.

<direction>-0.5 0.1 -0.9</direction>

빛이 향하는 방향을 나타냅니다. Z축 값이 음수이므로 빛이 위쪽에서 지면 방향으로 비춥니다.

d. 바닥 평면 모델
<model name="ground_plane">
  <static>true</static>

ground_plane은 로봇과 장애물이 놓일 바닥입니다. static 값이 true이므로 물리적인 힘이나 충돌을 받아도 움직이지 않습니다.

<plane>
  <normal>0 0 1</normal>
  <size>20 20</size>
</plane>

바닥은 평면 형상으로 정의되어 있으며 크기는 가로 20m, 세로 20m입니다. normal 값이 0 0 1이므로 평면의 법선 방향은 위쪽 Z축입니다.

바닥에는 collisionvisual이 모두 정의되어 있습니다.

  • collision은 실제 물리 충돌 계산에 사용됩니다.
  • visual은 Gazebo 화면에 표시되는 외형을 정의합니다.

바닥의 색상은 다음과 같이 회색으로 설정되어 있습니다.

<ambient>0.5 0.5 0.5 1</ambient>
<diffuse>0.5 0.5 0.5 1</diffuse>

diffuse는 물체 표면이 빛을 받을 때 보이는 확산 반사 색상을 RGBA 형식으로 설정합니다. RGB 값이 모두 같으므로 결과는 중간 밝기의 회색입니다.

e. 첫 번째 박스 장애물
<model name="box_obstacle_1">
  <static>true</static>
  <pose>2 0 0.5 0 0 0</pose>

첫 번째 장애물의 이름은 box_obstacle_1이며 정적인 물체로 설정되어 있습니다.

장애물의 중심 위치는 다음과 같습니다.

  • X축: 2m
  • Y축: 0m
  • Z축: 0.5m

박스의 높이가 1m이기 때문에 중심을 Z축 0.5m에 배치하면 박스의 바닥면이 지면과 정확하게 맞닿습니다.

<size>1 1 1</size>

장애물의 크기는 가로, 세로, 높이가 각각 1m인 정육면체입니다.

재질 색상은 빨간색 계열입니다.

<ambient>0.8 0.1 0.1 1</ambient>
<diffuse>0.8 0.1 0.1 1</diffuse>
f. 두 번째 박스 장애물
<model name="box_obstacle_2">
  <static>true</static>
  <pose>-2 1 0.5 0 0 0</pose>

두 번째 장애물의 이름은 box_obstacle_2이며, 다음 위치에 배치됩니다.

  • X축: -2m
  • Y축: 1m
  • Z축: 0.5m
<size>0.8 0.8 1.0</size>

장애물의 크기는 가로 0.8m, 세로 0.8m, 높이 1.0m입니다.

재질 색상은 파란색 계열입니다.

<ambient>0.1 0.1 0.8 1</ambient>
<diffuse>0.1 0.1 0.8 1</diffuse>
g. 전체 월드 구성

이 월드는 다음 요소로 구성됩니다.

  • 20m × 20m 크기의 회색 바닥
  • X축 2m 위치에 배치된 빨간색 정육면체 장애물
  • X축 -2m, Y축 1m 위치에 배치된 파란색 박스 장애물
  • 월드 전체를 비추는 방향성 태양광
  • 물리 계산과 화면 표시를 위한 Gazebo Sim 시스템 플러그인

아직 모바일 로봇 모델은 포함되어 있지 않습니다. 따라서 별도의 URDF, Xacro 또는 SDF 로봇 모델을 월드에 추가하거나, ROS 2 launch 파일에서 ros_gz_sim create 명령을 이용해 로봇을 생성해야 합니다.

3) CMakeLists.txt 수정

CMakeLists.txt에 다음을 추가합니다.

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

전체 예시는 다음과 같습니다.

cmake_minimum_required(VERSION 3.8)
project(mobile_robot_gazebo)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)

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

ament_package()

ROS 2 패키지에서 launch, world 파일을 설치하지 않으면 ros2 launch 실행 시 파일을 찾지 못합니다. 강의할 때 이 부분에서 수강생들이 자주 막힙니다.

4) package.xml 확인

package.xml에는 최소한 다음이 있어야 합니다.

<?xml version="1.0"?>
<package format="3">
<name>mobile_robot_gazebo</name>
<version>0.0.0</version>
<description>Gazebo world package for mobile robot lecture</description>
<maintainer email="user@example.com">user</maintainer>
<license>Apache-2.0</license>

<buildtool_depend>ament_cmake</buildtool_depend>

<exec_depend>launch</exec_depend>
<exec_depend>launch_ros</exec_depend>
<exec_depend>gazebo_ros</exec_depend>

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

<build_type>ament_cmake</build_type>가 빠지면 ROS 2 패키지 인식이나 빌드/런치 과정에서 문제가 생길 수 있습니다.

5) ROS 2 Launch 파일로 Gazebo 실행하기

Gazebo Sim은 ros_gz_sim 패키지의 launch 파일을 통해 실행할 수 있습니다. 공식 문서에서는 gz_sim.launch.py를 사용해 Gazebo 서버와 GUI를 함께 실행할 수 있고, gz_server.launch.py로 서버만 실행할 수 있다고 설명합니다.

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

touch launch/mobile_world.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_ros.substitutions import FindPackageShare


def generate_launch_description():
pkg_mobile_robot_gazebo = get_package_share_directory('mobile_robot_gazebo')

world_file = os.path.join(
pkg_mobile_robot_gazebo,
'worlds',
'basic_mobile_world.sdf'
)

gz_sim_launch = os.path.join(
get_package_share_directory('gazebo_ros'),
'launch',
'gazebo.launch.py'
)

return LaunchDescription([
IncludeLaunchDescription(
PythonLaunchDescriptionSource(gz_sim_launch),
launch_arguments={
'world': world_file
}.items()
)
])

5) 빌드 및 실행

cd ~/gazebo_ws
colcon build
source install/setup.bash

실행:

ros2 launch mobile_robot_gazebo mobile_world.launch.py

9. 벽이 있는 모바일 로봇 주행 공간 만들기

모바일 로봇 강의에서는 단순 박스보다 벽 구조가 중요합니다. SLAM, Nav2, 라이다 장애물 인식 실습에 바로 연결되기 때문입니다.

아래 코드를 </world> 앞에 추가합니다.

<model name="wall_front">
  <static>true</static>
  <pose>0 5 1 0 0 0</pose>
  <link name="link">
    <collision name="collision">
      <geometry>
        <box>
          <size>10 0.2 2</size>
        </box>
      </geometry>
    </collision>
    <visual name="visual">
      <geometry>
        <box>
          <size>10 0.2 2</size>
        </box>
      </geometry>
      <material>
        <ambient>0.7 0.7 0.7 1</ambient>
        <diffuse>0.7 0.7 0.7 1</diffuse>
      </material>
    </visual>
  </link>
</model>

<model name="wall_back">
  <static>true</static>
  <pose>0 -5 1 0 0 0</pose>
  <link name="link">
    <collision name="collision">
      <geometry>
        <box>
          <size>10 0.2 2</size>
        </box>
      </geometry>
    </collision>
    <visual name="visual">
      <geometry>
        <box>
          <size>10 0.2 2</size>
        </box>
      </geometry>
      <material>
        <ambient>0.7 0.7 0.7 1</ambient>
        <diffuse>0.7 0.7 0.7 1</diffuse>
      </material>
    </visual>
  </link>
</model>

<model name="wall_left">
  <static>true</static>
  <pose>-5 0 1 0 0 0</pose>
  <link name="link">
    <collision name="collision">
      <geometry>
        <box>
          <size>0.2 10 2</size>
        </box>
      </geometry>
    </collision>
    <visual name="visual">
      <geometry>
        <box>
          <size>0.2 10 2</size>
        </box>
      </geometry>
      <material>
        <ambient>0.7 0.7 0.7 1</ambient>
        <diffuse>0.7 0.7 0.7 1</diffuse>
      </material>
    </visual>
  </link>
</model>

<model name="wall_right">
  <static>true</static>
  <pose>5 0 1 0 0 0</pose>
  <link name="link">
    <collision name="collision">
      <geometry>
        <box>
          <size>0.2 10 2</size>
        </box>
      </geometry>
    </collision>
    <visual name="visual">
      <geometry>
        <box>
          <size>0.2 10 2</size>
        </box>
      </geometry>
      <material>
        <ambient>0.7 0.7 0.7 1</ambient>
        <diffuse>0.7 0.7 0.7 1</diffuse>
      </material>
    </visual>
  </link>
</model>
전체 공간 구조

네 개의 벽은 월드 좌표계의 원점을 중심으로 다음 위치에 배치됩니다.

                 Y축 +
                   ↑
       wall_front: y = 5
        ┌──────┐
        │                 │
        │                 │
X축 - │      원점      │   X축 +
        │     (0, 0)      │
        │                 │
        └──────┘
        wall_back: y = -5
                   ↓
                 Y축 -

왼쪽 벽과 오른쪽 벽은 각각 x = -5, x = 5 위치에 배치됩니다.

결과적으로 중심이 (0, 0)인 약 10m × 10m 크기의 폐쇄된 공간이 만들어집니다.

SDF 모델의 기본 구조

각 벽은 다음과 같은 공통 구조를 사용합니다.

<model name="모델 이름">
  <static>true</static>
  <pose>위치와 회전</pose>

  <link name="link">
    <collision name="collision">
      충돌 형상
    </collision>

    <visual name="visual">
      화면에 표시할 형상
    </visual>
  </link>
</model>

model은 Gazebo 월드 안에 배치되는 하나의 독립적인 객체를 의미합니다.

이번 코드에서는 각 벽을 별도의 모델로 정의하므로, 네 개의 벽은 서로 독립된 모델입니다.

앞쪽 벽: wall_front
<model name="wall_front">
  <static>true</static>
  <pose>0 5 1 0 0 0</pose>

앞쪽 벽의 중심 위치는 다음과 같습니다.

x = 0
y = 5
z = 1

따라서 월드 원점에서 Y축 양의 방향으로 5m 떨어진 위치에 배치됩니다.

벽의 크기는 다음과 같습니다.

<size>10 0.2 2</size>

박스 크기의 순서는 다음과 같습니다.

X축 길이, Y축 길이, Z축 높이

따라서 앞쪽 벽의 실제 크기는 다음과 같습니다.

  • 가로 길이: 10m
  • 두께: 0.2m
  • 높이: 2m

벽 중심의 Z 좌표가 1이고 벽 높이가 2이므로, 벽의 아래쪽은 z = 0, 위쪽은 z = 2가 됩니다.

즉, 벽이 바닥 위에 정확하게 놓입니다.

뒤쪽 벽: wall_back
<model name="wall_back">
  <static>true</static>
  <pose>0 -5 1 0 0 0</pose>

뒤쪽 벽은 Y축 음의 방향으로 5m 떨어진 위치에 배치됩니다.

x = 0
y = -5
z = 1

벽의 크기는 앞쪽 벽과 같습니다.

<size>10 0.2 2</size>

따라서 앞쪽 벽과 뒤쪽 벽은 서로 평행하며, 약 10m 간격으로 배치됩니다.

왼쪽 벽: wall_left
<model name="wall_left">
  <static>true</static>
  <pose>-5 0 1 0 0 0</pose>

왼쪽 벽은 X축 음의 방향으로 5m 떨어진 위치에 배치됩니다.

x = -5
y = 0
z = 1

왼쪽 벽의 크기는 다음과 같습니다.

<size>0.2 10 2</size>

앞쪽 벽과 비교하면 X축과 Y축 크기가 서로 바뀌어 있습니다.

  • X축 두께: 0.2m
  • Y축 길이: 10m
  • Z축 높이: 2m

이렇게 하면 벽이 Y축 방향으로 길게 배치됩니다.

오른쪽 벽: wall_right
<model name="wall_right">
  <static>true</static>
  <pose>5 0 1 0 0 0</pose>

오른쪽 벽은 X축 양의 방향으로 5m 떨어진 위치에 배치됩니다.

x = 5
y = 0
z = 1

크기는 왼쪽 벽과 동일합니다.

<size>0.2 10 2</size>

왼쪽 벽과 오른쪽 벽이 서로 마주 보면서 공간의 양쪽 경계를 구성합니다.

<link> 태그의 역할
<link name="link">

link는 모델을 구성하는 물리적인 본체입니다.

로봇 모델에서는 하나의 모델에 여러 개의 링크가 포함될 수 있습니다. 예를 들어 로봇 몸체, 바퀴, 로봇 팔의 각 관절 구조가 각각 별도의 링크로 정의됩니다.

이번 벽 모델은 단순한 하나의 박스로 구성되므로 각 모델마다 하나의 링크만 사용합니다.

충돌 영역을 정의하는 <collision>
<collision name="collision">
  <geometry>
    <box>
      <size>10 0.2 2</size>
    </box>
  </geometry>
</collision>

collision은 물리 엔진에서 사용하는 충돌 형상을 정의합니다.

로봇이 벽에 접근했을 때 더 이상 통과하지 못하고 충돌하게 되는 이유가 바로 이 영역 때문입니다.

collision을 생략하면 화면에는 벽이 보이더라도 로봇이나 다른 물체가 벽을 그대로 통과할 수 있습니다.

이번 예제에서는 실제 벽 크기와 동일한 박스를 충돌 영역으로 사용하고 있습니다.

화면 표시를 담당하는 <visual>
<visual name="visual">
  <geometry>
    <box>
      <size>10 0.2 2</size>
    </box>
  </geometry>
</visual>

visual은 Gazebo 화면에 표시되는 외형을 정의합니다.

collisionvisual은 역할이 서로 다릅니다.

  • collision: 물리 충돌 계산용
  • visual: 화면 렌더링용

이번 코드에서는 두 형상의 크기를 동일하게 설정했기 때문에, 화면에 보이는 벽과 실제 충돌 영역이 일치합니다.

복잡한 메시 모델을 사용할 때는 성능을 위해 visual에는 정밀한 메시를 사용하고, collision에는 단순한 박스나 원통을 사용하는 경우도 많습니다.

벽의 색상 설정

각 벽에는 다음과 같은 재질 설정이 들어 있습니다.

<material>
  <ambient>0.7 0.7 0.7 1</ambient>
  <diffuse>0.7 0.7 0.7 1</diffuse>
</material>

색상값의 순서는 RGBA입니다.

Red Green Blue Alpha

현재 값은 다음과 같습니다.

0.7 0.7 0.7 1

빨간색, 초록색, 파란색 값이 모두 같기 때문에 회색으로 표시됩니다.

마지막 값 1은 알파값으로, 완전히 불투명하다는 의미입니다.

ambient
<ambient>0.7 0.7 0.7 1</ambient>

주변광에 의해 표현되는 기본 색상입니다.

diffuse
<diffuse>0.7 0.7 0.7 1</diffuse>

광원이 물체 표면에 직접 비칠 때 나타나는 색상입니다.

ambientdiffuse를 동일하게 설정하면 조명 환경이 바뀌더라도 비교적 일정한 회색 벽으로 표현할 수 있습니다.

만들어지는 공간의 실제 크기

벽 중심은 각각 x = ±5, y = ±5에 있고, 벽 두께는 0.2m입니다.

따라서 벽의 중심선을 기준으로 한 전체 크기는 10m × 10m입니다.

다만 벽 안쪽 면을 기준으로 계산하면 실제 내부 공간은 약간 작습니다.

예를 들어 오른쪽 벽의 중심이 x = 5이고 두께가 0.2m이면, 벽의 안쪽 면은 대략 x = 4.9에 위치합니다.

왼쪽 벽의 안쪽 면은 x = -4.9이므로 내부 폭은 약 9.8m입니다.

내부 폭 = 4.9 - (-4.9)
         = 9.8m

Y축 방향도 동일하므로 실제 내부 공간은 약 9.8m × 9.8m입니다.

모서리 부분의 겹침

각 벽의 길이가 10m이고 중심이 정확히 ±5m에 배치되어 있으므로 네 모서리에서는 벽의 두께만큼 형상이 일부 겹칩니다.

정적인 벽 모델에서는 일반적으로 큰 문제가 발생하지 않습니다.

오히려 모서리에 틈이 생기지 않기 때문에 이동 로봇이나 드론이 벽 사이로 빠져나가는 현상을 방지할 수 있습니다.

다만 물리 시뮬레이션을 매우 정밀하게 구성해야 한다면 벽 길이와 위치를 조정하여 겹침을 제거할 수도 있습니다.

다시 실행합니다.

colcon build --packages-select mobile_robot_gazebo
source install/setup.bash
ros2 launch mobile_robot_gazebo mobile_world.launch.py

이제 10m × 10m 크기의 사각 실내 공간이 만들어집니다.

10. 원통형 장애물 추가하기

라이다 실습에서는 원통 장애물이 유용합니다.

<model name="cylinder_obstacle">
  <static>true</static>
  <pose>0 2 0.5 0 0 0</pose>
  <link name="link">
    <collision name="collision">
      <geometry>
        <cylinder>
          <radius>0.3</radius>
          <length>1.0</length>
        </cylinder>
      </geometry>
    </collision>

    <visual name="visual">
      <geometry>
        <cylinder>
          <radius>0.3</radius>
          <length>1.0</length>
        </cylinder>
      </geometry>
      <material>
        <ambient>0.1 0.8 0.1 1</ambient>
        <diffuse>0.1 0.8 0.1 1</diffuse>
      </material>
    </visual>
  </link>
</model>

이 장애물은 다음 조건을 가집니다.

속성값

위치x=0, y=2
반지름0.3m
높이1.0m
고정 여부static true

라이다가 원통을 감지하면 scan 데이터에서 곡면 형태가 나타나므로, 단순 박스 장애물보다 센서 실습에 좋습니다.

Leave a Comment