검증된 로봇으로 시작한다는 것
직접 만든 로봇 모델을 Gazebo에 띄우는 일은 꽤 매력적이다. 바퀴 지름을 정하고, 센서 위치를 잡고, 링크와 조인트를 연결하다 보면 정말 내 로봇이 하나씩 조립되는 느낌이 든다. 하지만 자율주행 로봇을 처음 배우는 단계에서는 이 즐거움이 곧 함정이 되기도 한다. 로봇이 움직이지 않을 때, 문제가 ROS2 통신에 있는지, URDF에 있는지, Gazebo 플러그인에 있는지, TF에 있는지 한 번에 구분하기 어렵기 때문이다.
그래서 이번 장에서는 직접 만든 로봇 대신 TurtleBot 계열 시뮬레이션으로 시작한다. TurtleBot은 ROS 학습과 연구에서 오랫동안 사용되어 온 이동 로봇 플랫폼이다. 특히 TurtleBot3는 ROS2, Gazebo, RViz, SLAM, Nav2 실습을 연결하기 좋은 표준 예제 환경을 제공한다. TurtleBot3 공식 매뉴얼에서도 시뮬레이션을 실제 로봇 없이 개발할 수 있는 환경으로 설명하며, SLAM이나 Navigation처럼 센서가 필요한 실습에는 Gazebo 기반 시뮬레이션이 적합하다고 안내한다.1
이 말은 단순히 "예제가 잘 되어 있다"는 뜻이 아니다. TurtleBot3 시뮬레이션을 한 번 정상적으로 실행해 두면, 이후 장에서 다룰 SLAM과 Nav2 실습의 기준점이 생긴다. 로봇이 움직이지 않을 때 "내 코드가 문제인지, 환경이 문제인지" 비교할 수 있는 기준 상태가 생기는 것이다.
이번 장의 목표는 화려한 자율주행이 아니다. 먼저 로봇을 띄우고, 움직이고, 센서를 보고, 좌표계를 확인한다. 이 단순한 흐름이 앞으로의 모든 자율주행 실습을 떠받치는 바닥이 된다.

