wholebody-vlahumanoid-vlaros2mcapfoxglovelerobotrobodmdatasetdata-engineering

ROS 2 MCAP làm chuẩn raw log

Thiết kế raw log MCAP cho humanoid VLA: record, YAML writer options, replay, inspect và map topic sang LeRobot/Robo-DM.

Nguyễn Anh Tuấn10 tháng 6, 202616 phút đọc
ROS 2 MCAP làm chuẩn raw log

Mục tiêu của bài này

Trong bài 1, chúng ta chia vai operator và data supervisor cho một ca pilot humanoid VLA. Trong bài 2, chúng ta chọn teleop stack để tạo action có thể học được. Bài 3 xử lý lớp nằm giữa robot và dataset training: raw log.

Raw log là bản ghi gốc, chưa bị ép vào schema training. Nó phải trả lời được ba câu hỏi: robot đã thấy gì, robot đã làm gì, và từng tín hiệu xảy ra lúc nào. Nếu raw log sai, mọi bước phía sau đều phải đoán. LeRobot có thể lưu observation.state, action, camera và timestamp. Robo-DM nhắm tới lưu trữ và tải robot trajectories đa phương thức ở quy mô lớn. Nhưng trước khi chuyển sang hai định dạng đó, bạn cần một chuẩn ghi thô có thể replay, inspect và audit. Với ROS 2, lựa chọn thực dụng nhất hiện nay là MCAP qua rosbag2_storage_mcap.

Nếu bạn mới xây ROS graph hoặc chưa quen topic/service/action, đọc thêm giới thiệu ROS 2 cho robotics trước sẽ giúp phần record dễ theo hơn. Nếu mục tiêu của bạn là đưa log lên một hệ vận hành đội robot, bài monitor ROS 2 robots từ xa là lớp bổ sung tốt sau khi raw log đã ổn định.

Bài này đi từ lệnh đơn giản:

ros2 bag record -s mcap --all

đến file YAML cấu hình McapWriterOptions, rồi tới bảng map topic humanoid sang schema dataset. Sau bài này, bạn sẽ có một design raw log có thể:

Việc cần làm Công cụ
Ghi toàn bộ ROS 2 topics ra file .mcap ros2 bag record -s mcap
Replay lại vào ROS graph ros2 bag play -s mcap
Kiểm tra nhanh topic, type, timestamp ros2 bag info -s mcap, mcap info, Foxglove
Xem camera, joint state, TF, action theo timeline Foxglove
Export sang dataset training Script đọc MCAP, giữ timestamp gốc, ghi LeRobot/Robo-DM

Điểm quan trọng nhất: đừng biến MCAP thành dataset training ngay từ đầu. Hãy coi MCAP là "hộp đen" của episode. Training schema có thể đổi, model có thể đổi, camera có thể thêm, nhưng raw log phải đủ trung thực để bạn quay lại điều tra.

Vì sao MCAP hợp với humanoid VLA

MCAP là container cho các message pub/sub có timestamp và serialization khác nhau. Theo MCAP spec, một message record có channel_id, sequence, log_time, publish_timedata. Với ROS 2, data thường là payload CDR của message, còn channel chứa topic và message encoding. Đây là cấu trúc rất hợp với humanoid vì robot không chỉ có một stream video. Một episode có thể có camera đầu, camera cổ tay trái/phải, joint state, IMU, force/torque, TF, teleop command, action đã retarget, safety state và prompt ngôn ngữ.

rosbag2_storage_mcap là storage plugin cho rosbag2. Tài liệu ROS 2 cho biết bạn có thể dùng MCAP với lệnh rosbag thường bằng cách thêm --storage mcap hoặc -s mcap: record, play và info đều dùng được. Tài liệu MCAP cũng nhấn mạnh Foxglove có thể mở MCAP chứa ROS 2 data, trong khi SQLite .db3 cũ không tự chứa đầy đủ message definitions cho nhiều workflow bên ngoài ROS.

Với data center cho VLA, MCAP có bốn lợi ích rất cụ thể:

Lợi ích Ý nghĩa trong thu dữ liệu humanoid
Self-contained hơn .db3 Tool như Foxglove có nhiều thông tin hơn để decode message, đặc biệt khi có custom messages
Có schema, channel, message Dễ tách topic thành sensor/action/meta khi export
log_timepublish_time Giữ được thời điểm record và thời điểm publish nếu pipeline cung cấp
Có chunk, index, compression Có thể tối ưu giữa throughput trên robot và truy vấn sau ca thu

