@@ -30,7 +30,10 @@ diverge by action. Learning-free episodic planning — see
3030 over a shared JSON wire; ROS 2 clients are unchanged.
3131- ** Compiled Nav2 costmap layer** — predicted occupancy flows straight into the
3232 Nav2 costmap (real ` nav2_costmap_2d::Layer ` , not a mock).
33- - ** rosbag2 → LeRobot** dataset export, ** GPU-free** .
33+ - ** Compiled Nav2 DWB critic** — score DWB rollouts against the same predicted
34+ occupancy (` world_model_dwb_critics ` ).
35+ - ** rosbag2 → LeRobot** dataset export, ** GPU-free** (optional ` lerobot ` loader
36+ validation test included).
3437- ** Counterfactual imagination & planning (learning-free)** — imagine * "what if I
3538 steer left / straight / right"* and plan to an image goal from an episodic
3639 retrieval world model, ** no dynamics training** ; validated on real public data.
@@ -152,16 +155,80 @@ ros2 bag record -s mcap -o my_run \
152155panel and enable the ` /world_model_viz/imagination ` topic — markers carry their
153156own colors.)
154157
158+ ### 5. Replay your own rosbag2
159+
160+ Feed a recorded drive through the World Model — no robot, no synthetic publisher.
161+ The ` bag_relay ` node joins camera + ` cmd_vel ` + ` odom ` into
162+ ` world_model_msgs/Observation ` ; the launch file starts bag play, the runtime,
163+ the imagination viewer and RViz.
164+
165+ ``` bash
166+ ros2 launch world_model_bringup replay_imagination.launch.py \
167+ bag:=/path/to/my_drive \
168+ adapter:=dummy # or ijepa / vjepa2 on a GPU box
169+
170+ # topic names differ in your bag? override them:
171+ # image_topic:=/front_camera/image_raw action_topic:=/control/cmd state_topic:=/localization/odom
172+ # headless:
173+ # rviz:=false
174+ ```
175+
176+ Record your own bag first:
177+
178+ ``` bash
179+ ros2 bag record -s mcap -o my_drive /camera/image_raw /cmd_vel /odom
180+ ```
181+
182+ ** Record an experience memory while replaying** (for learning-free counterfactual
183+ planning). The ` experience_recorder ` encodes each frame, pairs consecutive
184+ latents with steering (` cmd_vel.angular.z ` ), and writes ` experience.npz ` when
185+ the bag finishes. ` planning_node ` reloads the file automatically.
186+
187+ ``` bash
188+ ros2 launch world_model_bringup replay_imagination.launch.py \
189+ bag:=/path/to/my_drive \
190+ record_experience:=true \
191+ experience_out:=/tmp/experience.npz
192+
193+ # after bag play ends (~3s idle), imagine L / straight / R:
194+ ros2 service call /world_model_planning/imagine_futures \
195+ world_model_msgs/srv/ImagineFutures \
196+ " {steering_options: [-0.7, 0.0, 0.7], horizon: 12}"
197+ ```
198+
199+ Use ` adapter:=ijepa ` on a GPU box for real latent encoding; ` dummy ` works
200+ GPU-free for pipeline smoke tests.
201+
202+ ### 6. Counterfactual replay demo (bundled bag, one command)
203+
204+ A synthetic driving bag ships in the repo. This replays it, records
205+ ` experience.npz ` , auto-calls ` ImagineFutures ` , and shows the L / straight / R
206+ mosaic in RViz — no robot, no dataset download, GPU-free with ` dummy ` .
207+
208+ ``` bash
209+ ros2 launch world_model_bringup replay_counterfactual_demo.launch.py
210+ # faster replay: rate:=3.0 headless: rviz:=false
211+ ```
212+
213+ Topics: ` /counterfactual_viz/counterfactual/mosaic ` (Image) and
214+ ` …/markers ` (MarkerArray). Rebuild the bundled bag with
215+ ` python3 world_model_bringup/scripts/build_demo_bag.py ` .
216+
217+ Open the counterfactual clip in [ Foxglove] ( https://foxglove.dev/ ) with layout
218+ [ ` world_model_viz/foxglove/counterfactual.json ` ] ( world_model_viz/foxglove/counterfactual.json )
219+ (record ` /counterfactual_viz/counterfactual/mosaic ` during a replay).
220+
155221## Packages
156222
157223| package | type | what |
158224| ---| ---| ---|
159225| ` world_model_msgs ` | ament_cmake | the message/action ** contract** : ` Observation ` , ` ActionCondition ` , ` FutureState ` , ` FutureOccupancy ` , ` LatentState ` , ` RiskScore ` , ` Rollout ` , ` PredictFuture.action ` |
160- | ` world_model_py ` | ament_python | adapter SDK (` load_model ` ), ` dummy ` / ` ijepa ` / ` remote ` adapters, lifecycle runtime node, reference ` world-model-server ` , benchmark, ` world-model ` CLI |
161- | ` world_model_viz ` | ament_python | imagination viewer: imagined ` FutureOccupancy ` + ` RiskScore ` → RViz ` MarkerArray ` |
226+ | ` world_model_py ` | ament_python | adapter SDK (` load_model ` ), ` dummy ` / ` ijepa ` / ` remote ` adapters, lifecycle runtime node, ` bag_relay ` (rosbag2 → Observation), reference ` world-model-server ` , benchmark, ` world-model ` CLI |
227+ | ` world_model_viz ` | ament_python | imagination viewer ( ` FutureOccupancy ` + ` RiskScore ` → RViz); counterfactual mosaic viewer ( ` ImagineFutures ` → Image + MarkerArray) |
162228| ` world_model_datasets ` | ament_python | ` export_lerobot ` : rosbag2 → LeRobot-compatible dataset (parquet + mp4 + meta), GPU-free |
163229| ` world_model_nav2 ` | ament_python | score Nav2 candidate trajectories by model-based risk (` ScoreTrajectories ` service + risk-coloured path markers) |
164230| ` world_model_costmap ` | ament_cmake (C++) | ** compiled** ` nav2_costmap_2d::Layer ` that stamps predicted ` FutureOccupancy ` into the costmap |
231+ | ` world_model_dwb_critics ` | ament_cmake (C++) | ** compiled** ` dwb_core::TrajectoryCritic ` that scores DWB rollouts against predicted occupancy |
165232| ` world_model_bringup ` | ament_cmake | launch files + demo config |
166233
167234## Architecture
@@ -295,9 +362,18 @@ Output (v2.1 layout): `meta/{info,episodes,tasks,stats}.json[l]`,
295362Supported state/action types: ` nav_msgs/Odometry ` , ` sensor_msgs/JointState ` ,
296363` geometry_msgs/Twist[Stamped] ` , ` std_msgs/Float{32,64}MultiArray ` .
297364
298- > Honesty: validated structurally (parquet round-trips, mp4 is ffprobe-readable,
299- > counts consistent), ** not** against the ` lerobot ` loader (not a dependency
300- > here). Verify against your ` lerobot ` version before training.
365+ ** Loader validation (optional).** With ` pip install lerobot ` , convert v2.1 → v3.0
366+ and open the dataset:
367+
368+ ``` bash
369+ pip install lerobot
370+ cd world_model_datasets
371+ python3 -m pytest test/validate_lerobot_loader.py -q
372+ ```
373+
374+ The test exports a toy dataset, runs ` lerobot.scripts.convert_dataset_v21_to_v30 ` ,
375+ and loads it with ` LeRobotDataset ` . Structural checks (parquet round-trip, ffprobe-
376+ readable mp4, counts) run in CI without ` lerobot ` .
301377
302378** Validated on real public data.** ` test/validate_real_lerobot.py ` builds a
303379rosbag2 from a public ** LeRobot SO-101** episode (real camera video + real 6-DoF
@@ -522,6 +598,34 @@ unit-tested. This follows recent World-Model failure/OOD-monitoring work, e.g.
522598[ failure detection without failure data] ( https://arxiv.org/html/2503.08558v1 ) ,
523599and [ foundation world models detecting manipulation failures] ( https://arxiv.org/pdf/2603.06987 ) .
524600
601+ ## Nav2 avoidance preview (predicted occupancy → detour)
602+
603+ ![ World Model predicted occupancy steers around the lethal union] ( docs/nav2_avoidance.gif )
604+
605+ <sub >** Predicted occupancy → avoid.** The dummy World Model imagines a moving
606+ obstacle blob; ` costmap_preview_node ` shows the lethal union that
607+ ` world_model_costmap::WorldModelLayer ` would stamp into Nav2, and
608+ ` avoidance_demo_node ` picks a detour path that misses it (naive straight in red,
609+ safe arc in green). GPU-free, no Gazebo.</sub >
610+
611+ ``` bash
612+ ros2 launch world_model_bringup nav2_avoidance_demo.launch.py
613+ ```
614+
615+ Honest scope: this previews the ** costmap layer** logic in RViz; for a ** live Nav2
616+ stack** (loopback sim, not Gazebo) with both plugins loaded:
617+
618+ ``` bash
619+ sudo apt install ros-jazzy-nav2-loopback-sim # once
620+ ros2 launch world_model_bringup nav2_loopback_world_model.launch.py use_rviz:=false
621+ # optional headless smoke:
622+ python3 $( ros2 pkg prefix world_model_bringup) /share/world_model_bringup/scripts/smoke_nav2_loopback.py
623+ ```
624+
625+ For trajectory * ranking* by model risk, see the scorer mock below. For a ** compiled
626+ DWB critic** , see
627+ [ world_model_dwb_critics/README.md] ( world_model_dwb_critics/README.md ) .
628+
525629## Nav2 trajectory scoring (mock)
526630
527631![ Nav2 trajectory scoring by World Model risk] ( docs/nav2_scoring.gif )
@@ -551,16 +655,20 @@ For the **production path**, `world_model_costmap` is a *compiled*
551655` nav2_costmap_2d::Layer ` that stamps the model's predicted ` FutureOccupancy `
552656straight into the Nav2 costmap — so any planner/controller avoids predicted
553657obstacles. See [ world_model_costmap/README.md] ( world_model_costmap/README.md ) .
658+ ` world_model_dwb_critics ` adds the same rule as a ` dwb_core::TrajectoryCritic `
659+ for DWB local planners.
554660
555661## Roadmap (90-day MVP)
556662
557663- ** 0–30d (this scaffold):** msgs, adapter SDK, dummy + remote adapters,
558664 lifecycle node, CLI, HTML smoke/bench report — ** all GPU-free** . ✅
559665- ** 31–60d:** JEPA latent adapter (image→latent→surprise) ✅, RViz imagination
560- markers ✅, rosbag2 replay demo (next).
561- - ** 61–90d:** rosbag2 → LeRobotDataset converter ✅, Nav2 trajectory-scoring
562- mock ✅, compiled Nav2 costmap layer ✅, remote adapter + reference server
563- (Cosmos/DreamZero-ready) ✅, benchmark dashboard, VLA Zoo / Walking Zoo examples.
666+ markers ✅, rosbag2 replay demo ✅, counterfactual replay demo ✅, Nav2
667+ avoidance preview ✅.
668+ - ** 61–90d:** rosbag2 → LeRobotDataset converter ✅ (optional ` lerobot ` loader
669+ validation ✅), Nav2 trajectory-scoring mock ✅, compiled Nav2 costmap layer ✅,
670+ compiled DWB critic ✅, remote adapter + reference server (Cosmos/DreamZero-ready)
671+ ✅, benchmark dashboard. VLA Zoo / Walking Zoo — out of scope for now.
564672
565673## Contributing
566674
0 commit comments