[Physical AI W2D1 · 2/6]
RViz의 진짜 핵심은 TF(좌표계 변환)다. map·odom·base_link·laser·camera_link가 어떻게 연결되는지, URDF/RobotModel과 LaserScan·Odometry·Path·PointCloud2·Image·IMU·Marker Display를 정리하고, "데이터가 안 보일 때" 원인을 분리해 잡는 법까지 다룬다.
이 글에서 잡는 개념
TF — 좌표계 사이의 위치·회전 관계, Static vs Dynamic
주요 좌표계:map·odom·base_link·laser·camera_linkURDF / RobotModel Display
센서 Display 총정리: LaserScan·Odometry·Path·PointCloud2·Image·IMU·Marker
RViz vs Gazebo, 그리고 "데이터가 안 보일 때" 디버깅 순서
(1편의 RViz 기본 위에서, 이번엔 좌표계와 각 Display를 깊게 봅니다.)
1. TF — RViz가 "어디에 그릴지" 아는 법
TF(Transform)는 ROS 2에서 좌표계 사이의 위치·회전 관계를 표현하는 기능입니다. 로봇은 좌표계 하나만 쓰지 않습니다 — 본체·바퀴·LiDAR·카메라·IMU·지도·주행 기준이 각각 다른 좌표계를 가집니다.
map
└── odom
└── base_link
├── laser
├── camera_link
└── imu_link
각 좌표계는 따로 노는 게 아니라 서로 연결돼야 합니다. LiDAR가 본체 앞에 달렸다면 laser는 base_link 앞쪽에 위치해야 하고, 로봇이 움직이면 base_link는 odom 안에서 위치가 계속 변합니다. TF가 이 관계를 시간에 따라 관리합니다.