Beginner thường hỏi: "Tôi có cần tự học binary format MCAP không?" Câu trả lời là không cho bước đầu. Bạn dùng ros2 bag record -s mcap, sau đó dùng ros2 bag play, ros2 bag info, Foxglove hoặc thư viện đọc MCAP. Nhưng bạn cần hiểu vài khái niệm để không cấu hình sai.

Bắt đầu với ros2 bag record -s mcap

Trước tiên cài plugin, thay $ROS_DISTRO bằng distro của bạn:

sudo apt-get install ros-$ROS_DISTRO-rosbag2-storage-mcap

Kiểm tra nhanh:

ros2 bag record --help | rg "storage|mcap"

Nếu hệ của bạn không có rg, dùng grep:

ros2 bag record --help | grep -E "storage|mcap"

Lệnh ghi toàn bộ topics:

mkdir -p ~/humanoid_logs

ros2 bag record -s mcap \
  -o ~/humanoid_logs/episode_000123_raw \
  --all

Lệnh này tiện để smoke test, nhưng production không nên luôn dùng --all. Với humanoid, --all có thể kéo theo debug image, point cloud nặng, TF phụ, log text tần số cao và topic test không ổn định. Bắt đầu bằng --all trong pilot, sau đó chốt danh sách topic rõ ràng:

ros2 bag record -s mcap \
  -o ~/humanoid_logs/episode_000123_raw \
  /clock \
  /tf \
  /tf_static \
  /robot_description \
  /humanoid/head_camera/image_raw \
  /humanoid/left_wrist_camera/image_raw \
  /humanoid/right_wrist_camera/image_raw \
  /humanoid/joint_states \
  /humanoid/imu \
  /humanoid/teleop/raw_command \
  /humanoid/control/action \
  /humanoid/safety/state \
  /humanoid/task/prompt

Quy tắc naming nên ổn định từ ngày đầu:

logs/
  2026-06-10/
    robot_g1_001/
      episode_000123_raw/
        episode_000123_raw_0.mcap
        metadata.yaml
        operator_notes.md
        calibration/

metadata.yaml là metadata của rosbag2, không phải nơi bạn nhét toàn bộ annotation. Annotation của episode nên nằm cạnh bag hoặc trong topic riêng như /humanoid/task/prompt, /humanoid/task/result, /humanoid/operator/event. Lý do: nếu annotation cũng có timestamp trong ROS graph, bạn có thể nhìn nó trong Foxglove và align với video/action.

Replay và inspect trước khi nói về dataset

Sau khi record, làm ba bước kiểm tra trước khi export:

ros2 bag info -s mcap ~/humanoid_logs/episode_000123_raw

Bạn cần nhìn thấy duration, message count và topic list. Nếu topic camera không có message hoặc action count bằng 0, đừng export. Sửa recorder trước.

Replay:

ros2 bag play -s mcap ~/humanoid_logs/episode_000123_raw

Nếu bag là một file .mcap đơn lẻ thay vì folder rosbag2, một số môi trường cũng cần chỉ rõ storage:

ros2 bag play -s mcap ./episode_000123_raw_0.mcap

Khi replay custom message trong ROS 2, máy replay vẫn cần có package message/type support phù hợp trong workspace. MCAP chứa schema hữu ích cho tool bên ngoài như Foxglove, nhưng ROS 2 player vẫn publish serialized messages qua type support của ROS 2. Vì vậy data center nên version hóa Docker image hoặc workspace dùng để thu dữ liệu.

Inspect bằng Foxglove:

  1. Mở Foxglove.
  2. Chọn open local file hoặc kéo thả folder/file MCAP.
  3. Mở Topics để kiểm tra topic có đủ không.
  4. Thêm Raw Messages cho /humanoid/control/action.
  5. Thêm Image panels cho camera.
  6. Thêm Plot cho joint positions, velocities, command norm, safety state.
  7. Thêm 3D nếu bạn có TF và robot model đủ tốt.

Foxglove docs nói có thể mở MCAP local và xem timeline topic. Đây là lý do MCAP nên là chuẩn raw log: data supervisor không cần chờ script export chạy xong mới biết episode hỏng.

McapWriterOptions: YAML nên dùng thế nào

rosbag2_storage_mcap cho phép truyền --storage-config-file để cấu hình mcap::McapWriterOptions. Một file production vừa phải có thể bắt đầu như sau:

# mcap_writer_options.yaml
noChunkCRC: false
noAttachmentCRC: false
enableDataCRC: false
noSummaryCRC: false
noChunking: false
noMessageIndex: false
noSummary: false
chunkSize: 4194304
compression: "Zstd"
compressionLevel: "Fast"
forceCompression: false

Record với file này:

