제7부 센서·인지 파이프라인
7.1 센서 메시지 규약 — REP 103·105·145
7.1.1 이 장에서 배우는 것 / 선수 지식
로봇 소프트웨어에서 가장 비용이 큰 버그는 단위와 좌표 규약을 틀리는 것이다. 컴파일도 되고 실행도 되고 값도 그럴듯한데, 로봇이 잘못된 방향으로 간다. 그리고 원인을 찾기까지 며칠이 걸린다.
ROS 생태계는 이 문제를 REP(ROS Enhancement Proposal) 로 표준화했다. REP 103(단위·좌표), REP 105(프레임), REP 145(IMU)가 그것이며, Nav2·MoveIt 2·robot_localization·tf2가 전부 이 규약을 전제한다. 규약을 어기면 라이브러리가 조용히 틀린 답을 낸다.
이 장은 제7부의 기초를 세운다. 이후 장들(이미지·포인트클라우드·IMU·융합)이 전부 여기 의존한다.
배우는 것:
- REP 103 — 단위·좌표계·회전 표현의 표준.
- REP 105 — 프레임 계층과 각 프레임의 의미(5.1.7 심화).
- REP 145 — IMU 데이터 규약.
- 광학 프레임 — 왜 예외가 있는가.
- 표준 센서 메시지 — 각각의 의미론과 필수 필드.
- 공분산 규약 — 표현 방식과 "모름"을 나타내는 법.
- 위반의 결과 — 무엇이 어떻게 깨지는가.
선수 지식: 5.1(tf·REP 105 개요), 1.2.7(표준 인터페이스), 1.10.6(타임스탬프를 기록하는 지점).
정직: 단위와 공분산 "모름" 표현은 Jazzy에 설치된
sensor_msgs의 메시지 정의를 직접 열어 확인했다(7.1.12) —.msg파일 주석이 규약의 정본이고, 상당수(확인 필요)가 그것으로 해소됐다. 프레임 이름 관행처럼 드라이버에 달린 항목은 여전히 확인이 필요하다(목차 3).
7.1.2 REP 103 — 단위와 좌표
단위는 SI다. 예외 없다.
| 물리량 | 단위 |
|---|---|
| 길이 | 미터(m) |
| 질량 | 킬로그램(kg) |
| 시간 | 초(s) |
| 각도 | 라디안(rad) |
| 주파수 | 헤르츠(Hz) |
| 힘 | 뉴턴(N) |
| 토크 | 뉴턴미터(N·m) |
| 속도 | m/s |
| 각속도 | rad/s |
| 가속도 | m/s² |
| 온도 | 섭씨(℃) — Temperature.msg가 "Degrees Celsius"로 명시(실행 확인) |
함정: 각도가 도(degree)가 아니라 라디안이다. 센서 드라이버가 도를 주면 변환해야 하고, 이것을 빠뜨리면 57배 오차가 난다. 드라이버 코드에서 변환 지점을 명확히 표시하라.
좌표계: 오른손 좌표계.
| 축 | 방향 | 회전(오른손 법칙) |
|---|---|---|
| x | 앞 | roll(굴림) |
| y | 왼쪽 | pitch(끄덕임) |
| z | 위 | yaw(방향) — 반시계가 양 |
"y가 왼쪽"이 중요하다. 자동차 공학이나 항공(NED: North-East-Down)에서는 다른 규약을 쓰므로, 그쪽 배경에서 온 사람이 자주 틀린다.
| 규약 | x | y | z | 쓰는 곳 |
|---|---|---|---|---|
| ROS(ENU 계열) | 앞·동 | 왼쪽·북 | 위 | ROS 전체 |
| NED | 앞·북 | 오른쪽·동 | 아래 | 항공·해양 |
| 비전(광학) | 오른쪽 | 아래 | 앞 | OpenCV·카메라 |
함정: 드론·항공 배경의 코드를 가져오면 NED일 가능성이 높다. z 부호가 반대이므로 고도가 음수로 나오거나 상하가 뒤집힌다. 좌표 변환을 명시적 함수로 만들고 이름에 규약을 적으라(
ned_to_enu()).
지리 좌표: 야외 로봇에서는 ENU(East-North-Up) 를 쓴다. map 프레임의 x가 동쪽, y가 북쪽, z가 위다.
회전 표현:
| 표현 | ROS에서 |
|---|---|
| 쿼터니언 | 메시지의 표준(geometry_msgs/Quaternion) |
| 회전 행렬 | 계산 중간 |
| 오일러각(RPY) | URDF·사람 입력용. 고정축 x-y-z 순 |
함정: 쿼터니언이 정규화되지 않으면 tf2가 거부하거나 비정상적인 결과를 낸다. 직접 만들 때는 정규화를 확인하고,
w=0, x=y=z=0인 영 쿼터니언은 유효하지 않다 — 초기화 시w=1로 두어야 한다. 메시지 구조체의 기본값이 전부 0이므로 이것을 빠뜨리는 실수가 흔하다.
geometry_msgs::msg::Quaternion q; // 전부 0 — 무효!
q.w = 1.0; // 항등 회전으로 초기화
7.1.3 REP 105 — 프레임 계층
5.1.7에서 개요를 봤다. 여기서는 센서 관점으로 확장한다.
| 프레임 | 정의 | 센서 관점 |
|---|---|---|
earth | 지구 고정(ECEF) | GNSS 다중 map 연결 |
map | 세계 고정 | 전역 측위 결과 |
odom | 오도메트리 기준 | 연속 — 지역 인지의 기준 |
base_link | 로봇 본체 | 센서 장착의 기준 |
base_footprint | 지면 투영 | 2D 항법 편의(확인 필요: 선택적 관행) |
| 센서 프레임 | 각 센서 | 데이터의 frame_id |
센서 데이터의 frame_id는 그 센서의 물리적 위치여야 한다.
scan.header.frame_id = "laser_link"; // ✅ 센서 자체
scan.header.frame_id = "base_link"; // ❌ 센서가 아닌 곳
scan.header.frame_id = ""; // ❌ 비어 있음
scan.header.frame_id = "/laser_link"; // ❌ 앞 슬래시(1.1.11)
함정:
frame_id를base_link로 두면 센서 오프셋이 무시된다. 라이다가 로봇 앞 20 cm에 있는데base_link라고 하면, 장애물이 20 cm 가까이 있는 것으로 계산된다. URDF에 위치를 정의했어도frame_id가 틀리면 소용없다.
견해:
base_footprint의 사용 여부는 시스템마다 다르다. Nav2 설정에서 어느 것을 쓰는지 확인하고 일관되게 유지하라. 2D 항법에서는 지면 투영이 편리하지만, 필수는 아니다.
7.1.4 광학 프레임 — 규약의 예외
카메라만 다른 축 규약을 쓴다. 컴퓨터 비전 관례(OpenCV) 때문이다.
| 프레임 | x | y | z |
|---|---|---|---|
camera_link | 앞 | 왼쪽 | 위 |
camera_optical_frame | 오른쪽 | 아래 | 앞 |
둘 사이의 변환은 고정 회전이다:
<!-- URDF에서 (5.3) -->
<joint name="camera_optical_joint" type="fixed">
<origin xyz="0 0 0" rpy="${-pi/2} 0 ${-pi/2}"/>
<parent link="camera_link"/>
<child link="camera_optical_frame"/>
</joint>
어느 쪽을 frame_id로 쓰는가:
| 데이터 | 프레임 |
|---|---|
sensor_msgs/Image | camera_optical_frame |
sensor_msgs/CameraInfo | camera_optical_frame(7.3) |
| 이미지에서 계산한 3D 점 | camera_optical_frame |
| 깊이 카메라 포인트클라우드 | camera_optical_frame(확인 필요: 드라이버마다 다를 수 있다) |
함정: 이것을 혼동하면 결과가 90도씩 회전한다. 5.1.11의 7번에서 예고한 것이며, 실무에서 매우 흔하다. "검출한 물체가 옆에 있다고 나온다"의 전형적 원인이다.
견해: 카메라 드라이버가 두 프레임과 그 사이 변환을 함께 제공하는지 확인하라. 제공하지 않으면 URDF에 직접 추가해야 하며, 회전값을 틀리면 조용한 오차가 된다.
rpy="-1.5708 0 -1.5708"이 표준 값이다.
7.1.5 REP 145 — IMU 규약
IMU는 규약이 특히 까다롭다.
sensor_msgs::msg::Imu imu;
imu.header.frame_id = "imu_link";
imu.header.stamp = measurement_time; // 측정 시각(1.10.6)
// 방향 (쿼터니언) — 없으면 covariance[0] = -1
imu.orientation = q;
imu.orientation_covariance = {...};
// 각속도 (rad/s) — 센서 프레임 기준
imu.angular_velocity.x = wx;
// 선가속도 (m/s²) — ★ 중력 포함
imu.linear_acceleration.z = az;
핵심 규약:
| 항목 | 규약 |
|---|---|
| 데이터 프레임 | IMU 자체 프레임(frame_id), 축은 REP 103 |
| 선가속도 | 중력을 포함한다 — 정지 시 z ≈ +9.81 |
| 각속도 | rad/s, 오른손 법칙 |
| 방향 기준 | ENU 기준(x 동, y 북, z 위)(확인 필요: 센서·드라이버마다 다름) |
| 없는 데이터 | 해당 공분산의 첫 원소를 -1 |
함정: "중력 포함"이 가장 자주 틀린다. 정지한 IMU가 z축 +9.81을 보고해야 하는데, 어떤 센서·드라이버는 중력을 뺀 "선형 가속도"를 준다. 이것을 그대로 쓰면
robot_localization(7.6)이 로봇이 자유낙하 중이라고 판단한다.
// 정지 상태에서 확인 — 가장 빠른 검증
// ros2 topic echo /imu/data --field linear_acceleration
// z ≈ 9.81 이어야 정상 (센서가 수평일 때)
함정: 방향(orientation)의 기준이 센서마다 다르다. 어떤 IMU는 자북 기준, 어떤 것은 전원 인가 시점 기준, 어떤 것은 방향을 전혀 제공하지 않는다. 드라이버 문서를 확인하고, 융합에 쓸 때 무엇을 신뢰할지 명시적으로 결정하라(7.6).
"데이터 없음" 표현:
// 방향을 제공하지 않는 IMU
imu.orientation_covariance[0] = -1.0; // 이 필드를 무시하라는 신호
이 규약을 지키지 않으면 소비자가 쓰레기 값을 신뢰한다. 방향이 0인데 공분산이 정상이면, 융합 노드는 "로봇이 항등 자세"라고 믿는다.
7.1.6 표준 센서 메시지
| 메시지 | 용도 | 핵심 주의 |
|---|---|---|
Image | 이미지 | encoding·optical frame(7.2) |
CompressedImage | 압축 이미지 | format 필드 |
CameraInfo | 카메라 파라미터 | Image와 같은 스탬프·프레임(7.3) |
PointCloud2 | 3D 점 | field 레이아웃(7.4) |
LaserScan | 2D 스캔 | 각도·시간 규약(7.5) |
Imu | 관성 | 중력 포함(7.1.5) |
MagneticField | 자기장 | 테슬라 단위 |
NavSatFix | GNSS | 위경도(도!)·고도 |
Range | 단일 거리 | 시야각 |
JointState | 관절 | 5.5 |
BatteryState | 배터리 | 전압·전류 부호 |
Temperature·FluidPressure | 환경 | 단위 |
TimeReference | 시각 기준 | 시간 동기(7.7) |
함정:
NavSatFix의 위경도는 도(degree) 단위다 — REP 103의 라디안 규칙의 예외다. 지리 좌표는 관례상 도를 쓴다(확인 필요: 메시지 정의 주석이 정본).
함정:
BatteryState의 전류 부호 규약(방전이 음수인지 양수인지)을 확인하라. 메시지 주석에 명시되어 있으며, 틀리면 충전 중인데 방전으로 표시된다.
헤더 규약(모든 stamped 메시지 공통):
| 필드 | 규약 |
|---|---|
stamp | 측정 시각(발행 시각 아님 — 1.10.6) |
frame_id | 센서 프레임(슬래시 없이 — 1.1.11) |
7.1.7 공분산 규약
불확실성을 표현하는 방식이며, 융합에서 결정적이다.
| 메시지 | 공분산 |
|---|---|
Imu | orientation_covariance·angular_velocity_covariance·linear_acceleration_covariance (각 3×3 = 9) |
Odometry | pose.covariance·twist.covariance (각 6×6 = 36) |
PoseWithCovariance | 6×6 |
NavSatFix | position_covariance (3×3) |
행 우선(row-major) 순서로 저장된다.
$$ \Sigma_{6\times6} = \begin{bmatrix} \sigma_{xx} & \sigma_{xy} & \sigma_{xz} & \sigma_{x\phi} & \sigma_{x\theta} & \sigma_{x\psi} \\ \vdots & & & & & \vdots \\ \sigma_{\psi x} & \cdots & & & & \sigma_{\psi\psi} \end{bmatrix} $$
6×6의 순서는 x, y, z, roll(φ), pitch(θ), yaw(ψ) 다.
// 대각 원소 접근
odom.pose.covariance[0] = var_x; // (0,0)
odom.pose.covariance[7] = var_y; // (1,1)
odom.pose.covariance[14] = var_z; // (2,2)
odom.pose.covariance[21] = var_roll; // (3,3)
odom.pose.covariance[28] = var_pitch; // (4,4)
odom.pose.covariance[35] = var_yaw; // (5,5)
"모름"을 표현하는 세 가지 관행:
| 값 | 의미 |
|---|---|
covariance[0] = -1 | 이 데이터 자체가 없다(IMU orientation 등) |
| 전부 0 | 규약상 "공분산을 모른다" — Imu.msg 주석이 명시(실행 확인, 7.1.12.2) |
| 매우 큰 값(예: 1e9) | "이 값을 믿지 말라" — 융합에서 사실상 무시 |
규약과 구현을 나눠서 봐야 한다. 규약은 확정되어 있다 —
Imu.msg가 "A covariance matrix of all zeros will be interpreted as covariance unknown"이라고 명시한다(실행 확인). 문제는 구현이다. 소비자가 그 규약을 확인하지 않고 0을 분산 값으로 그대로 쓰면, 수학적으로 "분산 0 = 완벽하게 정확"이 되어 그 센서를 절대 신뢰한다. 융합이 다른 센서를 전부 무시하게 되는 경로가 이것이며, 7.6.9가 경고하는 바로 그 증상이다.
견해: 그래서 "규약이 0을 모름으로 정했으니 0을 써도 된다"고 생각하면 안 된다. 규약을 지키는 소비자만 안전하고, 안 지키는 소비자에게는 최악의 값이 된다. 발행자는 0을 피하라 — 데이터가 없으면
-1, 불확실하면 큰 값을 넣는 편이 어느 소비자에게도 안전하다.
견해: 공분산을 0으로 두지 말라. 모르면 큰 값(1e6~1e9)을 넣어 "믿지 말라"고 명시하는 편이 안전하다. 실제 값을 아는 것이 최선이지만, 틀린 확신보다 정직한 무지가 낫다.
공분산 값 정하기:
| 방법 | 설명 |
|---|---|
| 센서 사양서 | 노이즈 밀도·바이어스 안정성 |
| 정지 상태 측정 | 분산을 직접 계산 |
| 경험적 튜닝 | 융합 결과를 보며 조정 |
# 정지 상태에서 IMU 분산 측정
ros2 topic echo /imu/data --field angular_velocity.z > /tmp/gyro_z.txt
# → 표준편차를 계산해 공분산으로
7.1.8 위반의 결과
규약 위반은 조용히 틀린 결과를 만든다. 무엇이 어떻게 깨지는지 알면 진단이 빨라진다.
| 위반 | 증상 |
|---|---|
| 각도를 도로 발행 | 로봇이 57배 회전·발산 |
| y축이 오른쪽(NED) | 좌우 반전 — 왼쪽으로 가라니 오른쪽으로 |
| z축이 아래(NED) | 상하 반전·중력 부호 오류 |
| 광학 프레임 혼동 | 결과가 90도 회전 |
| IMU 중력 미포함 | 융합이 자유낙하로 판단 |
frame_id가 base_link | 센서 오프셋 무시 |
stamp가 발행 시각 | tf 변환 오차(1.10.6) |
stamp가 0 | 시간 정합 상실(1.10.6) |
| 공분산 0 | 융합이 그 센서만 신뢰 |
| 공분산 과소 | 동일 |
| 쿼터니언 미정규화 | tf2 거부·비정상적인 회전 |
| 쿼터니언이 전부 0 | 무효 회전 |
견해: 이 표를 진단 도구로 쓰라. "로봇이 반대로 간다"·"결과가 90도 틀어졌다"·"융합이 이상하다" 같은 증상에서 원인 후보가 즉시 좁혀진다. 규약 위반은 코드 버그처럼 보이지 않기 때문에, 이 가능성을 목록에 두는 것 자체가 중요하다.
7.1.9 검증
드라이버를 만들거나 새 센서를 장착할 때 확인할 것들이다.
① 단위·부호 확인:
# IMU 정지 상태 — z ≈ +9.81
ros2 topic echo /imu/data --field linear_acceleration --once
# 로봇을 반시계로 회전 — angular_velocity.z 가 양수여야
ros2 topic echo /imu/data --field angular_velocity.z
# 로봇을 앞으로 이동 — odom position.x 증가
ros2 topic echo /odom --field pose.pose.position
② 프레임 확인:
# frame_id 가 올바른가
ros2 topic echo /scan --field header.frame_id --once
# 그 프레임이 tf에 있는가 (5.1.10)
ros2 run tf2_ros tf2_echo base_link laser_link
③ 시각적 확인 — 가장 확실:
RViz2에서 센서 데이터를 표시하고 로봇을 실제로 움직이며 본다.
| 확인 | 방법 |
|---|---|
| 라이다 방향 | 앞에 벽을 두고 점이 앞(+x)에 출력되는가 |
| 카메라 방향 | 오른쪽 물체가 이미지 오른쪽인가, 3D 투영이 맞는가 |
| IMU | 로봇을 기울이면 같은 방향으로 기우는가 |
| 오도메트리 | 앞으로 밀면 앞으로 가는가 |
견해: "앞에 벽을 두고 확인"이 가장 빠르고 확실한 검증이다. 수식과 문서로 확인하는 것보다 RViz2에서 눈으로 보는 것이 오류를 즉시 드러낸다. 새 센서를 장착할 때마다 이 절차를 거치라 — 5분이면 되고, 나중에 며칠을 아낀다.
④ 자동 검사:
# 개념: 규약 검사 노드
def check_imu(msg):
warnings = []
# 중력 포함 여부 (정지 가정)
acc_norm = (msg.linear_acceleration.x**2 +
msg.linear_acceleration.y**2 +
msg.linear_acceleration.z**2) ** 0.5
if abs(acc_norm - 9.81) > 2.0:
warnings.append(f'가속도 크기 {acc_norm:.2f} — 중력 포함 여부 확인')
# 쿼터니언 정규화
q = msg.orientation
qn = (q.x**2 + q.y**2 + q.z**2 + q.w**2) ** 0.5
if msg.orientation_covariance[0] >= 0 and abs(qn - 1.0) > 0.01:
warnings.append(f'쿼터니언 노름 {qn:.4f} — 미정규화')
# 공분산 0
if all(c == 0.0 for c in msg.angular_velocity_covariance):
warnings.append('각속도 공분산이 전부 0 — 융합에서 위험')
# 프레임
if not msg.header.frame_id or msg.header.frame_id.startswith('/'):
warnings.append(f'frame_id 이상: "{msg.header.frame_id}"')
return warnings
견해: 이런 검사 노드를 개발 도구로 만들어 두면 값을 한다. 새 센서·새 드라이버를 추가할 때 한 번 실행해 보면 규약 위반이 즉시 드러난다. 진단 토픽으로 내보내면 상시 감시도 된다(6.9).
7.1.12 .msg 파일이 규약의 정본이다 — 실측
REP 문서를 찾기 전에 메시지 정의 파일을 먼저 열어라. 단위와 "모름" 표현이 주석으로 적혀 있고, 그것이 실제 생태계가 따르는 규약이다. Jazzy의 sensor_msgs를 확인한 결과다(실행 확인).
ros2 interface show sensor_msgs/msg/Imu # 주석까지 함께 나온다
cat /opt/ros/jazzy/share/sensor_msgs/msg/Imu.msg # 원문
7.1.12.1 단위 — 정의 파일에 적힌 그대로
| 메시지·필드 | 단위 | 정의 파일의 표현 |
|---|---|---|
Temperature.temperature | 섭씨 | "Measurement of the Temperature in Degrees Celsius" |
NavSatFix.latitude · longitude | 도(degree) | "Latitude [degrees]. Positive is north of equator" |
NavSatFix.altitude | m | "Altitude [m]. Positive is above the WGS 84 ellipsoid" |
Imu.linear_acceleration | m/s² | "should be in m/s^2 (not in g's)" |
Imu.angular_velocity | rad/s | "rotational velocity should be in rad/sec" |
MagneticField.magnetic_field | Tesla | "field vector in Tesla" |
NavSatFix.altitude의 기준면이 WGS 84 타원체라는 점을 놓치기 쉽다. 지도·고도계가 흔히 쓰는 평균해수면(MSL, 지오이드)과 다르다 — 지역에 따라 수십 미터 차이가 난다. 두 값을 섞어 쓰면 고도가 조용히 어긋난다.
함정: 가속도 단위가
g가 아니라는 것을 정의 파일이 굳이 명시해 두었다("not in g's"). 그만큼 자주 틀린다는 뜻이다. 센서 데이터시트는 대개g단위이므로 드라이버에서 9.80665를 곱해야 한다(7.1.4의 중력 포함 문제와는 별개의 실수다 — 단위 변환과 중력 보정 둘 다 틀릴 수 있다).
7.1.12.2 공분산의 "모름"은 두 가지다
이 장이 (확인 필요)로 남겨 둔 항목이다. Imu.msg의 주석이 정확히 답한다(실행 확인):
| 표현 | 뜻 |
|---|---|
| 공분산 행렬이 전부 0 | "공분산을 모른다" — 값 자체는 유효하되 불확실도를 제공하지 않음 |
첫 원소([0])가 -1 | 그 추정값이 전혀 없다 — 필드를 해석하지 말 것 |
즉 0과 -1은 다른 말이다. 0은 "값은 쓰되 불확실도는 네가 정하라"이고, -1은 "이 필드를 보지 마라"다. Imu에는 orientation·angular_velocity·linear_acceleration 세 묶음이 있어 각각 독립적으로 -1을 가질 수 있다 — 방향은 없지만 각속도는 있는 IMU가 흔하다.
정의 파일의 원문은 이렇다:
# A covariance matrix of all zeros will be interpreted as "covariance unknown", and to use the
# ... please set element 0 of the associated covariance matrix to -1
# If you are interpreting this message, please check for a value of -1 in the first element of each
함정: 규약을 아는 것과 소비자가 지키는 것은 다르다. 전부 0을 "모름"으로 읽지 않고 분산 0(=완벽히 정확) 으로 쓰는 코드가 실제로 있고, 그 결과가 7.6.9의 "한 센서만 신뢰하는 융합"이다. 발행자 입장에서 가장 안전한 선택은 0을 전혀 쓰지 않는 것이다(7.1.7).
함정: 소비하는 쪽에서 -1을 확인하라고 정의 파일이 직접 요구한다. 확인 없이 공분산을 그대로 필터에 넣으면 -1이라는 음수 분산이 들어가 추정이 발산한다.
robot_localization(7.6)은 이를 걸러 주지만, 직접 만든 융합 코드는 그렇지 않다.
견해: 발행하는 쪽이라면 -1을 성실히 채워라. "방향을 안 쓰니 단순히 0으로 두자"는 흔한 선택인데, 그러면 소비자는 그것을 유효한 방향 추정(공분산 모름)으로 읽는다. 0과 -1의 차이가 바로 여기서 사고가 된다.
7.1.10 함정 카탈로그
| # | 함정 | 증상 | 대처 |
|---|---|---|---|
| 1 | 각도를 도로 발행 | 57배 오차·발산 | 라디안 변환 |
| 2 | NED 규약 코드 이식 | 좌우·상하 반전 | 명시적 변환 함수 |
| 3 | 광학 프레임 혼동 | 90도 회전된 결과 | _optical_frame 구분 |
| 4 | IMU 중력 미포함 | 융합이 자유낙하 판단 | 정지 시 z≈9.81 확인 |
| 5 | frame_id를 base_link로 | 센서 오프셋 무시 | 센서 프레임 사용 |
| 6 | frame_id에 앞 슬래시 | tf2 거부 | 슬래시 없이(1.1.11) |
| 7 | stamp가 발행 시각·0 | tf 오차·정합 상실 | 측정 시각(1.10.6) |
| 8 | 공분산 전부 0 | 융합이 절대 신뢰 | 큰 값으로 "모름" 표현 |
| 9 | 쿼터니언 전부 0 | 무효 회전 | w=1로 초기화 |
| 10 | 쿼터니언 미정규화 | tf2 거부·이상 회전 | 정규화 확인 |
| 11 | 없는 데이터를 0으로 | 소비자가 신뢰 | covariance[0]=-1 |
| 12 | NavSatFix 단위 혼동 | 위치 오류 | 위경도는 도 |
| 13 | 시각 검증 생략 | 규약 오류가 나중에 발견 | RViz2 + 실제 움직임 |
| 14 | 공분산 -1 미확인 | 음수 분산으로 추정 발산 | 첫 원소 검사 후 사용(7.1.12.2) |
| 15 | 안 쓰는 필드 공분산을 0으로 | 소비자가 유효한 추정으로 오독 | 없으면 -1(7.1.12.2) |
| 16 | 규약대로 공분산 0 발행 | 지키지 않는 소비자가 절대 신뢰로 읽음 | -1 또는 큰 값(7.1.7) |
7.1.11 체크포인트
- REP 103의 SI 단위 표를 재구성할 수 있는가. 각도의 예외는 어디서 발생하는가.
- ROS의 좌표 규약(x·y·z 방향)과 NED·광학 규약의 차이를 표로 말할 수 있는가.
- 쿼터니언을 초기화할 때 주의할 점은 무엇인가.
- 센서 데이터의
frame_id가 무엇이어야 하는가.base_link로 두면 무슨 일이 생기는가. camera_link와camera_optical_frame의 축 차이와, 그 사이의 표준 회전값은?- 어떤 데이터가 광학 프레임을
frame_id로 갖는가. - REP 145에서 선가속도의 핵심 규약은 무엇인가. 정지 상태에서 어떤 값을 기대하는가.
- IMU가 방향을 제공하지 않을 때 어떻게 표현하는가. 그 규약을 안 지키면?
- 6×6 공분산의 원소 순서와 대각 원소의 인덱스를 말할 수 있는가.
- 공분산을 0으로 두면 안 되는 이유와, "모름"의 안전한 표현은?
- 규약 위반의 증상 다섯 가지를 원인과 짝지을 수 있는가.
- 새 센서를 장착할 때 가장 빠르고 확실한 검증 방법은 무엇인가.
.msg주석이 정하는 단위 규약을 네 가지 들 수 있는가. 공분산이 전부 0인 것과 첫 원소가 -1인 것은 어떻게 다른가(7.1.12).
다음 장 7.2은 이 규약 위에서 가장 대용량 데이터 — 이미지 파이프라인을 다룬다. image_transport의 플러그인 구조, cv_bridge의 복사 비용, 그리고 대역폭 설계.