도구·컨테이너·메시지를 구분한다
rosbag2는 ROS 2 통신을 기록/재생하는 도구이고 MCAP은 시각이 붙은 메시지들을 저장할 수 있는 컨테이너 형식이다. 컨테이너는 여러 자료와 그 설명을 담는 바깥 구조다. rosbag2가 선택한 storage plugin을 통해 MCAP으로 기록할 수 있지만 rosbag2와 MCAP이 같은 단어는 아니다. MCAP 자체는 ROS에만 한정된 형식도 아니다.
직렬화는 메시지의 필드/값을 정한 규칙으로 바이트열에 옮기는 과정이다. ROS 2에서 사용하는 CDR 같은 직렬화 형식과 MCAP 저장 형식을 분리한다. MCAP을 열었다고 안의 바이트를 임의의 float 배열로 읽어도 되는 것은 아니다. 메시지 타입/스키마와 encoding을 알아야 올바르게 복원할 수 있다. 스키마는 필드 이름·자료형/구조를 설명하며 단위/프레임/행동 의미까지 충분히 설명하는지는 별도다.
MCAP 공식 규격은 schema, channel, message 등의 레코드를 정의한다. channel은 메시지 흐름/토픽과 encoding·스키마 연결을 담고 message는 해당 채널의 데이터/시각을 담는다. schema ID나 channel ID는 그 파일/기록의 연결 식별자이지 모든 데이터셋에서 같은 과업을 뜻하는 전역 ID가 아니다. 변환 시 이 연결과 토픽별 메시지 수를 보존/검증한다.
취득·발행·기록 시각이 무엇을 뜻하는지 적는다
카메라 메시지 header의 stamp는 해당 드라이버 계약의 취득 시각이어야 하는 반면 bag의 기록 시각은 별도의 사건을 나타낼 수 있다. MCAP 규격의 log_time은 기록 시각, publish_time은 발행 시각이며 발행 시각이 없으면 log_time을 사용하도록 정의한다. 따라서 publish_time 필드가 존재한다고 실제 발행 시각을 독립 관측했다고 단정하지 않는다. 어느 writer가 무엇을 넣었는지도 확인한다.
그림 M20.04-1. 각 칸은 서로 다른 역할/근거다. 아래 방향은 자료/처리의 연결이며 실제 성공을 자동으로 보장하지 않는다. 직접 제작한 개념도이며 본문의 조건과 함께 읽는다.
합성 예에서 같은 시계의 취득 1.00 s/발행 1.02 s/기록 1.04 s가 주어지면 취득→발행은 20 ms, 발행→기록도 20 ms, 취득→기록은 40 ms다. 기록 1.04를 취득 시각으로 덮어쓰면 이 차이를 잃는다. 단위가 모두 초라도 시계가 다르면 바로 빼지 않는다. 시계 변환·reset/시뮬레이션 시간 여부와 유효 구간을 기록한다.
Jazzy rosbag2 공식 README는 simulation time 옵션과 /clock 기반 기록 시각도 설명한다. bag의 시간과 메시지 내부 stamp를 한 열로 합치지 않는다. 정렬용 기록 시각, 영상/관절 정합에 쓸 취득 시각과 원본 정밀도를 보존해야 지연과 시계 문제를 구분할 수 있다.
압축은 바이트 크기의 질문이다
무손실 압축은 원래 바이트를 복원하는 방식이다. 로그 파일 압축과 영상 codec의 손실 모드는 계층이 다르다. 이미 손실 압축된 영상의 바이트를 무손실 압축했다고 원래 센서 픽셀을 복원한 것은 아니다. 어떤 계층에서 무엇을 줄였는지와 디코딩 뒤의 검사 기준을 정한다.
합성 파일이 5,000,000 byte에서 2,000,000 byte로 줄었다고 하자. 원본/결과 크기 비율은 2.5:1, 절감률은(5,000,000−2,000,000)/5,000,000=60%다. '60% 압축'이라는 표현만 쓰면 결과가 60%인지 감소량이 60%인지 모호하므로 두 크기/정의를 적는다. 이 수치는 측정된 제품 성능이 아니라 계산용 입력이다. 데이터 내용/codec·설정/장비에 따라 실제 비율과 처리 비용은 달라진다.
Jazzy rosbag2의 파일/메시지 압축과 MCAP plugin의 내부 chunk 압축은 다른 설정이다. chunk는 여러 레코드를 묶은 저장 단위이며 압축·인덱스 구성에 사용된다. Jazzy MCAP plugin 문서에서 선택한 설정을 확인한다. 작은 파일이 됐다는 것만으로 무작위 접근/인덱스·복구나 기록 중 처리량까지 좋아졌다고 결론 내리지 않는다. 여러 압축을 겹친 실험이면 각 계층/시간 비용을 기록한다.
변환에는 수치와 의미의 손실이 따로 있다
시각은 특히 큰 기준값과 작은 차이를 함께 다룰 때 정밀도가 중요하다. 원본 취득 시각이 1,000,000,001 ns와 1,000,000,002 ns라면 차이는 1 ns다. 둘을 초로 바꿔 float32에 저장하면 이 예에서는 모두 1.0으로 반올림될 수 있어 순서/차이가 사라진다. 원본 정수 ns와 시계/기준을 보존하고 상대 시간 표현을 추가로 만들 때도 정밀도/반올림 규칙을 명시한다. float64라는 이름만으로 모든 크기의 ns 차이가 항상 보존된다고도 가정하지 않는다.
각도 변환은 q_deg=q_rad×180/π, 역변환은 q_rad=q_deg×π/180이다. π는 원주율이고 rad/degree는 각도 표현이다. q_rad=π/2라면 90 degree다. 숫자 90을 rad로 이름만 바꾸면 약 57.3배 스케일 오류가 된다. schema의 float32 형식은 그대로라 파서가 오류 없이 읽어도 의미가 틀릴 수 있다. 그리퍼 0~100을 0~1로 바꿀 때도 값/스키마·정규화 통계를 함께 갱신해야 한다.
bag을 Dataset으로 바꾸는 과정에서는 관절 이름/순서·단위와 action의 절대/증분·제한 전/후, 카메라 이름/색/전처리, 취득 시각과 명령 적용 시점의 대응을 정한다. rosbag2 저장 형식만 바꾸는 작업이 이런 정책 학습용 의미 변환까지 자동 수행하지는 않는다. 토픽의 숫자가 비슷하다는 이유로 observation과 action을 같은 열에서 생성하지 않는다.
파일 변환과 에피소드 복원을 구분한다
bag은 여러 토픽의 연속 통신 기록일 수 있다. 하나의 파일이 하나의 시도라는 보장은 없다. 에피소드 시작/종료와 재시작·실패/지시문을 복원할 근거가 없으면 이 정보를 만들어낸 사실처럼 쓰지 않는다. 파일 분할도 크기/시간 기준일 수 있어 경계가 과업 종료를 뜻하지 않을 수 있다. M20.03의 논리 에피소드와 저장 파일의 구분을 적용한다.
기록 당시 토픽이 발견되지 않았거나 QoS/중단·처리 지연 등으로 필요한 자료가 빠질 수 있다. 변환 후 메시지 수가 원본과 같아도 애초 수집에서 놓친 자료까지 복구한 것은 아니다. 예상 빈도/구간·순서/시각 간격과 관측한 메시지를 비교하고 결측/중복·순서 역전을 별도 보고한다. 재색인/파일 복구와 잃어버린 원본 메시지의 복원도 다른 문제다.
round-trip과 의미 검사를 함께 남긴다
변환 계획에는 원본은 보존하고 새 결과를 생성한다는 원칙, 입력/출력 형식·버전·변환기/옵션·모델/보정/스키마 revision을 기록한다. byte hash는 특정 파일의 동일성을 검사하지만 다른 컨테이너/압축 결과와 반드시 같아야 하는 것은 아니다. 반대로 hash가 정상이라고 단위/action 뜻이 맞다는 증거도 아니다. 무손실 압축만 바꾼 경우 원본 복원 바이트를 비교하는 검사와, 필드 변환의 허용 오차/의미 검사를 구분한다.
round-trip은 변환 후 원래 표현으로 되돌려 비교하는 것이다. 메시지 수·토픽/스키마 연결, 원본 취득/기록 시각과 순서, 값/단위·그리퍼/카메라 축/색, episode/task 매핑과 누락을 확인한다. 수치 허용 오차는 변수별 단위에 맞춰 정하며 단일 임계값을 rad와 m·정규화 값에 섞지 않는다. 잘못된 변환과 잘못된 역변환이 서로 상쇄될 수도 있으므로 독립 기준값/스키마 검사도 필요하다.
기존 4.4 시간 정합과 기존 5.3 Dataset 구조를 연결해 비교할 수 있다. 이번 활동은 파일 명세/합성 값만 검토하고 메시지를 실제 로봇 토픽에 재발행하지 않는다.