ros2 bag record -s mcap \
  -o ~/humanoid_logs/episode_000123_raw \
  --storage-config-file mcap_writer_options.yaml \
  /tf /tf_static /humanoid/joint_states /humanoid/control/action

Các option cần hiểu:

Option Beginner nên hiểu Gợi ý mặc định
noChunking Nếu true, writer không gom message vào chunk. Ghi có thể nhẹ hơn, nhưng mất nhiều lợi ích index/compression false cho raw log dài hạn
chunkSize Kích thước chunk chưa nén. Chunk lớn hơn thường nén tốt hơn, nhưng cần bộ nhớ/CPU hơn 4 MB là điểm khởi đầu tốt
compression None, Lz4, Zstd Zstd cho lưu lâu, Lz4 nếu CPU yếu
compressionLevel Nén nhanh hay nhỏ Fast hoặc Fastest trên robot
noChunkCRC Tắt CRC của chunk false nếu muốn kiểm tra hỏng dữ liệu
enableDataCRC Tính CRC toàn Data section, hữu ích khi không chunking false khi đã chunking
noMessageIndex Tắt index message false, vì export theo topic/time cần index
noSummary Tắt summary section false, vì info/seek/tooling cần summary
forceCompression Ép nén mọi chunk false, để writer bỏ qua chunk không đáng nén

Tài liệu ROS 2 cũng có preset fastwritezstd_small. fastwrite tối ưu throughput bằng cách cấu hình kiểu không chunking và không summary CRC, nhưng tài liệu cảnh báo không nên coi file đó là long-term storage nếu chưa post-process vì một số tác vụ như seek hoặc đọc subset topic có thể kém. Với humanoid data center, cách dùng thực dụng là:

Tình huống Cấu hình
Robot CPU yếu, đang pilot safety --storage-preset-profile fastwrite, sau đó convert/compress ngay sau ca
Thu production bình thường YAML Zstd/Fast, chunking/index bật
Archive sau QA Convert sang zstd_small hoặc nén offline

Ví dụ convert sau khi thu nhanh:

# convert_to_archive.yaml
output_bags:
  - uri: episode_000123_archive
    storage_id: mcap
    storage_preset_profile: zstd_small
ros2 bag convert \
  -i ~/humanoid_logs/episode_000123_raw \
  -o convert_to_archive.yaml

Map topic humanoid sang schema dataset

Đừng bắt đầu bằng code exporter. Bắt đầu bằng bảng topic. Một schema tốt nói rõ topic nào là observation, action, instruction, metadata và quality signal.

ROS 2 topic Message type ví dụ Vai trò trong raw log Field dataset gợi ý
/humanoid/head_camera/image_raw sensor_msgs/msg/Image Camera ego/head observation.images.head
/humanoid/left_wrist_camera/image_raw sensor_msgs/msg/Image Camera tay trái observation.images.left_wrist
/humanoid/right_wrist_camera/image_raw sensor_msgs/msg/Image Camera tay phải observation.images.right_wrist
/humanoid/joint_states sensor_msgs/msg/JointState Proprioception observation.state
/humanoid/imu sensor_msgs/msg/Imu Base orientation/accel observation.imu hoặc thêm vào state
/tf, /tf_static tf2_msgs/msg/TFMessage Frame tree Metadata/calibration, không nhất thiết train trực tiếp
/humanoid/teleop/raw_command custom/msg Input người điều khiển teleop.raw hoặc debug-only
/humanoid/control/action custom/msg hoặc Float32MultiArray Action thật gửi controller action
/humanoid/safety/state custom/msg E-stop, mode, fault episode.quality, filter
/humanoid/task/prompt std_msgs/msg/String Lệnh ngôn ngữ task, language_instruction
/humanoid/task/event custom/msg Start, success, fail, discard Episode boundaries/labels

LeRobot v3 mô tả dataset robot learning đa phương thức với time-series, sensorimotor signals, multi-camera video và metadata. API sample của LeRobot trả về các key như observation.state, action, observation.images.front_lefttimestamp. Vì vậy khi export từ MCAP, bạn không cần phát minh tên field lạ nếu mục tiêu là train với LeRobot. Hãy map về convention phổ biến trước.

Ví dụ một frame training sau export:

sample = {
    "episode_index": 123,
    "frame_index": 42,
    "timestamp": 8.400,  # seconds from episode start
    "observation.state": joint_state_vector,
    "observation.images.head": head_rgb,
    "observation.images.left_wrist": left_wrist_rgb,
    "observation.images.right_wrist": right_wrist_rgb,
    "action": action_vector,
    "task": "Pick up the blue bin and place it on the shelf.",
}