RViz는 TF로 "각 센서 데이터를 화면 어디에 그릴지" 계산합니다. TF가 없으면 토픽이 정상 발행돼도 데이터가 안 보이거나 No transform 오류가 납니다.
Static TF vs Dynamic TF
| 구분 | 의미 | 예 |
|---|---|---|
| Static TF | 시간이 지나도 안 변하는 관계 | base_link → laser, base_link → camera_link (센서 고정 장착) |
| Dynamic TF | 시간에 따라 변하는 관계 | odom → base_link (로봇이 이동) |
odom
└── base_link ← Dynamic (로봇 이동)
├── laser ← Static (고정)
├── camera_link ← Static
└── imu_link ← Static
2. 주요 좌표계의 의미
| 좌표계 | 의미 | 주 사용 |
|---|---|---|
map |
전역 지도 기준 | SLAM·Navigation에서 지도 기반 위치 |
odom |
주행 추정 기준 | 출발 후 상대 이동(단기적으로 부드러움) |
base_link |
로봇 본체 기준 | 전진·회전 방향, 센서 장착 위치 |
laser |
LiDAR 센서 기준 | 거리값이 보통 이 기준으로 발행 |
camera_link |
카메라 기준 | 영상·깊이·점군 데이터 기준 |
💡
odom은 바퀴·IMU 추정이라 누적 오차가 생기지만 단기 이동 추정엔 중요합니다.map은 SLAM/Navigation으로 보정된 전역 기준입니다.
TF Display를 추가하면 각 좌표계가 축(axes) 형태로 화면에 표시됩니다. 로봇이 움직이면 base_link가 odom 기준으로 이동하고 laser는 base_link에 붙어 함께 움직이죠. 다른 Display보다 TF Display를 먼저 확인하세요 — LaserScan·Odometry·Path가 안 보이는 원인이 대개 TF 누락입니다.
3. URDF와 RobotModel Display
URDF(Unified Robot Description Format)는 로봇의 물리 구조를 XML로 표현하는 파일입니다. Link(부품/좌표계 단위: base_link, laser_link…)와 Joint(Link 사이 연결: fixed, revolute, continuous)로 구성됩니다.
RobotModel Display는 URDF 기반으로 로봇 외형을 화면에 그립니다. 보통 RViz가 URDF를 직접 읽기보다, robot_state_publisher 노드가 URDF를 /robot_description으로 제공하고 Joint 상태와 함께 TF를 발행하면, RobotModel Display가 이를 표시합니다.
RobotModel은 단순 그림이 아니라 로봇 구조·좌표계가 올바른지 검증하는 도구입니다 — LiDAR가 앞에 있어야 하는데 뒤에 그려지면 URDF의 joint origin이 잘못된 것입니다.
4. 센서 Display 총정리
RViz가 화면에 그리는 핵심 Display들입니다. 공통적으로 "토픽 존재 + 발행 지속 + 타입 일치 + frame_id 존재 + TF 연결"의 5조건(1편)이 필요합니다.
| Display | 메시지 타입 | 대표 토픽 | 무엇을 보나 |
|---|---|---|---|
| LaserScan | sensor_msgs/msg/LaserScan |
/scan |
2D LiDAR 거리 → 점으로 |
| Odometry | nav_msgs/msg/Odometry |
/odom |
추정 위치·방향 → 화살표/축 |
| Path | nav_msgs/msg/Path |
/path |
이동·계획 경로 → 선 |
| PointCloud2 | sensor_msgs/msg/PointCloud2 |
/points |
3D 점군(LiDAR/Depth) |
| Image | sensor_msgs/msg/Image |
/camera/image_raw |
카메라 영상(별도 창) |
| IMU | sensor_msgs/msg/Imu |
/imu |
자세·각속도·가속도 |
| Marker | visualization_msgs/msg/Marker |
/visualization_marker |
사용자 정의(점·선·화살표·텍스트) |
핵심 디테일 몇 가지:
- LaserScan:
angle_min~angle_max(측정 범위),ranges(각 방향 거리). Display의Style(Points/Flat Squares)·Size·Decay Time조정. 안 보이면 Topic과 TF부터 확인. - Odometry:
header.frame_id=odom,child_frame_id=base_link.Shape(Arrow/Axes),Keep(이전 표시 유지). TF의odom→base_link와 어긋나면 위치가 어색해집니다. - Path:
PoseStamped배열을 선으로. Odometry가 "현재 위치"라면 Path는 "시간에 따른 이동 흐름". - PointCloud2:
Color Transformer(RGB/Intensity/AxisColor/FlatColor)로 색 기준 선택. 3D 환경 인식 확인의 핵심. - Image: 3D View가 아닌 별도 Image 창에 표시.
Transport Hint(raw/compressed). 비전 실습의 출발점. - IMU: 단독 시각화보다 Odometry·SLAM·센서융합과 함께 쓰임(orientation은 보통 quaternion — 5편에서).
- Marker: 정해진 센서 형식이 아니라, 개발자가 디버깅·알고리즘 결과를 임의 도형으로 표시.
5. RViz vs Gazebo — 헷갈리지 말 것
| 구분 | Gazebo | RViz |
|---|---|---|
| 역할 | 물리 시뮬레이션 | ROS 2 데이터 시각화 |
| 물리 엔진 | 사용(중력·충돌·마찰) | 사용 안 함 |
| 센서 | 가상 센서 생성 | 직접 생성 안 함 |
| 토픽 | 발행 | 주로 구독 |
→ Gazebo는 데이터를 만드는 쪽, RViz는 확인하는 쪽입니다(W1D1의 Gazebo /clock을 떠올리세요).
Gazebo 가상 센서 → ROS 2 Bridge/Plugin → /scan 발행 → RViz LaserScan Display → 점 시각화
⚠️ Gazebo+RViz를 함께 쓸 때는 좌표계 일관성이 중요합니다.
Gazebo 모델의 센서 위치와 TF가 어긋나면 RViz에서 센서 점이 엉뚱한 곳에 찍힙니다 — 센서 토픽뿐 아니라 TF·URDF 구조도 확인하세요.
6. 데이터가 안 보일 때 — 디버깅 순서
RViz에서 "안 보임"은 매우 흔하고, 대부분 코드가 아니라 좌표계/설정 문제입니다. 무조건 다음 6단계 순서로 원인을 분리하세요.
1. 토픽이 존재하는가 (ros2 topic list)
2. 메시지가 발행되는가 (ros2 topic echo)
3. 메시지 타입이 Display와 맞는가 (ros2 topic type)
4. 메시지의 header.frame_id는?
5. Fixed Frame은 무엇인가
6. frame_id ↔ Fixed Frame 사이 TF가 있는가
frame_id — 가장 자주 틀리는 곳
frame_id는 그 메시지가 어느 좌표계 기준인지를 나타냅니다. 철자 하나만 달라도 다른 frame으로 취급됩니다.
laser Laser laser_link /laser ← 모두 서로 다른 frame!
토픽 메시지의 frame_id와 TF가 발행하는 frame 이름을 정확히 맞춰야 합니다.
대표 오류 메시지 해석
| 오류 | 의미 |
|---|---|
Fixed Frame [odom] does not exist |
Fixed Frame 좌표계가 TF에 없음 → TF 발행 노드 확인 |
No transform from [laser] to [odom] |
두 좌표계 사이 TF 변환이 없음(토픽은 정상일 수 있음) |
Frame [base_link] does not exist |
메시지 frame_id가 TF에 없음 |
Message Filter dropping message |
TF 변환 시간/대기 문제 |
Topic has no publisher |
그 토픽을 발행하는 노드가 없음 |
예: No transform from [laser] to [odom] → /scan은 잘 나오는데 laser→…→odom TF가 없어 LiDAR 점이 안 보이는 상황입니다.
💡
.rviz설정 파일 — Fixed Frame·Display 목록·Topic·색상·View를.rviz파일로 저장하면,
다음 실행에서 같은 화면을 바로 복원하고 팀과 공유할 수 있습니다(데이터가 아니라 화면 구성을 저장). 3편 실습에서 직접 만듭니다.
2편 정리
- TF = 좌표계 사이 위치·회전 관계.
map→odom→base_link→{laser,camera,imu}트리. Static(고정 장착) vs Dynamic(이동). - TF Display를 가장 먼저 — 안 보이는 원인의 대부분이 TF 누락.
- URDF/RobotModel = 로봇 구조·좌표계 검증(
robot_state_publisher가 TF·/robot_description제공). - 센서 Display(LaserScan·Odometry·Path·PointCloud2·Image·IMU·Marker)는 5조건 공통 + 각자 디테일.
- 안 보이면 6단계 순서(토픽→발행→타입→frame_id→Fixed Frame→TF). frame_id 철자가 단골 범인.
다음 편 예고
개념은 충분히 잡았습니다. 3편에서는 직접 실습합니다 — RViz는 GUI라 순수 터미널론 안 되니, GCP VM에 헤드리스 GUI 스택(Xvfb + VNC + noVNC + Cloudflared) 을 세워 브라우저로 RViz2를 띄우고, Publisher 노드로 LaserScan·Odometry·Path·TF를 발행해 화면에서 확인합니다.
📚 Week2 Day1 전체 목차 (총 6편)
- 1/6 RViz로 로봇을 눈으로 보다 — 시각화 흐름·Fixed Frame·Display
- 2/6 RViz 개념 ② — TF·좌표계·URDF·센서 Display·디버깅 — 이번 글
- 3/6 RViz2 실습 — GCP VM 헤드리스 GUI + Publisher + 시각화
- 4/6 좌표계와 동차 변환 행렬 ① — 좌표계·2D 변환·동차좌표
- 5/6 좌표계와 동차 변환 행렬 ② — 3D·TF·Quaternion·기구학
- 6/6 좌표 변환 실습 — Python 변환 + TF Publisher + RViz
'피지컬AI' 카테고리의 다른 글
| [Physical AI W2D1] 5/6 — 좌표계와 동차 변환 행렬 ②: 3D·TF·Quaternion·기구학 (0) | 2026.06.20 |
|---|---|
| [Physical AI W2D1] 4/6 — 좌표계와 동차 변환 행렬 ①: 좌표계·2D 변환·동차좌표 (0) | 2026.06.20 |
| [Physical AI W2D1] 1/6 — RViz로 로봇을 눈으로 보다: 시각화 흐름·Fixed Frame·Display (0) | 2026.06.20 |
| [Physical AI W1D2] 6/6 — ROS 2 패키지 빌드 마스터: Python·C++·colcon (0) | 2026.06.14 |
| [Physical AI W1D2] 5/6 — ROS 2 서비스·액션 구현: 요청/응답과 목표/피드백 (0) | 2026.06.14 |