어떤 값을 어느 좌표와 시각으로 얻는지 적는다
같은 로봇에 센서를 달아도 센서 이름만으로 입력이 완성되지 않는다. parent link와 장착 pose, 관측 frame과 취득 시각, 형식/단위·주기, 유효 범위와 노이즈를 함께 정한다. 카메라는 영상의 색/밝기 표본, 깊이 센서는 광학 거리 정보, LiDAR는 정한 광선 방향의 거리 표본을 만든다. IMU는 각속도와 가속도계에 해당하는 관측을 제공하며 방향 필드의 출처/기준도 확인한다.
설정 파일의 센서와 실행 중 센서를 갱신하는 system, Gazebo Transport 출력과 ROS 변환 경로는 다른 구성이다. Harmonic 센서 문서는 IMU system과 렌더링 센서 system의 예를 구분한다. 예를 들어 gpu_lidar 예제는 Sensors system의 렌더 엔진 설정을 사용한다. sensor 태그/GUI 표시만 있고 필요한 시스템이 없으면 기대한 자료를 얻지 못할 수 있다. 기본 활동은 이 구성을 읽는 것이며 GPU/시뮬레이터를 실행했다고 하지 않는다.
SDFormat의 sensor 정의에서 type, pose와 topic, update_rate 같은 항목을 확인한다. 지원 type과 실제 backend/센서별 지원 항목은 버전에 맞춰 검토한다. 이 모듈은 Harmonic을 기준으로 하고 Classic의 plugin/태그를 그대로 복사하지 않는다. 모델 자산/장착/센서·bridge의 정확한 버전도 기록한다.
camera link와 optical frame을 구별한다
로봇의 link frame은 x 전방/y 왼쪽/z 위를 사용할 수 있지만 camera optical frame은 x 오른쪽/y 아래/z 전방 규약을 사용한다. 둘의 이름만 바꿔도 좌표가 회전되는 것은 아니다. bridge의 header frame_id를 덮어쓴다고 표본 값의 기하가 올바르게 변환되었다고 가정하지 않는다. 장착 변환과 렌더링/메시지의 실제 좌표 규약을 확인한다.
왜곡 보정된 pinhole 영상에서 fx/fy는 pixel 단위 초점 거리, cx/cy는 주점, u/v는 관측 pixel 위치다. 이 내부 파라미터를 K라고 부른다. depth Z는 optical z축 성분(m)으로 주어진다고 가정하며 광선 방향의 range와 다르다. 점 좌표는 다음과 같다.
fx=fy=400, cx=320/cy=240, u=400/v=240, Z=2 m면 X=0.4/Y=0이라 p_C=(0.4,0,2) m다. 광선 range는 sqrt(0.4²+2²)≈2.0396 m다. Z와 range를 바꾸어 쓰면 역투영 위치가 달라진다. 해상도/crop·왜곡/실제 K가 바뀌면 이 식에 옛 값을 그대로 쓰지 않는다. 한 점의 깊이가 물체의 전체 자세나 파지 가능성을 의미하지도 않는다.
원시 자료 크기는 형식과 복사/주기를 포함한다
합성 RGB 영상이 640×480, pixel당 uint8 3성분이고 행 padding/압축이 없다고 하자. 한 장은 640×480×3=921,600 byte다. 10 Hz에서 원시 payload는 9,216,000 byte/s=9.216 MB/s다. 여기의 MB는 100만 byte다. float32 한 성분의 같은 크기 depth 영상은 한 장 1,228,800 byte·10 Hz에서 12.288 MB/s다. 둘의 원시 합은 21.504 MB/s다.
실제 메시지 header/행 stride와 전송 오버헤드, 압축/복사·큐/시간창은 별도다. uint16 depth를 쓰면 단위 scale/무효 코드와 성분당 byte가 다르므로 float32 표를 그대로 적용하지 않는다. 색 채널/배열 shape와 실제 소비하는 전처리 출력도 따로 기록한다. 토픽의 이름만 image라고 입력 계약을 확정할 수 없다.
update_rate=10 Hz의 목표 모형 간격은 0.1 s다. M09.01의 고정 physics step이 1 ms라면 100 step에 해당하지만 렌더/발행/전송·소비 시점은 별도다. 설정 주기와 실제 stamp 간격·수신 지연/관측 나이를 나누어 확인한다. pause 중 남은 마지막 영상은 새 취득 표본이 아니다.
LiDAR는 각도와 거리의 짝이다
평면 scan frame을 x 전방/y 왼쪽/z 위로 정한다. θ_i는 +x에서 +y 방향으로 재는 i번째 광선 각도(rad), r_i는 그 광선의 range(m)다. 이번 합성 scan은 같은 순간에 얻은 세 유효 광선이며 motion distortion과 수직 scan은 제외한다.
각도 −π/4, 0, +π/4에 range가 각각 √2, 2, √2 m라면 점은 (1,−1),(2,0),(1,1) m다. 세 값이 모두 2 m인 것처럼 처리하거나 왼쪽/오른쪽 순서를 바꾸면 장애물 형상이 달라진다. 실제 메시지의 각도 시작/증분·개수와 frame, 취득/scan 시간도 확인한다. samples와 resolution 설정의 조합이 실제 배열 길이에 어떻게 반영되는지도 대상 센서 문서/출력으로 검증한다.
최소/최대 range 바깥이나 비유한 값·no-return은 해당 센서/형식의 규칙에 따라 유효 mask와 분리한다. no-return을 무조건 원점의 장애물이나 관측한 빈 공간으로 바꾸지 않는다. 얇은 물체/가림과 반사·렌더링 모형의 한계가 있을 수 있다. 범위/노이즈를 이상적으로 설정했다고 실제 LiDAR의 모든 오류를 재현한 것은 아니다.
IMU의 가속도와 중력·공분산을 해석한다
가속도계의 specific force는 중력과 운동이 연결된 관측이다. 세계 중력이 g_W=(0,0,−9.81) m/s², 센서가 세계와 정렬되어 정지한 이상 예에서는 운동 가속도 0에서 중력을 뺀 z 관측이 +9.81 m/s²가 된다. 센서가 기울면 중력 성분도 다른 축으로 섞인다. 모든 자세에서 z에 9.81을 단순히 빼는 방식은 맞지 않으며 방향/변환과 바이어스·노이즈를 함께 고려한다.
gz-sensors 8의 ImuSensor 구현은 sensor frame으로 변환한 중력의 기여와 노이즈 처리를 보여 준다. 자유낙하 이상 예처럼 운동 가속도가 중력과 같으면 specific force가 0일 수 있으므로 관측 0을 곧바로 정지로 읽지 않는다. 방향 필드도 시뮬레이터 상태에서 만든 값인지 실제 추정기의 결과인지 구분한다. IMU만으로 모든 운동/중력을 완벽히 분리한 것은 아니다.
Jazzy sensor_msgs/Imu 정의는 가속도 m/s²·각속도 rad/s와 공분산 규약을 명시한다. 전부 0인 공분산은 모른다는 뜻으로 해석하고, 특정 추정이 없을 때 첫 공분산 원소 −1의 규약을 확인한다. noise=0이라는 합성 설정과 실제 센서의 완벽한 정확성을 동일시하지 않는다. 노이즈 표준편차와 분산·바이어스와 반복 잡음도 나누어 기록한다.
구성/표본/소비를 한 구간씩 확인한다
센서가 보이지 않으면 먼저 model/link와 sensor type/pose, 필요한 system/렌더 엔진과 실제 Gazebo topic을 조사한다. Gazebo 표본은 있는데 ROS에 없으면 bridge의 형식/방향·이름/QoS를 확인한다. 둘 다 있어도 잘못된 frame/단위·encoding과 오래된 stamp라면 소비 알고리즘의 입력으로 유효하지 않을 수 있다. 설정 하나로 원인을 단정하지 않고 관측된 구간과 미상을 구분한다.
기존 4.1 카메라 모델과 4.4 타임스탬프를 연결해 기하/시간을 확인하자. 아래 계산의 통과는 Gazebo 실행이나 실물 센서 검증의 성과가 아니다.