Điểm dễ sai là timestamp. MCAP message có log_timepublish_time ở nanoseconds. ROS message cũng có thể có header.stamp. Bạn phải chọn policy rõ:

Timestamp Dùng khi Rủi ro
header.stamp Sensor driver đã timestamp đúng tại capture time Một số custom action không có header
MCAP publish_time Muốn thời điểm node publish message Có thể lệch capture nếu pipeline xử lý lâu
MCAP log_time Muốn thời điểm recorder nhận message Bị ảnh hưởng network/recorder load

Khuyến nghị cho beginner:

  1. Nếu message có header.stamp đáng tin, dùng nó làm timestamp semantic.
  2. Luôn lưu thêm MCAP log_time để debug recorder delay.
  3. Chuẩn hóa timestamp = (t - episode_start_t) / 1e9 khi ghi LeRobot.
  4. Không nội suy phá hủy dữ liệu gốc trong raw log. Nội suy chỉ làm ở exporter.

Episode boundaries: đừng chỉ dựa vào tên file

Một lỗi phổ biến là mỗi lần bấm record tạo một episode, rồi coi toàn bộ bag là demo tốt. Với humanoid, operator có thể mất 10 giây chuẩn bị, thử gripper, reset stance, sau đó mới làm nhiệm vụ. Bạn cần boundary.

Cách đơn giản nhất là publish event:

/humanoid/task/event
  stamp: 2026-06-10T10:01:02.123Z
  episode_id: "episode_000123"
  event_type: "START" | "SUCCESS" | "FAIL" | "DISCARD" | "RESET"
  note: "left wrist camera bumped at 00:13"

Exporter sẽ cắt đoạn START đến SUCCESS, bỏ đoạn trước/sau, và đánh dấu episode failed nếu có FAIL hoặc DISCARD. Nếu bạn chưa có custom message, có thể dùng std_msgs/msg/String chứa JSON trong pilot. Production nên có message type rõ ràng để Foxglove và exporter validate tốt hơn.

Export sang LeRobot mà không mất timestamp

Pipeline tối thiểu:

MCAP raw log
  -> reader đọc theo topic/time
  -> chọn episode window
  -> decode camera/state/action
  -> align theo action hoặc camera clock
  -> ghi Parquet/MP4/metadata theo LeRobot

Pseudo-code:

episode_start_ns = find_event("START").timestamp_ns
episode_end_ns = find_event("SUCCESS").timestamp_ns

frames = []
for action_msg in iter_topic("/humanoid/control/action", episode_start_ns, episode_end_ns):
    t_ns = choose_timestamp(action_msg)
    frame = {
        "timestamp": (t_ns - episode_start_ns) / 1e9,
        "observation.state": nearest("/humanoid/joint_states", t_ns),
        "observation.images.head": nearest_frame("/humanoid/head_camera/image_raw", t_ns),
        "observation.images.left_wrist": nearest_frame("/humanoid/left_wrist_camera/image_raw", t_ns),
        "observation.images.right_wrist": nearest_frame("/humanoid/right_wrist_camera/image_raw", t_ns),
        "action": decode_action(action_msg),
        "task": current_prompt(t_ns),
    }
    frames.append(frame)

Ở đây action là clock chính vì imitation learning thường học "observation tại thời điểm t -> action tại thời điểm t". Nếu camera 30 FPS và control 20 Hz, bạn có thể lấy nearest camera frame cho mỗi action. Nếu policy train ở 10 Hz, downsample action sau khi align, không downsample raw log.

Checklist chống mất timestamp:

Check Pass khi
Monotonic timestamp timestamp tăng đều trong mỗi episode
Max camera-action gap Nearest image không lệch quá ngưỡng, ví dụ 50 ms
Header vs log delta Sensor header.stamp không lệch recorder log_time bất thường
Dropped frames Frame index không nhảy quá nhiều
Safety filter Episode có E-stop hoặc fault không vào train set mặc định

Export sang Robo-DM: coi MCAP là nguồn sự thật

Robo-DM paper mô tả bài toán lớn: dataset robot có video, text, numerical modalities và nhiều camera rất khó curate, distribute và load. Robo-DM hướng tới framework quản lý data robot hiệu quả, có lưu trữ self-contained dựa trên EBML, compression mạnh và retrieval nhanh. Paper cũng nhấn mạnh khả năng giữ original timestamps để tránh heuristic alignment.

Điều đó không có nghĩa bạn phải record trực tiếp bằng Robo-DM ngay ngày đầu. Với ROS 2 humanoid, thiết kế sạch là:

Robot ROS 2 graph
  -> MCAP raw log, immutable
  -> QA report
  -> Robo-DM trajectory store for large-scale train/retrieval

