로봇의 머릿속을 들여다보는 창
로봇을 처음 움직이면 가장 답답한 순간이 찾아온다. 코드는 분명 실행 중이고, 터미널에는 로그가 쏟아지며, 토픽도 발행되고 있다. 그런데 정작 로봇이 무엇을 보고 있는지, 자기 위치를 어디라고 믿고 있는지, 목표 지점까지 어떤 길로 가려 하는지는 눈에 보이지 않는다.
이때 등장하는 도구가 RViz다. RViz는 ROS2 시스템 안에서 오가는 데이터를 3차원 공간에 그려 주는 시각화 도구다. 로봇 모델, TF 좌표계, LiDAR 스캔, 카메라 영상, 오도메트리, 지도, 경로, 디버깅용 마커까지 한 화면에서 확인할 수 있다. 그래서 RViz는 단순한 뷰어가 아니라 로봇 개발자의 현미경이자 블랙박스 리더에 가깝다.
중요한 점은 RViz가 로봇을 대신 움직여 주는 프로그램은 아니라는 것이다. 물리 시뮬레이터도 아니고, 제어기도 아니다. RViz는 이미 ROS2 네트워크에 발행되고 있는 데이터를 구독해서 보여 준다. 즉, RViz에 보이지 않는다는 것은 크게 두 가지 중 하나다. 데이터가 실제로 발행되지 않았거나, 데이터는 발행되지만 RViz가 그것을 올바른 좌표계로 변환하지 못하고 있다는 뜻이다.
RViz를 실행하는 가장 기본 명령은 다음과 같다.
rviz2또는 패키지 실행 형식으로 다음처럼 실행할 수도 있다.
ros2 run rviz2 rviz2RViz 화면을 처음 열면 가운데에는 3D 뷰가 있고, 왼쪽에는 Displays 패널이 있다. Displays에는 현재 화면에 무엇을 그릴지 정하는 항목들이 들어간다. 기본적으로 Global Options와 Grid 정도만 보일 것이다. 여기에 RobotModel, TF, LaserScan, Map, Path 같은 디스플레이를 하나씩 추가하면서 로봇의 내부 상태를 눈으로 확인하게 된다.
RViz는 ROS2의 TF 변환 시스템을 사용해 각 데이터가 도착한 좌표계를 전역 기준 좌표계로 변환한 뒤 화면에 표시한다. 공식 문서에서도 RViz에서 중요한 좌표계로 Fixed Frame과 Target Frame을 설명하며, 들어온 데이터를 고정 기준 좌표계로 변환해 시각화한다는 점을 강조한다.1 이 원리를 이해하면 RViz에서 마주치는 오류의 절반은 이미 해결한 것이나 다름없다.