TurtleBot3 시뮬레이션의 기본 구성
TurtleBot3 시뮬레이션을 실행하면 겉으로는 Gazebo 창에 작은 원형 로봇 하나가 나타난다. 하지만 그 뒤에서는 여러 ROS2 구성 요소가 동시에 움직인다. Gazebo는 로봇의 물리 세계를 계산하고, TurtleBot3 모델은 바퀴와 센서를 가진 로봇의 형태를 제공한다. ROS2 노드들은 속도 명령, 오도메트리, LiDAR 데이터, TF, 조인트 상태를 토픽으로 주고받는다.
이 장에서 주로 사용할 패키지는 다음과 같다.
turtlebot3_gazebo: TurtleBot3 모델과 Gazebo 월드를 실행한다.turtlebot3_teleop: 키보드 입력으로 TurtleBot3에 속도 명령을 보낸다.turtlebot3_bringup: RViz 설정과 로봇 표시 구성을 실행할 때 사용한다.
ROS-Industrial의 ROS2 TurtleBot 튜토리얼에서도 TurtleBot3 Gazebo 시뮬레이션, 키보드 제어, 실제 TurtleBot bringup 흐름을 이러한 패키지 중심으로 소개한다.2
여기서 중요한 것은 패키지 이름을 외우는 것이 아니라 역할을 이해하는 것이다. turtlebot3_gazebo는 가상 세계를 만들고, turtlebot3_teleop은 사람이 직접 조작할 수 있는 입력 장치를 제공하며, turtlebot3_bringup은 실제 로봇 또는 시각화 환경을 실행할 때 필요한 구성을 묶어 준다.
우리는 이 구조를 앞으로 직접 만든 이동 로봇 패키지에도 재사용하게 된다. 이름은 달라질 수 있다. 예를 들어 my_robot_gazebo, my_robot_description, my_robot_bringup, my_robot_navigation처럼 바뀔 수 있다. 그러나 역할은 크게 달라지지 않는다. 시뮬레이션, 모델, 실행 묶음, 자율주행 설정을 나누어 관리하는 방식은 그대로 이어진다.
실행 전에 기준 환경을 맞추기
터미널을 열기 전에 먼저 한 가지를 정해야 한다. 이번 장에서는 TurtleBot3 모델을 하나로 고정한다. TurtleBot3에는 burger, waffle, waffle_pi 같은 모델이 있다. 모델마다 크기, 센서 구성, 최대 속도가 조금씩 다르다. 실습이 흔들리지 않도록 처음에는 가장 가벼운 burger 모델로 시작하자.
터미널에서 ROS2 환경을 불러온다.
source /opt/ros/$ROS_DISTRO/setup.bash만약 TurtleBot3 패키지를 별도의 워크스페이스에서 소스 빌드했다면, 해당 워크스페이스의 install/setup.bash도 함께 불러와야 한다.
cd ~/turtlebot3_ws
source install/setup.bash패키지가 설치되어 있는지 확인한다.
ros2 pkg list | grep turtlebot3아무 결과도 나오지 않는다면 TurtleBot3 관련 패키지를 설치해야 한다. 배포판에 따라 패키지 제공 범위가 조금씩 다를 수 있으므로, 먼저 사용 중인 배포판 이름을 확인한다.
echo $ROS_DISTRODebian 패키지로 설치할 수 있는 환경이라면 보통 다음과 같은 방식으로 설치한다.
sudo apt update
sudo apt install ros-$ROS_DISTRO-turtlebot3*또는 TurtleBot3 시뮬레이션 저장소를 워크스페이스에 받아 빌드할 수도 있다. TurtleBot3 공식 매뉴얼은 ROS2 Humble과 Jazzy 환경에서 turtlebot3_simulations 저장소를 받아 빌드한 뒤 Gazebo 시뮬레이션을 실행하는 절차를 안내한다.1
이제 모델 환경 변수를 설정한다.
export TURTLEBOT3_MODEL=burger이 값은 생각보다 중요하다. TurtleBot3 launch 파일과 teleop 노드는 이 환경 변수를 보고 어떤 로봇 모델을 사용할지 판단한다. 터미널을 새로 열 때마다 이 값을 다시 설정해야 한다. 매번 입력하기 귀찮다면 ~/.bashrc에 추가할 수도 있다.
echo 'export TURTLEBOT3_MODEL=burger' >> ~/.bashrc
source ~/.bashrc하지만 처음 실습할 때는 .bashrc에 바로 넣기보다, 터미널마다 직접 입력해 보는 편이 좋다. 환경 변수가 어떤 터미널에 적용되어 있고 어떤 터미널에는 없는지 몸으로 익힐 수 있기 때문이다.
Gazebo에 TurtleBot3 띄우기
이제 Gazebo에서 TurtleBot3를 실행해 보자. 가장 단순한 빈 월드부터 시작한다.
export TURTLEBOT3_MODEL=burger
ros2 launch turtlebot3_gazebo empty_world.launch.py잠시 기다리면 Gazebo 창이 열리고 바닥 위에 TurtleBot3 Burger 모델이 나타난다. 첫 실행에서는 모델 다운로드나 리소스 준비 때문에 시간이 더 걸릴 수 있다. TurtleBot3 매뉴얼도 처음 시뮬레이션을 실행할 때 환경 설정이나 모델 다운로드로 시간이 걸릴 수 있다고 안내한다.1
빈 월드는 실험실의 흰 도화지와 같다. 벽도 없고 장애물도 거의 없다. 로봇이 제대로 움직이는지, /cmd_vel 명령이 전달되는지, 오도메트리가 나오는지 확인하기에 좋다. 하지만 LiDAR로 주변 구조를 관찰하거나 SLAM을 연습하기에는 너무 심심하다. 그래서 TurtleBot3는 몇 가지 기본 월드를 함께 제공한다.
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py또는 집 구조가 있는 월드를 실행할 수도 있다.
ros2 launch turtlebot3_gazebo turtlebot3_house.launch.py공식 매뉴얼은 Empty World, TurtleBot3 World, TurtleBot3 House 같은 시뮬레이션 환경을 제공하며, 한 월드를 실행하기 전에 기존 시뮬레이션을 완전히 종료하라고 안내한다.1 이 조언은 실제로 중요하다. Gazebo 서버가 뒤에 남아 있으면 새 월드가 이상하게 뜨거나, 같은 토픽이 중복되어 디버깅이 어려워질 수 있다.
월드를 바꿀 때는 실행 중인 Gazebo와 launch 터미널을 Ctrl+C로 종료한다. 그래도 문제가 있으면 남아 있는 Gazebo 프로세스를 확인한다.
ps aux | grep gz필요하면 관련 프로세스를 종료한 뒤 다시 실행한다. 시뮬레이션은 가상 세계이지만, 실행 프로세스는 현실의 운영체제 위에서 돌아간다. 가상 로봇이 이상하게 행동할 때는 로봇만 보지 말고, 프로세스와 터미널도 함께 보아야 한다.
첫 번째 관찰: 지금 어떤 ROS2 시스템이 떠 있는가
Gazebo 창에 로봇이 보이면 바로 키보드로 움직이고 싶어진다. 하지만 잠깐만 참자. 로봇이 움직이기 전에 먼저 ROS2 시스템이 어떤 모양으로 떠 있는지 확인해야 한다. 좋은 로봇 개발자는 로봇이 움직이는 장면만 보지 않는다. 그 뒤에서 어떤 노드와 토픽이 숨 쉬고 있는지 본다.
새 터미널을 열고 ROS2 환경과 TurtleBot3 모델을 다시 설정한다.
source /opt/ros/$ROS_DISTRO/setup.bash
export TURTLEBOT3_MODEL=burger노드 목록을 확인한다.
ros2 node list실행 환경과 ROS2 배포판에 따라 이름은 조금 다를 수 있지만, Gazebo 관련 노드와 robot state publisher, 센서 관련 노드들이 보일 것이다. 이제 토픽을 확인한다.
ros2 topic list여기서 특히 눈여겨볼 토픽은 /cmd_vel, /scan, /odom, /tf, /tf_static, /joint_states, /clock이다.
/cmd_vel은 로봇에게 "얼마나 앞으로 가고, 얼마나 회전하라"는 속도 명령을 전달한다. /scan은 LiDAR 거리 데이터다. /odom은 바퀴 움직임과 시뮬레이션 정보를 바탕으로 추정한 로봇의 이동량이다. /tf와 /tf_static은 좌표계 사이의 관계를 전달한다. /joint_states는 바퀴 조인트의 회전 상태를 알려 준다. /clock은 시뮬레이션 시간을 ROS2 시스템에 알려 준다.
이 토픽들은 앞으로도 계속 등장한다. SLAM을 할 때도, Nav2를 실행할 때도, 문제가 생겨 디버깅할 때도 거의 항상 이 이름들을 다시 보게 된다. 그러니 지금부터 친해져야 한다.
토픽 타입까지 함께 확인해 보자.
ros2 topic list -t/cmd_vel은 보통 geometry_msgs/msg/Twist 타입이다. Twist는 선속도와 각속도를 담는 메시지다. 이동 로봇에서는 주로 linear.x로 전진·후진 속도를, angular.z로 좌우 회전 속도를 표현한다. NVIDIA Isaac Sim의 TurtleBot3 ROS2 예제에서도 /cmd_vel 토픽에 geometry_msgs/Twist 메시지를 보내 로봇을 전진시키거나 정지시키는 방식을 설명한다.3
메시지 구조를 직접 확인해 보자.
ros2 interface show geometry_msgs/msg/Twist출력은 대략 다음 구조를 가진다.
Vector3 linear
Vector3 angularlinear와 angular는 각각 x, y, z 값을 가진다. 차동 구동 로봇인 TurtleBot3는 옆으로 미끄러지듯 이동하지 않는다. 그래서 보통 linear.x와 angular.z만 사용한다. 앞으로 가고 싶으면 linear.x를 양수로, 뒤로 가고 싶으면 음수로 둔다. 제자리에서 왼쪽 또는 오른쪽으로 돌고 싶으면 angular.z 값을 바꾼다.
키보드로 로봇 움직이기
이제 로봇에게 직접 명령을 보내자. 새 터미널을 열고 환경을 설정한다.
source /opt/ros/$ROS_DISTRO/setup.bash
export TURTLEBOT3_MODEL=burgerTurtleBot3 전용 키보드 조작 노드를 실행한다.
ros2 run turtlebot3_teleop teleop_keyboard실행에 성공하면 터미널에 조작 안내가 나타난다. TurtleBot3 teleop에서는 보통 w, x, a, d, s, 스페이스 키를 사용한다. w는 전진 속도를 높이고, x는 후진 또는 전진 속도 감소에 사용된다. a와 d는 회전 속도를 조절한다. s나 스페이스 키는 정지 명령이다. ROS-Industrial 튜토리얼도 turtlebot3_teleop의 teleop_keyboard를 실행해 /cmd_vel 명령을 발행하고 TurtleBot을 키보드로 제어하는 흐름을 소개한다.2
여기서 아주 중요한 점이 있다. 키보드 입력은 teleop 터미널에 포커스가 있을 때만 동작한다. Gazebo 창을 클릭한 상태에서 w를 눌러도 로봇이 움직이지 않을 수 있다. teleop을 실행한 터미널을 클릭한 뒤 키를 눌러야 한다. 처음에는 이 단순한 이유로 "왜 로봇이 안 움직이지?" 하고 당황하는 일이 많다.
로봇이 움직이면 Gazebo 화면에서 이동 방향을 관찰하자. w를 눌렀을 때 로봇의 앞쪽이 실제로 앞으로 가는지, a 또는 d를 눌렀을 때 기대한 방향으로 회전하는지 본다. 여기서 "앞쪽"은 단순히 화면에서 위쪽이 아니다. 로봇 모델의 base_link에서 x축 양의 방향이 앞쪽이다. 나중에 RViz에서 이 방향을 함께 확인할 것이다.
키보드 조작이 잘 안 되거나 다른 teleop 패키지를 사용하고 싶다면 범용 패키지인 teleop_twist_keyboard를 사용할 수도 있다.
ros2 run teleop_twist_keyboard teleop_twist_keyboard이 패키지는 일반적으로 /cmd_vel에 geometry_msgs/msg/Twist를 발행한다. 키 배열은 TurtleBot3 전용 teleop과 다를 수 있다. 보통 i, ,, j, l, k 등을 사용한다. 핵심은 어떤 키를 누르느냐가 아니라, 최종적으로 /cmd_vel에 Twist 메시지가 발행되고 로봇이 그 메시지를 받아 움직인다는 점이다.
/cmd_vel을 직접 발행해 보기
teleop 노드는 사람이 키보드로 명령을 입력하면 /cmd_vel 메시지를 대신 만들어 준다. 그러면 이번에는 사람이 직접 메시지를 만들어 보내 보자. teleop 터미널을 잠시 종료하고, 새 터미널에서 다음 명령을 실행한다.
ros2 topic pub --once /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}}"로봇이 잠깐 앞으로 움직일 것이다. 계속 발행하려면 --once를 빼거나 발행 주기를 지정할 수 있다.
ros2 topic pub -r 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}}"정지시키려면 0 속도를 보낸다.
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}"제자리 회전도 해 보자.
ros2 topic pub -r 10 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.8}}"이 명령은 linear.x가 0이므로 앞으로 가지 않고, angular.z가 양수이므로 제자리 회전을 만든다. 어느 방향으로 도는지 Gazebo 화면에서 확인하자. 이 순간 우리는 단순히 로봇을 장난감처럼 움직이는 것이 아니다. Nav2의 Controller가 나중에 하게 될 일을 아주 원초적인 형태로 직접 해 보는 것이다. Nav2도 결국 목표 방향으로 가기 위해 /cmd_vel 계열 속도 명령을 만든다.
명령을 보내는 동안 다른 터미널에서 /cmd_vel을 관찰해 보자.
ros2 topic echo /cmd_velteleop을 실행하고 키를 누르면 값이 바뀌는 것을 볼 수 있다. 이렇게 하면 "키보드 입력 → teleop 노드 → /cmd_vel 토픽 → Gazebo 로봇"으로 이어지는 흐름이 눈에 들어온다.
LiDAR 데이터 보기: /scan
이제 로봇의 눈을 보자. TurtleBot3 시뮬레이션에서는 LiDAR 센서 데이터가 /scan 토픽으로 발행된다. LiDAR는 레이저를 주변으로 쏘고 돌아오는 거리를 측정하는 센서다. 2D LiDAR는 로봇 주변을 평면으로 한 바퀴 훑어 장애물까지의 거리를 알려 준다.
토픽 정보를 확인한다.
ros2 topic info /scan메시지 타입은 보통 sensor_msgs/msg/LaserScan이다.
ros2 interface show sensor_msgs/msg/LaserScanLaserScan 메시지에는 각도 범위, 각도 간격, 최소·최대 거리, 그리고 실제 거리 배열인 ranges가 들어 있다. 출력이 많으므로 그대로 echo하면 화면이 빠르게 지나간다.
ros2 topic echo /scan값을 조금 더 실용적으로 보려면 발행 주기를 확인해 보자.
ros2 topic hz /scanLiDAR 데이터가 일정한 주기로 들어온다면 센서 시뮬레이션이 정상 동작하고 있다는 뜻이다. TurtleBot3나 TurtleBot4 같은 이동 로봇 플랫폼에서 /scan은 SLAM과 장애물 회피의 핵심 입력이다. TurtleBot4 사용자 매뉴얼 역시 RPLIDAR의 LaserScan 데이터가 기본적으로 /scan 토픽으로 발행된다고 설명한다.4 모델과 세대는 달라도, ROS2 이동 로봇에서 /scan이 차지하는 역할은 매우 비슷하다.
이제 Gazebo에서 로봇을 벽 가까이 움직여 보자. /scan의 일부 거리 값이 줄어든다. 로봇이 빈 공간을 볼 때와 벽을 가까이 볼 때 데이터가 달라지는지 확인한다. 이 감각이 중요하다. SLAM은 마법이 아니다. 결국 이런 거리 값들이 쌓여 지도가 된다.
오도메트리 보기: /odom
로봇이 움직였다고 해서 로봇 자신이 어디에 있는지 자동으로 아는 것은 아니다. 로봇은 바퀴가 얼마나 돌았는지, 시간이 얼마나 지났는지, 속도가 얼마였는지 등을 바탕으로 자신의 이동량을 추정한다. 이 추정 정보를 오도메트리라고 한다.
TurtleBot3 시뮬레이션에서는 오도메트리가 /odom 토픽으로 발행된다.
ros2 topic info /odom메시지 타입은 보통 nav_msgs/msg/Odometry이다.
ros2 topic echo /odom출력에는 위치 pose와 속도 twist가 들어 있다. pose.pose.position.x, pose.pose.position.y 값은 오도메트리 좌표계에서 로봇이 얼마나 이동했는지 보여 준다. 제자리에서 회전하면 위치 값은 크게 변하지 않고 방향 값이 변한다.
여기서 주의할 점이 있다. 오도메트리는 "정답 위치"가 아니라 "추정 위치"다. 시뮬레이션에서는 물리 계산이 비교적 깨끗하게 이루어지기 때문에 오차가 작아 보일 수 있다. 하지만 실제 로봇에서는 바퀴 미끄러짐, 바닥 재질, 충돌, 센서 노이즈 때문에 오차가 계속 누적된다. 그래서 나중에 SLAM과 AMCL 같은 위치 추정 기술이 필요해진다.
지금은 /odom 값이 로봇의 움직임에 따라 변하는지만 확인하면 된다. w로 전진하면 x 또는 y 위치가 변하고, 회전하면 방향이 변한다. 이 연결이 이해되면 22장에서 오도메트리를 배울 때 훨씬 수월해진다.
TF 보기: 로봇의 뼈대가 맞게 움직이는가
Gazebo에서 로봇이 움직이고 /odom도 변한다면 절반은 성공이다. 하지만 자율주행을 하려면 좌표계가 올바르게 연결되어야 한다. RViz에서 로봇이 이상한 방향으로 보이거나, LiDAR 데이터가 로봇 뒤쪽에서 나오는 것처럼 보이면 SLAM과 Nav2는 쉽게 망가진다.
먼저 TF 토픽이 있는지 확인한다.
ros2 topic list | grep tf보통 /tf와 /tf_static이 보인다. /tf_static은 변하지 않는 좌표 변환, 예를 들어 base_link에서 laser_frame까지의 고정 위치 관계를 담는다. /tf는 시간에 따라 변하는 좌표 변환, 예를 들어 odom에서 base_link까지의 로봇 위치 변화를 담는다.
tf2_echo로 오도메트리 좌표계에서 로봇 본체 좌표계까지의 관계를 확인해 보자.
ros2 run tf2_ros tf2_echo odom base_link로봇을 움직이면 translation과 rotation 값이 변한다. TurtleBot4 시뮬레이션 실습에서도 tf2_echo odom base_link를 사용해 로봇의 현재 위치와 자세를 확인하는 흐름이 소개된다.5 TurtleBot3에서도 원리는 같다. odom은 로봇의 출발점 근처에 놓인 지역 좌표계이고, base_link는 로봇 몸체에 붙어 있는 좌표계다.
이번에는 LiDAR 프레임을 확인한다. 프레임 이름은 배포판과 모델 설정에 따라 base_scan, laser_frame, base_link 하위의 다른 이름일 수 있다. 먼저 전체 TF 트리를 확인하거나 RViz에서 프레임 목록을 볼 수 있다. 명령으로는 다음 도구를 사용할 수 있다.
ros2 run tf2_tools view_frames이 명령은 TF 트리를 파일로 만들어 준다. 파일을 열어 보면 odom → base_footprint → base_link → ... → laser_frame처럼 좌표계가 연결된 구조를 확인할 수 있다. 프레임 이름이 조금 다르더라도 핵심은 같다. LiDAR 프레임이 로봇 본체와 끊어져 있으면 안 된다.
좌표계 확인에서 가장 중요한 기준은 다음 질문이다. 로봇이 Gazebo에서 앞으로 갈 때, RViz의 base_link x축도 앞으로 향하고 있는가? LiDAR 데이터가 로봇의 실제 센서 위치에서 퍼져 나가는가? odom에서 base_link까지의 이동 방향이 Gazebo에서 본 이동 방향과 일치하는가?
이 질문에 "예"라고 답할 수 있어야 한다. 그렇지 않으면 나중에 지도는 비뚤어지고, 로봇은 목표 지점 반대편으로 돌거나, 장애물이 없는 곳을 피하려고 할 수 있다.
RViz에서 로봇과 센서 함께 보기
Gazebo는 물리 세계를 보여 주는 창이다. RViz는 ROS2 데이터 세계를 보여 주는 창이다. 같은 로봇을 보더라도 두 도구가 보는 관점은 다르다. Gazebo는 "로봇이 시뮬레이션 공간에서 어떻게 움직이는가"를 보여 주고, RViz는 "ROS2 토픽과 TF가 어떻게 해석되는가"를 보여 준다.
새 터미널을 열고 RViz를 실행한다.
source /opt/ros/$ROS_DISTRO/setup.bash
export TURTLEBOT3_MODEL=burger
ros2 launch turtlebot3_bringup rviz2.launch.pyTurtleBot3 공식 매뉴얼도 시뮬레이션 실행 중 RViz2로 발행 토픽을 시각화할 수 있으며, turtlebot3_bringup rviz2.launch.py를 사용할 수 있다고 안내한다.1
RViz가 열리면 Fixed Frame을 확인한다. 처음에는 odom으로 두는 것이 좋다. 이후 SLAM을 실행하면 map 프레임이 등장하고, 그때는 Fixed Frame을 map으로 바꿀 수 있다. 지금은 아직 지도 좌표계가 없으므로 odom 기준으로 보는 것이 자연스럽다.
RViz에서 RobotModel이 보이는지 확인한다. 보이지 않는다면 robot_description이 제대로 전달되는지, TF가 연결되어 있는지, Fixed Frame이 올바른지 점검해야 한다. 이전 장에서 RViz를 다룰 때 보았듯이, RViz에서 데이터가 보이지 않을 때는 화면만 만지작거리지 말고 토픽과 프레임부터 확인해야 한다.
LaserScan 표시를 추가해 /scan을 선택한다. 로봇 주변으로 점들이 둥글게 나타난다면 LiDAR 데이터가 정상적으로 RViz에 들어오고 있다는 뜻이다. 로봇을 벽 가까이 이동시키면 점들의 모양이 바뀐다. Gazebo에서 본 벽의 위치와 RViz의 LaserScan 점들이 대략 일치해야 한다.
TF 표시도 추가하자. base_link, odom, LiDAR 프레임이 어떻게 놓여 있는지 볼 수 있다. 바퀴 조인트가 움직이는지 보고 싶다면 RobotModel과 /joint_states 흐름을 함께 확인한다.
ros2 topic echo /joint_states로봇이 움직일 때 바퀴 조인트의 position이나 velocity 값이 변한다면, 시뮬레이션의 바퀴 상태가 ROS2로 전달되고 있다는 뜻이다. 이 값은 로봇 모델 시각화와 상태 추정에서 중요한 역할을 한다.
Gazebo와 RViz가 같은 이야기를 하는지 확인하기
초보자는 Gazebo에서 로봇이 움직이면 성공했다고 생각하기 쉽다. 하지만 ROS2 자율주행에서는 Gazebo와 RViz가 같은 이야기를 하고 있는지 확인해야 한다. Gazebo에서는 로봇이 앞으로 가는데 RViz에서는 옆으로 가는 것처럼 보인다면, 그것은 성공이 아니다. 로봇은 움직이지만 데이터 해석이 틀어진 상태다.
간단한 검증을 해 보자. 먼저 로봇을 빈 공간에 둔다. RViz의 Fixed Frame은 odom으로 둔다. TF 표시에서 base_link 축을 본다. 빨간색 x축이 로봇의 앞쪽을 향해야 한다. 그런 다음 /cmd_vel로 전진 명령을 보낸다.
ros2 topic pub -r 5 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.1, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}"Gazebo에서 로봇이 앞으로 움직이는지 본다. 동시에 RViz에서 base_link가 x축 방향으로 이동하는지 본다. 멈추려면 0 속도를 보낸다.
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}"이번에는 회전 명령을 보낸다.
ros2 topic pub -r 5 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.5}}"Gazebo에서 로봇이 회전하는 방향과 RViz에서 base_link 축이 도는 방향을 비교한다. LaserScan 점들도 로봇 회전에 맞게 해석되어야 한다. 여기서 이상한 점이 보이면 나중에 SLAM을 시작하기 전에 반드시 해결해야 한다.
이 검증은 매우 단순하지만 강력하다. 앞으로 직접 만든 로봇을 Gazebo에 올릴 때도 똑같이 해야 한다. 바퀴가 돈다고 끝이 아니다. 로봇의 앞, 센서의 앞, TF의 앞, 속도 명령의 앞이 모두 같은 방향을 가리켜야 한다.
핵심 토픽을 하나의 흐름으로 묶어 보기
이제 지금까지 본 토픽들을 하나의 이야기로 묶어 보자. 사람이 키보드에서 w를 누른다. teleop 노드는 이 입력을 받아 /cmd_vel에 Twist 메시지를 발행한다. Gazebo의 TurtleBot3 구동 플러그인은 이 속도 명령을 받아 바퀴를 굴린다. 바퀴가 굴러 로봇이 움직이면 Gazebo는 로봇의 위치와 센서 값을 갱신한다. 그 결과 /odom, /joint_states, /scan, /tf가 계속 발행된다. RViz는 이 데이터를 받아 로봇 모델, 센서 점, 좌표계를 화면에 그린다.
이 흐름을 터미널 명령으로 관찰하면 다음과 같다.
ros2 topic echo /cmd_velros2 topic echo /odomros2 topic hz /scanros2 run tf2_ros tf2_echo odom base_linkros2 topic echo /joint_states이 다섯 가지를 볼 수 있으면 이동 로봇 시뮬레이션의 기본 생체 신호를 확인한 셈이다. 병원에서 심박, 혈압, 체온을 보듯이, ROS2 이동 로봇에서는 속도 명령, 오도메트리, 센서, TF, 조인트 상태를 본다.
문제가 생겼을 때도 이 순서대로 보면 된다. 로봇이 움직이지 않는다면 /cmd_vel이 나오는지 본다. /cmd_vel은 나오는데 움직이지 않으면 Gazebo 구동 플러그인이나 토픽 이름, 메시지 타입을 의심한다. 움직이는데 RViz가 이상하면 /tf와 Fixed Frame을 본다. 센서가 안 보이면 /scan 발행 여부와 frame_id를 본다.
이렇게 보면 디버깅은 막연한 감이 아니라 순서 있는 추적이 된다.
TurtleBot3와 TurtleBot4, 그리고 메시지 타입 주의
이 책의 기본 실습은 TurtleBot3를 기준으로 진행한다. TurtleBot3 Gazebo 예제는 ROS2 이동 로봇의 기본 구조를 배우기에 단순하고 안정적이다. 다만 TurtleBot 계열에는 TurtleBot2, TurtleBot4 같은 다른 플랫폼도 있다. 세대와 구동 베이스에 따라 토픽 이름이나 메시지 타입이 다를 수 있다.
예를 들어 TurtleBot3의 기본 속도 명령은 대체로 /cmd_vel에 geometry_msgs/msg/Twist를 보내는 방식이다. 반면 일부 TurtleBot4 또는 최신 구성에서는 /cmd_vel이 TwistStamped를 사용하거나, /cmd_vel_unstamped, /diffdrive_controller/cmd_vel 같은 관련 토픽이 함께 나타날 수 있다. TurtleBot4 시뮬레이션 자료에서도 /cmd_vel에 geometry_msgs/msg/TwistStamped를 발행하거나 teleop_twist_keyboard를 stamped:=true 파라미터와 함께 사용하는 예가 소개된다.5
따라서 로봇이 움직이지 않을 때는 무조건 "teleop이 고장났다"고 생각하면 안 된다. 먼저 토픽 타입을 확인해야 한다.
ros2 topic list -t | grep cmd_vel그리고 teleop 노드가 발행하는 타입과 로봇이 구독하는 타입이 같은지 확인한다.
ros2 topic info /cmd_velROS2에서는 같은 이름의 토픽이라도 메시지 타입이 맞지 않으면 통신이 성립하지 않는다. 이름이 같다고 해서 항상 연결되는 것이 아니다. 이 점은 뒤에서 안전 필터, velocity smoother, Nav2 controller를 연결할 때도 중요해진다.
Remote PC 중심의 실행 흐름 이해하기
TurtleBot3 공식 문서와 여러 튜토리얼에서는 "Remote PC"라는 표현이 자주 나온다. 실제 TurtleBot을 사용할 때 로봇 위의 작은 컴퓨터는 센서와 모터를 직접 제어하고, 개발자는 노트북이나 데스크톱에서 SSH로 접속하거나 같은 ROS2 네트워크에 참여해 명령을 보낸다. ROS-Industrial 튜토리얼도 실제 TurtleBot 컴퓨터와 Remote PC를 구분하고, 시뮬레이션을 먼저 실행해 키보드로 제어해 보라고 안내한다.2
시뮬레이션에서는 물리 로봇이 없으므로 모든 것이 한 컴퓨터에서 실행될 수 있다. 하지만 구조적으로는 실제 로봇과 비슷하게 생각하는 것이 좋다. Gazebo는 "로봇 쪽" 역할을 한다. teleop과 RViz는 "사용자 PC 쪽" 역할을 한다. /cmd_vel은 사용자 쪽에서 로봇 쪽으로 가는 명령이고, /scan, /odom, /tf는 로봇 쪽에서 사용자 쪽으로 오는 상태와 센서 정보다.
이 관점이 익숙해지면 실제 로봇으로 넘어갈 때 덜 낯설다. Gazebo 창이 실제 로봇으로 바뀌고, 로컬 프로세스가 원격 프로세스로 바뀔 뿐, 토픽을 확인하고 TF를 보고 RViz로 시각화하는 습관은 그대로 유지된다.
자주 만나는 문제와 점검 순서
TurtleBot3 시뮬레이션은 잘 준비된 예제지만, 개발 환경은 사람마다 다르다. Ubuntu 버전, ROS2 배포판, Gazebo 버전, 설치 방식, 쉘 설정에 따라 작은 문제가 생길 수 있다. 문제를 만났을 때는 화면을 바라보며 운에 맡기지 말고 순서대로 좁혀 가야 한다.
먼저 Gazebo에 로봇이 보이지 않는다면 TURTLEBOT3_MODEL이 설정되어 있는지 확인한다.
echo $TURTLEBOT3_MODEL아무 값도 나오지 않으면 다시 설정한다.
export TURTLEBOT3_MODEL=burger패키지를 찾지 못한다면 ROS2 환경을 source했는지 확인한다.
source /opt/ros/$ROS_DISTRO/setup.bash
ros2 pkg list | grep turtlebot3_gazebo소스 빌드한 패키지를 사용한다면 워크스페이스 setup 파일도 source해야 한다.
cd ~/turtlebot3_ws
source install/setup.bash로봇은 보이는데 움직이지 않는다면 /cmd_vel이 발행되는지 확인한다.
ros2 topic echo /cmd_vel값이 나오지 않는다면 teleop 터미널에 포커스가 있는지, teleop 노드가 실행 중인지 본다. 값은 나오는데 로봇이 움직이지 않는다면 토픽 타입과 구독자를 확인한다.
ros2 topic info /cmd_velRViz에서 로봇이나 센서가 보이지 않는다면 Fixed Frame, TF, 토픽 발행 여부를 차례대로 확인한다.
ros2 topic echo /tf
ros2 topic echo /scan
ros2 run tf2_ros tf2_echo odom base_link마지막으로 시뮬레이션 시간이 필요한 노드를 실행할 때는 use_sim_time 설정을 기억해야 한다. Gazebo 시뮬레이션은 /clock 토픽을 통해 가상 시간을 제공한다. SLAM이나 Nav2처럼 시간 동기화가 중요한 노드는 시뮬레이션 시간을 사용하도록 설정해야 한다. 이번 장에서는 깊게 다루지 않지만, 이후 SLAM과 Nav2 launch 파일에서 use_sim_time:=true를 자주 보게 될 것이다.
TurtleBot3 패키지 구조를 내 로봇 프로젝트의 기준으로 삼기
TurtleBot3 예제를 실행하는 것만으로는 부족하다. 우리는 이 예제를 "잘 되는 기준 구조"로 읽어야 한다. TurtleBot3는 로봇 모델, Gazebo 시뮬레이션, bringup, teleop, navigation 관련 구성을 패키지 단위로 나누어 관리한다. 이 분리는 실제 프로젝트에서도 매우 중요하다.
직접 이동 로봇을 만든다면 다음과 같은 구조를 생각해 볼 수 있다.
my_robot_description: URDF, xacro, mesh, RViz 표시 설정을 담는다.my_robot_gazebo: Gazebo 월드, spawn launch, 센서 시뮬레이션 설정을 담는다.my_robot_bringup: 실제 로봇 또는 전체 시스템 실행 launch 파일을 담는다.my_robot_navigation: Nav2 파라미터, 지도, behavior tree 설정을 담는다.my_robot_teleop: 키보드, 조이스틱, 원격 조작 설정을 담는다.
물론 작은 프로젝트라면 이 모든 패키지를 처음부터 나눌 필요는 없다. 하지만 역할을 머릿속에서 분리해 두어야 한다. URDF 문제를 navigation 설정 파일에서 해결하려고 하거나, Gazebo 월드 문제를 teleop 코드에서 해결하려고 하면 프로젝트는 금방 뒤엉킨다.
TurtleBot3 시뮬레이션은 단순한 예제가 아니라 좋은 폴더 구조와 실행 흐름을 보여 주는 참고 모델이다. 이 장에서 실행한 launch 파일들, 설정한 환경 변수, 관찰한 토픽들은 앞으로 우리가 만들 로봇 패키지의 설계 기준이 된다.
이번 장의 기준 상태 저장하기
이제 앞으로의 실습을 위해 "정상 상태"를 마음속에 저장하자. 정상 상태란 다음과 같다. Gazebo에서 TurtleBot3가 뜬다. teleop으로 /cmd_vel이 발행된다. 로봇이 전진, 후진, 회전한다. /scan, /odom, /tf, /joint_states가 발행된다. RViz에서 로봇 모델과 LaserScan, TF가 보인다. Gazebo의 이동 방향과 RViz의 좌표계 방향이 일치한다.
이 상태가 확보되면 다음 장에서 가상 센서를 추가하거나, 20장에서 rosbag으로 데이터를 기록하거나, 5부에서 SLAM을 실행할 때 훨씬 자신 있게 나아갈 수 있다. 반대로 이 기준 상태가 흔들리면 뒤의 실습은 계속 불안해진다. 지도 작성이 실패했을 때 SLAM 알고리즘을 의심해야 할지, LiDAR 토픽을 의심해야 할지, TF를 의심해야 할지 알 수 없기 때문이다.
로봇 개발에서 좋은 출발점은 화려한 기능이 아니다. 잘 움직이고, 잘 보이고, 잘 측정되는 기준 상태다. TurtleBot3 시뮬레이션은 그 기준 상태를 빠르게 제공한다. 이제 우리는 가상 로봇 하나를 얻었다. 다음 장에서는 이 로봇에 붙은 센서들을 더 자세히 들여다보고, 카메라, LiDAR, IMU, 엔코더 데이터가 ROS2 시스템 안에서 어떻게 흘러가는지 살펴볼 것이다.
참고
2TurtleBot in ROS 2 — ROS 2 Industrial Workshop
3Driving TurtleBot via ROS2 messages — NVIDIA Isaac Sim Documentation
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.