When to Use
- A scene is up (
launch-sceneskill) and the user wants to persist topic streams for later replay or dataset assembly. - Teleop is running (
run-teleopskill ingeniesim_teleop) and the user wants ROS-side recording on top of the teleop loop's own per-episode artifacts. - Benchmark run wants extra raw-topic capture beyond what
--benchmark.record=truewrites.
Do not use for:
- Replaying an already-recorded bag → just use
ros2 bag play. - Recording from a stopped sim —
ros2 bagneeds live publishers.
Critical Patterns
use_sim_time:=trueis set engine-side, so every bag captures/clockand replays cleanly at simulation rate. Don't override the topic with wall-clock.- Always record
/clockin addition to your topic list — replay without it falls back to wall-clock pacing and timing breaks for downstream learners. - Camera topics are heavy. A G2 scene with three cameras at 30 Hz
easily clocks 200+ MB/min. Either narrow the topic list or use
--max-bag-sizeto roll over bags. - One recording per run. Restart the scene between recordings —
the engine's
init_*blocks run only at startup, and a fresh reset is the only way to guarantee a deterministic t0. ros2 bagis the canonical path. There is no dedicated "recorder" distribution in the stack — the teleop and benchmark loops have their own per-episode writers (see below), and everything else goes throughros2 bag.
What to record
| Topic | Why |
|---|---|
/clock |
Sim time — required for deterministic replay |
/tf, /tf_static |
World pose tree (robot links, free objects) |
/joint_states |
Robot state from the engine |
/joint_command |
Whatever a teleop / planner pushed in |
/odom |
Mobile base (if applicable) |
/camera/* |
RGB / depth / fisheye streams (heavy, narrow if you can) |
/tf_render |
OVRtx render-layer transforms (only if you'll replay rendering) |
Workflow
Step 1 — Confirm the scene is up
ros2 topic list | grep -E "joint_states|tf|clock"
ros2 topic hz /clock # sim-time pacing should be live
Step 2 — Pick a topic list
For a typical G2 manipulation run:
TOPICS=(
/clock
/tf /tf_static
/joint_states
/joint_command
/odom
)
# add cameras if you want pixels:
TOPICS+=(/camera/head/rgb /camera/head/depth)
Step 3 — Start the bag
# inside the container, in a second shell:
source devel/setup.bash
ros2 bag record \
--output ./runs/$(date +%Y%m%d_%H%M%S)_${USER}_pnp \
--max-bag-size 1073741824 \
--storage mcap \
"${TOPICS[@]}"
Stop with Ctrl-C. The bag dir contains metadata.yaml + chunked
.mcap files.
Step 4 — Verify
ros2 bag info ./runs/<dir>
ros2 bag play ./runs/<dir> --clock # replay sim-time
Step 5 — (alternative) Let the loop record for you
For teleop:
geniesim teleop run --device_type=pico --record-dir ./runs
# writes per-episode artifacts under ./runs/<episode>/
For benchmark:
geniesim benchmark run <CONFIG> --infer-host=<IP>:<PORT> \
--benchmark.record=true
# output_dir comes from the config; check the run banner
Commands (copy-paste summary for the user)
# Inside the container, alongside a running scene:
source devel/setup.bash
ros2 bag record \
--output ./runs/$(date +%Y%m%d_%H%M%S)_demo \
--storage mcap \
/clock /tf /tf_static /joint_states /joint_command /odom
# Replay:
ros2 bag play ./runs/<dir> --clock
Notes
--storage mcapis preferred over the legacysqlite3storage — faster random access, smaller files, and the standard for ROS 2 Jazzy.- If you plan to feed the bag to a dataset pipeline, also pin the
manifest.json(underassets/scenes/<scene>/) alongside the bag — it records the exact USD + robot variant the bag was captured against. - For dataset-scale recording, the
geniesim_generatorskills (generate-scene,search-assets) plus the benchmarkrecord=trueflag are the closer fit — they write episode-indexed artifacts, not raw bags.
Resources
- Teleop recording hooks: source/geniesim_teleop/skills/run-teleop/SKILL.md
- Benchmark recording flag: source/geniesim_benchmark/skills/run-benchmark/SKILL.md
- ros2 bag (Jazzy): https://docs.ros.org/en/jazzy/Tutorials/Beginner-CLI-Tools/Recording-And-Playing-Back-Data/Recording-And-Playing-Back-Data.html