Fixed Frame: 모든 것이 놓이는 기준점
RViz를 켰을 때 가장 먼저 확인해야 할 항목은 Global Options의 Fixed Frame이다. Fixed Frame은 RViz 화면 전체의 기준 좌표계다. 모든 센서 데이터, 로봇 모델, 지도, 경로는 결국 이 프레임을 기준으로 변환되어 표시된다.
예를 들어 LiDAR 메시지가 laser_frame 기준으로 발행되고, 로봇 본체는 base_link, 오도메트리는 odom, 지도는 map 기준으로 발행된다고 해 보자. RViz의 Fixed Frame을 map으로 설정하면 RViz는 다음과 같은 변환을 찾으려 한다.
map → odom → base_link → laser_frame이 연결이 TF 트리 안에 존재하면 RViz는 laser_frame으로 들어온 LiDAR 점들을 map 기준 위치로 바꿔 화면에 그린다. 반대로 이 연결 중 하나라도 끊어져 있으면 No transform from [laser_frame] to [map] 같은 오류가 뜬다.
이 메시지는 처음 보면 무섭지만, 사실 아주 친절한 힌트다. RViz가 "나는 데이터를 받았지만, 이 데이터를 어디에 놓아야 할지 모르겠다"고 말하는 것이다.
어떤 Fixed Frame을 골라야 할지는 상황에 따라 다르다. 로봇 모델만 확인할 때는 base_link나 odom으로 충분할 수 있다. 오도메트리 기반 주행을 볼 때는 odom이 편하다. SLAM으로 지도를 만들거나 Nav2로 목표 지점까지 이동할 때는 보통 map을 사용한다.
실습 초반에 가장 많이 하는 실수는 아직 map 프레임이 없는데 RViz의 Fixed Frame을 map으로 두는 것이다. SLAM이나 AMCL을 실행하지 않았다면 map 프레임이 아직 없을 수 있다. 이때는 odom 또는 base_link로 바꿔서 현재 단계에서 존재하는 프레임을 기준으로 삼아야 한다.
Fixed Frame을 고를 때는 다음 순서로 생각하면 좋다.
로봇 모델과 센서 장착 위치만 확인한다면
base_link로봇이 움직이는 궤적과 오도메트리를 본다면
odom지도, 위치 추정, 전역 경로, Nav2 결과를 본다면
map
이 기준을 잡아 두면 RViz 화면이 갑자기 사라지거나 빨간 오류가 떠도 당황하지 않는다. 먼저 Fixed Frame을 보고, 그다음 TF 연결을 확인하면 된다.
Displays 패널 읽는 법
RViz에서 Display는 "화면에 그릴 데이터의 종류"다. LaserScan 디스플레이는 sensor_msgs/msg/LaserScan 메시지를 그린다. Map 디스플레이는 nav_msgs/msg/OccupancyGrid 메시지를 그린다. Path 디스플레이는 nav_msgs/msg/Path 메시지를 선으로 보여 준다.
왼쪽 아래의 Add 버튼을 누르면 새 디스플레이를 추가할 수 있다. By display type 탭에서는 원하는 타입을 직접 고를 수 있고, By topic 탭에서는 현재 발행 중인 토픽을 기준으로 RViz가 적절한 디스플레이를 제안한다. 초보자에게는 By topic 방식이 편하다. 예를 들어 /scan 토픽이 보이고 그 옆에 LaserScan이 표시된다면, 해당 토픽의 메시지 타입이 RViz의 LaserScan 디스플레이와 맞는다는 뜻이다.
각 Display에는 Status가 있다. 정상이라면 OK, 문제가 있으면 Warning 또는 Error가 나타난다. 이 상태 메시지를 펼쳐 보면 왜 표시되지 않는지 설명이 나온다. 대부분의 문제는 토픽 이름이 틀렸거나, 메시지 타입이 맞지 않거나, TF 변환을 찾지 못해서 발생한다.
RViz가 지원하는 기본 디스플레이에는 로봇 모델, TF, LiDAR, 포인트 클라우드, 이미지, 오도메트리, 지도, 경로, 포즈, 마커 등 로봇 개발에 필요한 주요 시각화 요소가 포함된다.2 이 장에서는 이동 로봇 개발에서 자주 쓰는 항목을 중심으로 살펴보자.
RobotModel로 URDF와 xacro 확인하기
앞 장들에서 URDF와 xacro로 로봇의 링크와 조인트를 정의했다. 하지만 XML 파일만 보고는 바퀴가 제대로 달렸는지, LiDAR가 앞을 보고 있는지, 카메라가 뒤집히지 않았는지 확신하기 어렵다. RViz의 RobotModel 디스플레이는 이 문제를 해결해 준다.
RobotModel은 보통 robot_description 파라미터에 들어 있는 URDF 모델을 읽어 화면에 그린다. 일반적인 실행 구조에서는 robot_state_publisher가 robot_description을 받아 로봇 링크와 조인트 관계를 TF로 발행하고, RViz가 이를 이용해 모델을 표시한다. URDF의 링크와 조인트 구조는 TF 트리와 밀접하게 연결되며, robot_state_publisher는 로봇 모델을 바탕으로 링크 사이의 변환을 발행하는 역할을 한다.3
모델 확인용 launch 파일은 대략 다음 흐름을 가진다.
ros2 launch my_robot_description display.launch.py실제 패키지 이름은 프로젝트마다 다르다. 중요한 것은 launch 파일 안에서 다음 노드들이 함께 실행되는 구조다.
xacro 또는 URDF 로딩
↓
robot_state_publisher 실행
↓
robot_description 설정
↓
RViz에서 RobotModel 표시RViz에서 Add 버튼을 눌러 RobotModel을 추가한 뒤, 모델이 제대로 보이는지 확인한다. 여기서 단순히 "보인다"로 끝내면 안 된다. 로봇 모델은 나중에 센서 데이터, TF, Nav2 설정의 기준이 되므로 꼼꼼히 봐야 한다.
먼저 base_link가 로봇의 중심에 있는지 확인한다. 이동 로봇에서는 보통 base_link의 x축이 전방, y축이 왼쪽, z축이 위쪽을 향하도록 둔다. RViz의 축 색은 일반적으로 x가 빨간색, y가 초록색, z가 파란색이다. 로봇의 빨간 축이 옆을 향하고 있다면, 나중에 /cmd_vel로 전진 명령을 줬을 때 로봇이 옆으로 가는 이상한 일이 벌어질 수 있다.
다음으로 바퀴 방향을 본다. 바퀴가 좌우에 붙어 있어야 하는데 앞뒤에 붙어 있다면 조인트 원점이나 회전 값이 잘못되었을 가능성이 높다. 바퀴 축이 잘못되어 있으면 시각적으로는 사소해 보여도, 시뮬레이션이나 구동 플러그인에서 큰 문제로 이어질 수 있다.
센서 위치도 반드시 확인해야 한다. LiDAR가 로봇 앞쪽 상단에 있다고 생각했는데 RViz에서는 바닥 아래에 묻혀 있다면, /scan 데이터가 벽이 아니라 바닥이나 로봇 본체를 감지하는 것처럼 보일 수 있다. 카메라가 뒤를 보고 있으면 이미지 처리는 정상인데 장애물 인식 결과가 전혀 엉뚱한 방향에 표시된다.
RobotModel로 확인할 핵심은 모양의 아름다움이 아니라 좌표의 정직함이다. 로봇이 멋지게 보이는지보다, 각 링크가 의도한 위치와 방향에 있는지가 훨씬 중요하다.
TF 디스플레이로 좌표계 나무 보기
RobotModel이 로봇의 몸을 보여 준다면, TF 디스플레이는 로봇의 뼈대를 보여 준다. RViz에서 TF를 추가하면 현재 발행 중인 프레임들이 축과 이름으로 표시된다. base_link, odom, map, camera_link, laser_frame 같은 이름들이 3D 공간에 나타나며, 프레임 사이의 관계를 시각적으로 확인할 수 있다.
TF는 트리 구조여야 한다. 하나의 child frame은 하나의 parent frame만 가져야 한다. 예를 들어 laser_frame은 base_link의 자식이고, base_link는 odom의 자식이며, odom은 map의 자식일 수 있다. TF 시스템은 이렇게 연결된 프레임 사이에서 점이나 자세를 다른 좌표계로 변환할 수 있게 해 준다.4
이동 로봇에서 자주 보는 구조는 다음과 같다.
map
└── odom
└── base_link
├── base_footprint
├── laser_frame
├── camera_link
└── imu_link프로젝트에 따라 base_footprint가 base_link의 부모가 되기도 하고, 카메라 프레임이 camera_link → camera_optical_frame처럼 한 단계 더 나뉘기도 한다. 중요한 것은 이름이 프로젝트 전체에서 일관되어야 한다는 점이다.
RViz의 TF 디스플레이를 켜고 프레임 이름을 표시하면, 잘못된 설정이 꽤 빨리 드러난다. 예를 들어 LiDAR 프레임 이름을 URDF에서는 laser_link라고 했는데 센서 메시지의 frame_id는 laser_frame으로 발행하고 있다면 RViz는 둘을 같은 것으로 볼 수 없다. 사람 눈에는 비슷해 보여도 ROS2에서는 완전히 다른 이름이다.
TF 디버깅에서 자주 만나는 문제는 다음과 같다.
프레임 이름 오타:
base_link와base-link,laser_frame과laser_link는 서로 다르다.끊어진 트리:
map에서base_link까지 이어지는 변환 중 하나가 없다.방향 오류: 센서 프레임의 x축, y축, z축이 실제 장착 방향과 다르다.
중복 부모: 하나의 child frame을 두 노드가 서로 다른 parent에 붙이려 한다.
시간 문제: 메시지 timestamp 시점의 TF를 찾지 못한다.
RViz는 이런 문제를 시각적으로 알려 준다. 프레임이 엉뚱한 곳에 떠 있거나, 로봇에서 한참 떨어져 있거나, 축 방향이 예상과 다르면 TF부터 의심해야 한다.
터미널 도구와 함께 쓰면 더 강력하다. 특정 두 프레임 사이의 변환을 확인하려면 다음 명령을 사용할 수 있다.
ros2 run tf2_ros tf2_echo odom base_linkTF 트리 전체를 파일로 보고 싶다면 tf2_tools의 view_frames를 사용할 수 있다.
ros2 run tf2_tools view_framesRViz는 직관을 주고, 터미널 도구는 수치를 준다. 둘을 함께 써야 좌표계 문제를 빠르게 잡을 수 있다.
LaserScan: LiDAR가 보는 세계
이동 로봇에서 가장 먼저 RViz에 띄워 보고 싶은 센서 데이터는 대개 LiDAR다. 2D LiDAR는 주변 장애물까지의 거리를 여러 각도에서 측정하고, ROS2에서는 보통 sensor_msgs/msg/LaserScan 메시지로 발행한다. 관례적으로 토픽 이름은 /scan을 많이 사용한다. Nav2 문서에서도 SLAM과 AMCL이 레이저 스캔 정보를 사용하며, 기본적인 scan topic이 /scan으로 설정되는 경우가 많다고 설명한다.5
RViz에서 LiDAR를 보려면 Add를 누르고 LaserScan 디스플레이를 추가한다. 그런 다음 Topic을 /scan으로 설정한다. 정상이라면 로봇 주변에 점들이 원호처럼 나타난다. 벽 앞에 로봇이 있으면 벽 모양의 점들이 보이고, 로봇이 회전하면 스캔 패턴도 함께 움직인다.
여기서 다시 Fixed Frame이 중요하다. LaserScan 메시지의 header.frame_id가 laser_frame이라면, RViz는 laser_frame에서 Fixed Frame까지의 TF를 찾아야 한다. Fixed Frame이 base_link이고 base_link → laser_frame 변환이 있으면 잘 보인다. Fixed Frame이 map인데 아직 map → odom 변환이 없다면 보이지 않을 수 있다.
LiDAR가 보이지 않을 때는 다음 순서로 확인하자.
ros2 topic list
ros2 topic info /scan
ros2 topic echo /scan --once
ros2 topic hz /scanros2 topic list에 /scan이 없다면 센서 노드나 시뮬레이터 브리지가 데이터를 발행하지 않는 것이다. /scan은 있지만 RViz에 보이지 않는다면 메시지 타입, frame_id, TF 연결을 확인해야 한다.
실제 화면에서 LiDAR 점들이 로봇 중심에 둥글게 보이면 정상에 가깝다. 그런데 점들이 로봇보다 몇 미터 위에 떠 있거나, 로봇 뒤쪽에만 나타나거나, 회전 방향이 반대로 보인다면 센서 프레임의 위치와 회전이 잘못되었을 수 있다. URDF의 센서 링크 원점, Gazebo나 Isaac Sim의 센서 장착 위치, 브리지에서 설정한 frame_id를 함께 확인해야 한다.
LiDAR는 SLAM과 Nav2의 눈이다. RViz에서 /scan이 정확히 보이지 않는 상태로 SLAM을 실행하면, 지도는 삐뚤어지고 위치 추정은 흔들리며 경로 계획은 엉뚱한 결정을 내린다. 그러므로 SLAM을 시작하기 전, 먼저 RViz에서 LiDAR가 로봇 기준으로 정확히 보이는지 확인하는 습관을 들이자.
PointCloud2와 Image: 더 풍부한 센서 보기
2D LiDAR가 평면 위의 거리 데이터를 준다면, 3D LiDAR나 깊이 카메라는 공간상의 점 구름을 만든다. ROS2에서는 이런 데이터를 주로 sensor_msgs/msg/PointCloud2 메시지로 다룬다. RViz에서는 PointCloud2 디스플레이를 추가해 볼 수 있다.
포인트 클라우드는 정보량이 많다. 테이블, 의자, 벽, 사람처럼 입체적인 구조가 점으로 나타난다. 하지만 데이터가 큰 만큼 RViz 성능에도 영향을 준다. 점 크기, 색상 변환 방식, 표시할 점의 누적 시간 등을 조절하면 훨씬 보기 편해진다. 예를 들어 Size를 너무 크게 하면 장애물이 뭉개져 보이고, 너무 작으면 멀리 있는 물체가 잘 보이지 않는다.
카메라 영상은 Image 또는 Camera 디스플레이로 볼 수 있다. Image 디스플레이는 이미지 토픽 자체를 창에 보여 주는 데 적합하다. Camera 디스플레이는 이미지와 카메라 정보를 함께 사용해 3D 공간과 연결된 시각화를 제공할 수 있다. 일반적으로 RGB 카메라는 /camera/image_raw, 카메라 내부 파라미터는 /camera/camera_info 같은 이름으로 발행된다.
카메라를 볼 때도 frame_id를 확인해야 한다. 이미지 메시지의 frame_id가 camera_link 또는 camera_optical_frame으로 되어 있을 수 있다. 특히 카메라 광학 프레임은 일반 로봇 프레임과 축 방향 규칙이 다를 수 있으므로, 이미지 처리 결과를 3D 공간에 표시할 때는 TF가 정확해야 한다.
RViz에서 센서 데이터를 볼 때는 "데이터가 보이는가?"에서 멈추지 말고 "로봇 기준으로 올바른 위치와 방향에 보이는가?"까지 확인해야 한다. 센서 데이터는 위치가 틀리면 오히려 위험하다. 잘못된 방향의 카메라, 뒤집힌 포인트 클라우드, 높이가 틀어진 LiDAR는 로봇에게 잘못된 세계를 보여 준다.
Odometry: 로봇이 믿고 있는 자기 움직임
오도메트리는 로봇이 자신의 움직임을 추정한 결과다. 바퀴 엔코더, IMU, 시뮬레이터의 ground truth 또는 여러 센서 융합 결과가 오도메트리로 발행될 수 있다. ROS2에서는 보통 nav_msgs/msg/Odometry 메시지를 사용하고, 토픽 이름은 /odom을 많이 쓴다.
RViz에서 Odometry 디스플레이를 추가하고 Topic을 /odom으로 설정하면 로봇의 위치와 방향이 화살표로 표시된다. 설정에 따라 과거 위치를 누적해서 궤적으로 볼 수도 있다. 로봇을 키보드로 전진시켰을 때 화살표가 앞으로 움직이고, 제자리 회전시켰을 때 방향만 바뀌면 기본적으로 정상이다.
오도메트리에서 특히 봐야 할 것은 실제 움직임과 추정 움직임의 방향이다. 로봇을 앞으로 보냈는데 RViz에서는 뒤로 간다면 좌표축 또는 바퀴 속도 부호가 잘못되었을 수 있다. 제자리 회전 방향이 반대로 보이면 각속도 부호나 좌우 바퀴 설정을 확인해야 한다.
또 하나 중요한 것은 odom → base_link TF와 /odom 메시지의 관계다. 많은 시스템에서 오도메트리 노드는 /odom 메시지를 발행하는 동시에 odom에서 base_link로 가는 TF도 발행한다. 둘이 서로 맞지 않으면 RViz에서 로봇 모델과 오도메트리 화살표가 어긋나 보인다.
오도메트리는 완벽한 위치가 아니다. 바퀴가 미끄러지거나 바닥이 고르지 않으면 오차가 누적된다. 하지만 짧은 시간 동안 로봇이 얼마나 움직였는지 알려 주는 중요한 기반이다. 나중에 SLAM과 AMCL은 이 오도메트리를 참고하면서 LiDAR나 지도 정보를 이용해 오차를 보정한다.
Map: SLAM이 그리는 환경
SLAM을 실행하면 RViz에서 가장 극적인 장면을 보게 된다. 검은 화면 위에 회색, 흰색, 검은색 셀이 조금씩 나타나며 공간의 지도가 만들어진다. 이 지도는 보통 nav_msgs/msg/OccupancyGrid 메시지로 /map 토픽에 발행된다.
RViz에서 Map 디스플레이를 추가하고 Topic을 /map으로 설정하면 점유 격자 지도를 볼 수 있다. 점유 격자 지도는 공간을 작은 셀로 나누어 각 칸이 비어 있는지, 막혀 있는지, 아직 모르는지 표현한다. 보통 흰색은 이동 가능한 자유 공간, 검은색은 장애물, 회색은 미확인 영역으로 표시된다.
SLAM 지도를 볼 때는 Fixed Frame을 대개 map으로 설정한다. 이때 TF 트리는 다음과 같이 이어져야 한다.
map → odom → base_link → laser_frameSLAM 노드는 로봇이 지도 안에서 어디에 있는지 추정하며 map → odom 변환을 제공하는 경우가 많다. 오도메트리 노드는 odom → base_link를 제공하고, 로봇 모델은 base_link → laser_frame 같은 센서 변환을 제공한다. 이 연결이 완성되어야 지도 위에 로봇과 LiDAR 데이터가 함께 놓인다.
지도에서 벽이 이중으로 그려지거나, 복도가 휘어지거나, 같은 장소가 겹쳐 보인다면 SLAM이 어려움을 겪고 있다는 신호다. 원인은 다양하다. 오도메트리 오차가 크거나, LiDAR 위치가 잘못되었거나, 로봇을 너무 빠르게 돌렸거나, 특징이 부족한 긴 복도에서 위치 추정이 흔들렸을 수 있다.
이때 RViz는 단순히 결과 지도를 보여 주는 데 그치지 않는다. /scan, TF, Odometry, Map을 동시에 켜면 SLAM이 왜 흔들리는지 추적할 수 있다. LiDAR 점이 벽과 맞지 않는지, 로봇 모델이 지도 위에서 미끄러지는지, map → odom 변환이 갑자기 튀는지 관찰할 수 있다.
Path와 PoseArray: 계획과 추정의 흔적
자율주행이 시작되면 로봇은 단지 현재 위치만 아는 것이 아니라, 앞으로 어디로 갈지도 계산해야 한다. 이때 RViz에서 중요한 디스플레이가 Path다. Path는 nav_msgs/msg/Path 메시지를 선으로 보여 준다. Nav2의 전역 경로나 지역 경로를 확인할 때 자주 사용한다.
예를 들어 목표 지점을 찍었을 때 지도 위에 긴 선이 나타난다면, 그것이 전역 경로일 수 있다. 이 선은 로봇이 목표까지 가기 위해 대략 어느 복도와 어느 코너를 지나갈지 보여 준다. 반면 로봇 가까이에 짧게 계속 변하는 경로는 지역 제어기나 로컬 플래너의 결과일 수 있다.
Path를 볼 때는 경로가 지도와 장애물을 어떻게 피하는지 확인해야 한다. 경로가 벽을 관통한다면 지도, costmap, footprint, inflation 설정을 의심해야 한다. 경로는 정상인데 로봇이 따라가지 못한다면 controller나 속도 제한, 로봇 동역학 설정을 봐야 한다.
PoseArray는 여러 개의 자세를 화살표 구름처럼 보여 준다. AMCL에서는 로봇 위치 후보 입자들을 표시하는 데 PoseArray가 자주 쓰인다. RViz에서 이 입자들이 로봇 주변에 넓게 퍼져 있다면 위치 추정이 불확실하다는 뜻이고, 한 지점에 모여 있다면 로봇이 자신의 위치를 비교적 확신하고 있다는 뜻이다.
초기 위치를 잘못 줬을 때 RViz에서 입자들이 지도 여기저기에 퍼지는 모습을 보면, AMCL이 얼마나 당황하고 있는지 눈으로 느낄 수 있다. 반대로 로봇이 움직이며 LiDAR 스캔과 지도가 잘 맞아떨어지면 입자들이 점점 좁아지고, 로봇 위치가 안정된다.
Marker: 개발자가 직접 남기는 시각적 단서
RViz의 Marker와 MarkerArray는 개발자가 원하는 도형을 직접 화면에 그릴 수 있게 해 준다. 화살표, 구, 큐브, 선, 텍스트 같은 객체를 토픽으로 발행하면 RViz가 이를 표시한다. 복잡한 알고리즘을 디버깅할 때 매우 유용하다.
예를 들어 장애물 감지 노드를 만들었다고 하자. 터미널에 "obstacle detected"라고 출력하는 것만으로는 부족하다. 장애물이 어디에서 감지되었는지 RViz에 빨간 구로 표시하면 훨씬 직관적이다. 사람 추적 알고리즘이라면 추적 중인 사람 위치에 원기둥을 그릴 수 있고, 경로 생성 알고리즘이라면 후보 경로들을 여러 색의 선으로 표시할 수 있다.
마커는 로봇 개발자의 형광펜이다. 보이지 않는 판단 과정을 공간 위에 직접 칠해 보여 준다. 알고리즘이 틀렸을 때도 도움이 되지만, 알고리즘이 맞았다는 것을 팀원에게 설명할 때도 강력하다.
마커를 사용할 때도 header.frame_id가 중요하다. map 기준으로 표시할 물체인지, base_link 기준으로 로봇 앞 1m에 표시할 물체인지 명확히 해야 한다. 같은 좌표 (1.0, 0.0, 0.0)이라도 map 기준이면 지도 원점에서 1m 앞이고, base_link 기준이면 로봇 정면 1m 앞이다.
Nav2 화면 구성 미리 보기
이 책의 뒤쪽에서는 Nav2를 본격적으로 다룬다. 그때 RViz는 단순한 확인 도구가 아니라 자율주행 조종석이 된다. RViz에서 초기 위치를 찍고, 목표 지점을 지정하고, 전역 costmap과 지역 costmap을 보며, 계획 경로와 로봇의 실제 움직임을 비교하게 된다.
Nav2는 지도, 위치 추정, 경로 계획, 장애물 회피를 여러 노드로 나누어 처리한다. 이 과정에서 RViz에는 다양한 중간 결과가 나타난다. Map은 정적 지도나 SLAM 지도를 보여 주고, Path는 planner가 만든 경로를 보여 준다. costmap은 장애물과 위험 구역을 격자로 표시하고, PoseArray는 위치 추정 후보를 보여 줄 수 있다. Nav2의 costmap은 센서 정보와 지도 정보를 이용해 이동 비용을 표현하며, 정적 레이어, 장애물 레이어, inflation 레이어 같은 여러 층을 조합해 만들어진다.6
Nav2를 실행할 때 자주 쓰는 RViz 구성은 대략 다음과 같다.
RobotModel: 지도 위에 로봇 모델 표시TF:map,odom,base_link, 센서 프레임 연결 확인LaserScan또는PointCloud2: 장애물 센서 입력 확인Map: SLAM 또는 저장된 점유 격자 지도 표시Path: 전역 경로와 지역 경로 표시PoseArray: AMCL 입자 또는 후보 자세 표시Marker와MarkerArray: 알고리즘 디버깅 정보 표시
Nav2는 RViz와 함께 쓰기 위한 플러그인도 제공한다. 예를 들어 nav2_rviz_plugins 패키지는 Navigation2용 RViz 플러그인을 제공하는 패키지로 배포되어 있다.7 뒤에서 Nav2를 다룰 때는 이 플러그인을 통해 목표 지점을 주거나 내비게이션 상태를 확인하는 흐름도 만나게 된다.
아직 Nav2를 자세히 몰라도 괜찮다. 지금은 RViz에서 "지도 위에 로봇이 있고, 로봇 주변에 센서 데이터가 있으며, 목표까지의 경로가 선으로 보인다"는 그림에 익숙해지는 것이 중요하다. 이 그림이 자율주행 로봇 시스템의 기본 대시보드가 된다.
RViz 설정 파일 저장하기
RViz 화면을 매번 손으로 구성하는 것은 번거롭다. Fixed Frame을 바꾸고, RobotModel을 추가하고, /scan을 고르고, Map과 Path를 추가하는 일을 매번 반복하면 실수도 늘어난다. 그래서 RViz 설정은 파일로 저장해 두는 것이 좋다.
RViz 상단 메뉴에서 다음 경로로 설정을 저장할 수 있다.
File → Save Config As...보통 패키지 안에 rviz 또는 config 폴더를 만들고 설정 파일을 저장한다.
my_robot_bringup/
├── launch/
│ └── bringup.launch.py
├── config/
│ └── nav2_params.yaml
└── rviz/
└── robot_debug.rviz저장한 설정 파일은 다음처럼 불러올 수 있다.
rviz2 -d ~/ros2_ws/src/my_robot_bringup/rviz/robot_debug.rviz패키지 설치 경로를 이용하면 더 이식성 있게 실행할 수 있다.
rviz2 -d $(ros2 pkg prefix my_robot_bringup)/share/my_robot_bringup/rviz/robot_debug.rvizlaunch 파일에서 RViz를 함께 실행할 수도 있다. Python launch 파일에서는 대략 다음과 같은 형태가 된다.
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
import os
rviz_config = os.path.join(
get_package_share_directory('my_robot_bringup'),
'rviz',
'robot_debug.rviz'
)
rviz_node = Node(
package='rviz2',
executable='rviz2',
name='rviz2',
arguments=['-d', rviz_config],
output='screen'
)이렇게 해 두면 팀원 모두가 같은 디버깅 화면을 열 수 있다. 시뮬레이션을 하든 실제 로봇을 연결하든, 동일한 RViz 구성을 재사용하면 문제를 비교하기 쉬워진다. "내 화면에서는 보이는데 네 화면에서는 안 보인다"는 혼란도 줄어든다.
RViz 설정 파일에는 Displays 목록, 토픽 이름, Fixed Frame, 카메라 시점, 색상, 크기 같은 정보가 들어간다. 따라서 프로젝트에서 자주 쓰는 화면을 목적별로 나누어 저장해 두면 좋다.
model_check.rviz # URDF, TF, RobotModel 확인용
sensor_debug.rviz # LaserScan, PointCloud2, Image 확인용
slam_debug.rviz # Map, TF, Scan, Odometry 확인용
nav2_debug.rviz # Map, Costmap, Path, Goal 확인용좋은 RViz 설정 파일은 좋은 실험 노트와 같다. 같은 상황을 다시 만들 수 있고, 같은 기준으로 문제를 볼 수 있다.
보이지 않을 때의 디버깅 순서
RViz를 쓰다 보면 반드시 "아무것도 안 보여요"라는 순간이 온다. 이때 무작정 RViz를 껐다 켜기보다 순서를 정해 확인하면 빠르게 원인을 좁힐 수 있다.
먼저 RViz의 Fixed Frame을 확인한다. 현재 시스템에 존재하지 않는 프레임을 기준으로 잡고 있으면 대부분의 데이터가 보이지 않는다. map이 없다면 odom이나 base_link로 바꿔 본다.
다음으로 토픽이 실제로 발행되는지 확인한다.
ros2 topic list
ros2 topic info /scan
ros2 topic hz /scan토픽이 없으면 RViz 문제가 아니다. 센서 노드, 시뮬레이터, 브리지, launch 파일을 확인해야 한다. 토픽은 있는데 RViz에 표시되지 않는다면 메시지 타입과 Display 타입이 맞는지 확인한다. LaserScan 디스플레이에 PointCloud2 토픽을 넣을 수는 없다.
그다음 frame_id를 본다.
ros2 topic echo /scan --once출력에서 header.frame_id를 확인한다. 예를 들어 frame_id: laser라고 되어 있는데 TF에는 laser_frame만 있다면 이름을 맞춰야 한다.
마지막으로 TF 연결을 확인한다.
ros2 run tf2_ros tf2_echo base_link laser_frame
ros2 run tf2_ros tf2_echo map base_link여기서 변환을 찾지 못한다면 RViz도 당연히 그릴 수 없다. TF 브로드캐스터, robot_state_publisher, SLAM, AMCL, 오도메트리 노드 중 어느 부분이 해당 변환을 제공해야 하는지 추적해야 한다.
문제가 복잡해 보일수록 RViz는 정직하다. RViz는 없는 데이터를 만들어내지 않고, 끊어진 좌표계를 억지로 이어 붙이지 않는다. 그래서 RViz의 빨간 오류 메시지는 실패가 아니라 길잡이다.
한 화면에 겹쳐 볼 때 진짜 로봇이 보인다
RViz의 진짜 힘은 여러 데이터를 겹쳐 볼 때 드러난다. 로봇 모델만 보면 로봇이 예쁘게 생겼는지 알 수 있다. LiDAR만 보면 점들이 들어오는지 알 수 있다. 지도만 보면 SLAM 결과가 그럴듯한지 알 수 있다. 하지만 이들을 한 화면에 함께 놓으면 전혀 다른 수준의 판단이 가능해진다.
지도 위에 로봇이 있고, 로봇 앞의 LiDAR 점들이 벽과 정확히 겹치며, 오도메트리 궤적이 부드럽게 이어지고, 계획 경로가 장애물을 피해 목표까지 뻗어 있다면 시스템은 건강하다. 반대로 로봇은 지도 위에 있는데 LiDAR 점들이 벽에서 한참 떨어져 있거나, 경로가 벽을 뚫고 지나가거나, TF 축이 로봇과 따로 움직인다면 어딘가 잘못된 것이다.
이 장에서 배운 RViz 사용법은 이후 SLAM과 Nav2를 배울 때 계속 반복된다. SLAM에서는 /scan, TF, /map, /odom을 함께 보며 지도가 왜 틀어지는지 확인한다. Nav2에서는 Map, Costmap, Path, RobotModel, TF를 함께 보며 로봇이 왜 목표까지 가지 못하는지 분석한다.
자율주행 로봇은 많은 노드가 동시에 움직이는 시스템이다. 터미널 로그만으로는 그 복잡한 관계를 한눈에 파악하기 어렵다. RViz는 그 관계를 공간 위에 펼쳐 준다. 로봇이 무엇을 보고, 어디에 있다고 믿고, 어디로 가려 하는지 보여 준다.
이제 우리는 로봇의 몸을 URDF로 정의했고, 좌표계를 TF로 연결했으며, RViz로 그 모든 것을 눈으로 확인할 수 있게 되었다. 다음부터는 이 로봇을 안전한 가상 세계로 데려가 보자. 시뮬레이션 안에서 로봇은 넘어져도 다치지 않고, 벽에 부딪혀도 고장 나지 않는다. 그곳에서 우리는 더 과감하게 실험하고, 더 빠르게 배울 수 있다.
참고
1RViz User Guide — ROS 2 Documentation
2ros2/rviz — ROS 3D Robot Visualizer
3The Transform System (tf2) | Articulated Robotics
4Session 2 — ROS2 Basics documentation
5Mapping and Localization — Nav2 Documentation
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.