MCAP giữ bản gốc để replay trong ROS 2 và inspect bằng Foxglove. Robo-DM hoặc LeRobot là bản dẫn xuất cho training. Khi model lỗi ở eval, bạn quay lại MCAP để xem action gốc, camera gốc, TF gốc và safety event gốc.

Cấu hình đề xuất cho pilot 10 episode

Nếu bạn đang ở giai đoạn beginner, hãy dùng cấu hình này:

# pilot_mcap_writer_options.yaml
noChunkCRC: false
noAttachmentCRC: false
enableDataCRC: false
noSummaryCRC: false
noChunking: false
noMessageIndex: false
noSummary: false
chunkSize: 4194304
compression: "Zstd"
compressionLevel: "Fast"
forceCompression: false

Lệnh record:

ros2 bag record -s mcap \
  -o ~/humanoid_logs/$(date +%Y%m%d_%H%M%S)_pilot \
  --storage-config-file pilot_mcap_writer_options.yaml \
  /clock /tf /tf_static /robot_description \
  /humanoid/head_camera/image_raw \
  /humanoid/left_wrist_camera/image_raw \
  /humanoid/right_wrist_camera/image_raw \
  /humanoid/joint_states \
  /humanoid/imu \
  /humanoid/teleop/raw_command \
  /humanoid/control/action \
  /humanoid/safety/state \
  /humanoid/task/prompt \
  /humanoid/task/event

Sau mỗi episode:

ros2 bag info -s mcap ~/humanoid_logs/20260610_100102_pilot
ros2 bag play -s mcap ~/humanoid_logs/20260610_100102_pilot --rate 0.5

Mở trong Foxglove và data supervisor đánh dấu:

Mục Pass/Fail
Ba camera có hình trong toàn bộ đoạn task
Joint state và action có tần số ổn định
Prompt đúng nhiệm vụ
Không có E-stop trong đoạn train
Replay không crash do thiếu message type
Exporter giữ timestamp tăng đều

Những lỗi cần tránh

Chỉ lưu video MP4 mà không lưu action. Video giúp demo đẹp, nhưng policy cần action. Hãy record action topic thật gửi controller.

Chỉ lưu action đã normalize. Raw log nên lưu action có đơn vị rõ ràng, ví dụ radian, meter, Newton, normalized gripper kèm metadata. Normalize là việc của exporter.

Đổi tên topic giữa các ngày pilot. Nếu hôm nay là /humanoid/control/action và mai là /action, exporter sẽ phải có nhiều nhánh đặc biệt. Chốt naming sớm.

Tắt index để tiết kiệm chút dung lượng rồi quên post-process. Data center cần seek theo time/topic. noMessageIndex: falsenoSummary: false là mặc định hợp lý.

Không lưu event start/success/fail. Không có boundary thì bạn sẽ train cả đoạn chuẩn bị, đoạn operator nói chuyện, hoặc đoạn reset.

Tin một timestamp duy nhất mà không audit. Hãy lưu cả semantic time từ header.stamp nếu có và recorder time từ MCAP. Khi alignment lỗi, hai clock này cho bạn manh mối.

Nguồn kỹ thuật nên đọc

Kết luận

MCAP không thay thế LeRobot hoặc Robo-DM. Nó đứng trước hai định dạng đó. Vai trò của nó là giữ raw truth: topic nào được publish, message type gì, timestamp nào, action nào, camera nào, safety event nào. Nếu bạn làm đúng, một episode humanoid có thể được replay bằng ros2 bag play -s mcap, inspect bằng Foxglove, export sang LeRobot để train nhanh, hoặc chuyển sang Robo-DM để quản lý quy mô lớn.

Trong bài 4, chúng ta sẽ đi sâu hơn vào bước sau MCAP: thiết kế exporter LeRobot/Robo-DM, chia train/val, lưu video, metadata và statistics sao cho không biến raw log sạch thành dataset khó debug.

Bài viết liên quan

NT

Nguyễn Anh Tuấn

Robotics & AI Engineer. Building VnRobo — sharing knowledge about robot learning, VLA models, and automation.

Khám phá VnRobo

Bài viết liên quan

LeRobotDataset và Robo-DM cho data lake
wholebody-vla

LeRobotDataset và Robo-DM cho data lake

10/6/202611 phút đọc
NT
Pilot 2 người cho dữ liệu humanoid VLA
wholebody-vla

Pilot 2 người cho dữ liệu humanoid VLA

10/6/202615 phút đọc
NT
Chọn teleoperation stack cho humanoid
wholebody-vla

Chọn teleoperation stack cho humanoid

10/6/202616 phút đọc
NT