Add GR00T-N1.7-LIBERO LIBERO smoke-test tooling, the 20260512_122756 10-rollout run artifacts (videos/frames/actions/plots/reports), setup logs, README/LICENSE/NOTICE. Built on NVIDIA Isaac-GR00T (Apache-2.0); upstream source / weights / gated backbone not included.
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +234 -0
- LICENSE +190 -0
- NOTICE +30 -0
- README.md +122 -0
- examples/LIBERO/smoke_tests/ACTIVATION_HOOK_NOTES.md +100 -0
- examples/LIBERO/smoke_tests/README.md +160 -0
- examples/LIBERO/smoke_tests/_libero_rollout_worker.py +311 -0
- examples/LIBERO/smoke_tests/make_video_montage.py +578 -0
- examples/LIBERO/smoke_tests/review_smoke_tests.py +139 -0
- examples/LIBERO/smoke_tests/run_10_smoke_tests.py +590 -0
- examples/LIBERO/smoke_tests/scenarios_10.yaml +167 -0
- examples/LIBERO/smoke_tests/visualize_smoke_run.py +739 -0
- outputs/_setup_logs/_dryrun.log +57 -0
- outputs/_setup_logs/_hf_download.log +4 -0
- outputs/_setup_logs/_libero_setup.log +439 -0
- outputs/_setup_logs/_server.log +85 -0
- outputs/_setup_logs/_uv_sync.log +6 -0
- outputs/_setup_logs/_venv_install.log +218 -0
- outputs/_setup_logs/server_live.log +20 -0
- outputs/_setup_logs/smoke_run.log +67 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/actions.npy +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00000.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00001.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00002.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00003.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00004.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00005.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00006.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00007.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00008.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00009.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00010.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00011.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00012.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00013.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00014.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00015.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00016.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00017.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00018.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00019.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00020.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00021.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00022.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00023.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00024.png +3 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/metadata.json +36 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/rollout_summary.json +22 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/stderr.log +22 -0
- outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/stdout.log +7 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,237 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00000.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00001.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00002.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00003.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00004.png filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00005.png filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00006.png filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00007.png filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00008.png filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00009.png filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00010.png filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00011.png filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00012.png filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00013.png filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00014.png filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00015.png filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00016.png filter=lfs diff=lfs merge=lfs -text
|
| 53 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00017.png filter=lfs diff=lfs merge=lfs -text
|
| 54 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00018.png filter=lfs diff=lfs merge=lfs -text
|
| 55 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00019.png filter=lfs diff=lfs merge=lfs -text
|
| 56 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00020.png filter=lfs diff=lfs merge=lfs -text
|
| 57 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00021.png filter=lfs diff=lfs merge=lfs -text
|
| 58 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00022.png filter=lfs diff=lfs merge=lfs -text
|
| 59 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00023.png filter=lfs diff=lfs merge=lfs -text
|
| 60 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00024.png filter=lfs diff=lfs merge=lfs -text
|
| 61 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_short_timeout/frames/frame_00000.png filter=lfs diff=lfs merge=lfs -text
|
| 62 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_short_timeout/frames/frame_00001.png filter=lfs diff=lfs merge=lfs -text
|
| 63 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_short_timeout/frames/frame_00002.png filter=lfs diff=lfs merge=lfs -text
|
| 64 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_short_timeout/frames/frame_00003.png filter=lfs diff=lfs merge=lfs -text
|
| 65 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_short_timeout/frames/frame_00004.png filter=lfs diff=lfs merge=lfs -text
|
| 66 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_short_timeout/frames/frame_00005.png filter=lfs diff=lfs merge=lfs -text
|
| 67 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_short_timeout/frames/frame_00006.png filter=lfs diff=lfs merge=lfs -text
|
| 68 |
+
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_short_timeout/frames/frame_00007.png filter=lfs diff=lfs merge=lfs -text
|
| 69 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00000.png filter=lfs diff=lfs merge=lfs -text
|
| 70 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00001.png filter=lfs diff=lfs merge=lfs -text
|
| 71 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00002.png filter=lfs diff=lfs merge=lfs -text
|
| 72 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00003.png filter=lfs diff=lfs merge=lfs -text
|
| 73 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00004.png filter=lfs diff=lfs merge=lfs -text
|
| 74 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00005.png filter=lfs diff=lfs merge=lfs -text
|
| 75 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00006.png filter=lfs diff=lfs merge=lfs -text
|
| 76 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00007.png filter=lfs diff=lfs merge=lfs -text
|
| 77 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00008.png filter=lfs diff=lfs merge=lfs -text
|
| 78 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00009.png filter=lfs diff=lfs merge=lfs -text
|
| 79 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00010.png filter=lfs diff=lfs merge=lfs -text
|
| 80 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00011.png filter=lfs diff=lfs merge=lfs -text
|
| 81 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00012.png filter=lfs diff=lfs merge=lfs -text
|
| 82 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00013.png filter=lfs diff=lfs merge=lfs -text
|
| 83 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00014.png filter=lfs diff=lfs merge=lfs -text
|
| 84 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00015.png filter=lfs diff=lfs merge=lfs -text
|
| 85 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00016.png filter=lfs diff=lfs merge=lfs -text
|
| 86 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00017.png filter=lfs diff=lfs merge=lfs -text
|
| 87 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00018.png filter=lfs diff=lfs merge=lfs -text
|
| 88 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00019.png filter=lfs diff=lfs merge=lfs -text
|
| 89 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00020.png filter=lfs diff=lfs merge=lfs -text
|
| 90 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00021.png filter=lfs diff=lfs merge=lfs -text
|
| 91 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00022.png filter=lfs diff=lfs merge=lfs -text
|
| 92 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00023.png filter=lfs diff=lfs merge=lfs -text
|
| 93 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove/frames/frame_00024.png filter=lfs diff=lfs merge=lfs -text
|
| 94 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00000.png filter=lfs diff=lfs merge=lfs -text
|
| 95 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00001.png filter=lfs diff=lfs merge=lfs -text
|
| 96 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00002.png filter=lfs diff=lfs merge=lfs -text
|
| 97 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00003.png filter=lfs diff=lfs merge=lfs -text
|
| 98 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00004.png filter=lfs diff=lfs merge=lfs -text
|
| 99 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00005.png filter=lfs diff=lfs merge=lfs -text
|
| 100 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00006.png filter=lfs diff=lfs merge=lfs -text
|
| 101 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00007.png filter=lfs diff=lfs merge=lfs -text
|
| 102 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00008.png filter=lfs diff=lfs merge=lfs -text
|
| 103 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00009.png filter=lfs diff=lfs merge=lfs -text
|
| 104 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00010.png filter=lfs diff=lfs merge=lfs -text
|
| 105 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00011.png filter=lfs diff=lfs merge=lfs -text
|
| 106 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00012.png filter=lfs diff=lfs merge=lfs -text
|
| 107 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00013.png filter=lfs diff=lfs merge=lfs -text
|
| 108 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00014.png filter=lfs diff=lfs merge=lfs -text
|
| 109 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00015.png filter=lfs diff=lfs merge=lfs -text
|
| 110 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00016.png filter=lfs diff=lfs merge=lfs -text
|
| 111 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00017.png filter=lfs diff=lfs merge=lfs -text
|
| 112 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00018.png filter=lfs diff=lfs merge=lfs -text
|
| 113 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00019.png filter=lfs diff=lfs merge=lfs -text
|
| 114 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00020.png filter=lfs diff=lfs merge=lfs -text
|
| 115 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00021.png filter=lfs diff=lfs merge=lfs -text
|
| 116 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00022.png filter=lfs diff=lfs merge=lfs -text
|
| 117 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00023.png filter=lfs diff=lfs merge=lfs -text
|
| 118 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer/frames/frame_00024.png filter=lfs diff=lfs merge=lfs -text
|
| 119 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00000.png filter=lfs diff=lfs merge=lfs -text
|
| 120 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00001.png filter=lfs diff=lfs merge=lfs -text
|
| 121 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00002.png filter=lfs diff=lfs merge=lfs -text
|
| 122 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00003.png filter=lfs diff=lfs merge=lfs -text
|
| 123 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00004.png filter=lfs diff=lfs merge=lfs -text
|
| 124 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00005.png filter=lfs diff=lfs merge=lfs -text
|
| 125 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00006.png filter=lfs diff=lfs merge=lfs -text
|
| 126 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00007.png filter=lfs diff=lfs merge=lfs -text
|
| 127 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00008.png filter=lfs diff=lfs merge=lfs -text
|
| 128 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00009.png filter=lfs diff=lfs merge=lfs -text
|
| 129 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00010.png filter=lfs diff=lfs merge=lfs -text
|
| 130 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00011.png filter=lfs diff=lfs merge=lfs -text
|
| 131 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00012.png filter=lfs diff=lfs merge=lfs -text
|
| 132 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00013.png filter=lfs diff=lfs merge=lfs -text
|
| 133 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00014.png filter=lfs diff=lfs merge=lfs -text
|
| 134 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00015.png filter=lfs diff=lfs merge=lfs -text
|
| 135 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00016.png filter=lfs diff=lfs merge=lfs -text
|
| 136 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00017.png filter=lfs diff=lfs merge=lfs -text
|
| 137 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00018.png filter=lfs diff=lfs merge=lfs -text
|
| 138 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00019.png filter=lfs diff=lfs merge=lfs -text
|
| 139 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00020.png filter=lfs diff=lfs merge=lfs -text
|
| 140 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00021.png filter=lfs diff=lfs merge=lfs -text
|
| 141 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00022.png filter=lfs diff=lfs merge=lfs -text
|
| 142 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00023.png filter=lfs diff=lfs merge=lfs -text
|
| 143 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave/frames/frame_00024.png filter=lfs diff=lfs merge=lfs -text
|
| 144 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00000.png filter=lfs diff=lfs merge=lfs -text
|
| 145 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00001.png filter=lfs diff=lfs merge=lfs -text
|
| 146 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00002.png filter=lfs diff=lfs merge=lfs -text
|
| 147 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00003.png filter=lfs diff=lfs merge=lfs -text
|
| 148 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00004.png filter=lfs diff=lfs merge=lfs -text
|
| 149 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00005.png filter=lfs diff=lfs merge=lfs -text
|
| 150 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00006.png filter=lfs diff=lfs merge=lfs -text
|
| 151 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00007.png filter=lfs diff=lfs merge=lfs -text
|
| 152 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00008.png filter=lfs diff=lfs merge=lfs -text
|
| 153 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00009.png filter=lfs diff=lfs merge=lfs -text
|
| 154 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00010.png filter=lfs diff=lfs merge=lfs -text
|
| 155 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00011.png filter=lfs diff=lfs merge=lfs -text
|
| 156 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00012.png filter=lfs diff=lfs merge=lfs -text
|
| 157 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00013.png filter=lfs diff=lfs merge=lfs -text
|
| 158 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00014.png filter=lfs diff=lfs merge=lfs -text
|
| 159 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00015.png filter=lfs diff=lfs merge=lfs -text
|
| 160 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00016.png filter=lfs diff=lfs merge=lfs -text
|
| 161 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00017.png filter=lfs diff=lfs merge=lfs -text
|
| 162 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00018.png filter=lfs diff=lfs merge=lfs -text
|
| 163 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00019.png filter=lfs diff=lfs merge=lfs -text
|
| 164 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00020.png filter=lfs diff=lfs merge=lfs -text
|
| 165 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00021.png filter=lfs diff=lfs merge=lfs -text
|
| 166 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00022.png filter=lfs diff=lfs merge=lfs -text
|
| 167 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00023.png filter=lfs diff=lfs merge=lfs -text
|
| 168 |
+
outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove/frames/frame_00024.png filter=lfs diff=lfs merge=lfs -text
|
| 169 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00000.png filter=lfs diff=lfs merge=lfs -text
|
| 170 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00001.png filter=lfs diff=lfs merge=lfs -text
|
| 171 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00002.png filter=lfs diff=lfs merge=lfs -text
|
| 172 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00003.png filter=lfs diff=lfs merge=lfs -text
|
| 173 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00004.png filter=lfs diff=lfs merge=lfs -text
|
| 174 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00005.png filter=lfs diff=lfs merge=lfs -text
|
| 175 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00006.png filter=lfs diff=lfs merge=lfs -text
|
| 176 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00007.png filter=lfs diff=lfs merge=lfs -text
|
| 177 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00008.png filter=lfs diff=lfs merge=lfs -text
|
| 178 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00009.png filter=lfs diff=lfs merge=lfs -text
|
| 179 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00010.png filter=lfs diff=lfs merge=lfs -text
|
| 180 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00011.png filter=lfs diff=lfs merge=lfs -text
|
| 181 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00012.png filter=lfs diff=lfs merge=lfs -text
|
| 182 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00013.png filter=lfs diff=lfs merge=lfs -text
|
| 183 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00014.png filter=lfs diff=lfs merge=lfs -text
|
| 184 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00015.png filter=lfs diff=lfs merge=lfs -text
|
| 185 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00016.png filter=lfs diff=lfs merge=lfs -text
|
| 186 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00017.png filter=lfs diff=lfs merge=lfs -text
|
| 187 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00018.png filter=lfs diff=lfs merge=lfs -text
|
| 188 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00019.png filter=lfs diff=lfs merge=lfs -text
|
| 189 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00020.png filter=lfs diff=lfs merge=lfs -text
|
| 190 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00021.png filter=lfs diff=lfs merge=lfs -text
|
| 191 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00022.png filter=lfs diff=lfs merge=lfs -text
|
| 192 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00023.png filter=lfs diff=lfs merge=lfs -text
|
| 193 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket/frames/frame_00024.png filter=lfs diff=lfs merge=lfs -text
|
| 194 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00000.png filter=lfs diff=lfs merge=lfs -text
|
| 195 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00001.png filter=lfs diff=lfs merge=lfs -text
|
| 196 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00002.png filter=lfs diff=lfs merge=lfs -text
|
| 197 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00003.png filter=lfs diff=lfs merge=lfs -text
|
| 198 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00004.png filter=lfs diff=lfs merge=lfs -text
|
| 199 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00005.png filter=lfs diff=lfs merge=lfs -text
|
| 200 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00006.png filter=lfs diff=lfs merge=lfs -text
|
| 201 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00007.png filter=lfs diff=lfs merge=lfs -text
|
| 202 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00008.png filter=lfs diff=lfs merge=lfs -text
|
| 203 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00009.png filter=lfs diff=lfs merge=lfs -text
|
| 204 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00010.png filter=lfs diff=lfs merge=lfs -text
|
| 205 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00011.png filter=lfs diff=lfs merge=lfs -text
|
| 206 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00012.png filter=lfs diff=lfs merge=lfs -text
|
| 207 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00013.png filter=lfs diff=lfs merge=lfs -text
|
| 208 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00014.png filter=lfs diff=lfs merge=lfs -text
|
| 209 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00015.png filter=lfs diff=lfs merge=lfs -text
|
| 210 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00016.png filter=lfs diff=lfs merge=lfs -text
|
| 211 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00017.png filter=lfs diff=lfs merge=lfs -text
|
| 212 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00018.png filter=lfs diff=lfs merge=lfs -text
|
| 213 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00019.png filter=lfs diff=lfs merge=lfs -text
|
| 214 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00020.png filter=lfs diff=lfs merge=lfs -text
|
| 215 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00021.png filter=lfs diff=lfs merge=lfs -text
|
| 216 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00022.png filter=lfs diff=lfs merge=lfs -text
|
| 217 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00023.png filter=lfs diff=lfs merge=lfs -text
|
| 218 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket/frames/frame_00024.png filter=lfs diff=lfs merge=lfs -text
|
| 219 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00000.png filter=lfs diff=lfs merge=lfs -text
|
| 220 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00001.png filter=lfs diff=lfs merge=lfs -text
|
| 221 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00002.png filter=lfs diff=lfs merge=lfs -text
|
| 222 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00003.png filter=lfs diff=lfs merge=lfs -text
|
| 223 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00004.png filter=lfs diff=lfs merge=lfs -text
|
| 224 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00005.png filter=lfs diff=lfs merge=lfs -text
|
| 225 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00006.png filter=lfs diff=lfs merge=lfs -text
|
| 226 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00007.png filter=lfs diff=lfs merge=lfs -text
|
| 227 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00008.png filter=lfs diff=lfs merge=lfs -text
|
| 228 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00009.png filter=lfs diff=lfs merge=lfs -text
|
| 229 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00010.png filter=lfs diff=lfs merge=lfs -text
|
| 230 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00011.png filter=lfs diff=lfs merge=lfs -text
|
| 231 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00012.png filter=lfs diff=lfs merge=lfs -text
|
| 232 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00013.png filter=lfs diff=lfs merge=lfs -text
|
| 233 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00014.png filter=lfs diff=lfs merge=lfs -text
|
| 234 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00015.png filter=lfs diff=lfs merge=lfs -text
|
| 235 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00016.png filter=lfs diff=lfs merge=lfs -text
|
| 236 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00017.png filter=lfs diff=lfs merge=lfs -text
|
| 237 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00018.png filter=lfs diff=lfs merge=lfs -text
|
| 238 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00019.png filter=lfs diff=lfs merge=lfs -text
|
| 239 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00020.png filter=lfs diff=lfs merge=lfs -text
|
| 240 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00021.png filter=lfs diff=lfs merge=lfs -text
|
| 241 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00022.png filter=lfs diff=lfs merge=lfs -text
|
| 242 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00023.png filter=lfs diff=lfs merge=lfs -text
|
| 243 |
+
outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates/frames/frame_00024.png filter=lfs diff=lfs merge=lfs -text
|
| 244 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00000.png filter=lfs diff=lfs merge=lfs -text
|
| 245 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00001.png filter=lfs diff=lfs merge=lfs -text
|
| 246 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00002.png filter=lfs diff=lfs merge=lfs -text
|
| 247 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00003.png filter=lfs diff=lfs merge=lfs -text
|
| 248 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00004.png filter=lfs diff=lfs merge=lfs -text
|
| 249 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00005.png filter=lfs diff=lfs merge=lfs -text
|
| 250 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00006.png filter=lfs diff=lfs merge=lfs -text
|
| 251 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00007.png filter=lfs diff=lfs merge=lfs -text
|
| 252 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00008.png filter=lfs diff=lfs merge=lfs -text
|
| 253 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00009.png filter=lfs diff=lfs merge=lfs -text
|
| 254 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00010.png filter=lfs diff=lfs merge=lfs -text
|
| 255 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00011.png filter=lfs diff=lfs merge=lfs -text
|
| 256 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00012.png filter=lfs diff=lfs merge=lfs -text
|
| 257 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00013.png filter=lfs diff=lfs merge=lfs -text
|
| 258 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00014.png filter=lfs diff=lfs merge=lfs -text
|
| 259 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00015.png filter=lfs diff=lfs merge=lfs -text
|
| 260 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00016.png filter=lfs diff=lfs merge=lfs -text
|
| 261 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00017.png filter=lfs diff=lfs merge=lfs -text
|
| 262 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00018.png filter=lfs diff=lfs merge=lfs -text
|
| 263 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00019.png filter=lfs diff=lfs merge=lfs -text
|
| 264 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00020.png filter=lfs diff=lfs merge=lfs -text
|
| 265 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00021.png filter=lfs diff=lfs merge=lfs -text
|
| 266 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00022.png filter=lfs diff=lfs merge=lfs -text
|
| 267 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00023.png filter=lfs diff=lfs merge=lfs -text
|
| 268 |
+
outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy/frames/frame_00024.png filter=lfs diff=lfs merge=lfs -text
|
| 269 |
+
outputs/libero_smoke_tests/20260512_122756/review_montage.mp4 filter=lfs diff=lfs merge=lfs -text
|
LICENSE
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to the Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by the Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding any notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 179 |
+
|
| 180 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 181 |
+
you may not use this file except in compliance with the License.
|
| 182 |
+
You may obtain a copy of the License at
|
| 183 |
+
|
| 184 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 185 |
+
|
| 186 |
+
Unless required by applicable law or agreed to in writing, software
|
| 187 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 188 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 189 |
+
See the License for the specific language governing permissions and
|
| 190 |
+
limitations under the License.
|
NOTICE
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
vla-sae-libero
|
| 2 |
+
==============
|
| 3 |
+
|
| 4 |
+
This repository contains tooling and run artifacts that build on, wrap, and run:
|
| 5 |
+
|
| 6 |
+
NVIDIA Isaac-GR00T — https://github.com/NVIDIA/Isaac-GR00T
|
| 7 |
+
Copyright (c) NVIDIA CORPORATION & AFFILIATES.
|
| 8 |
+
Licensed under the Apache License, Version 2.0 (see LICENSE).
|
| 9 |
+
|
| 10 |
+
The following were authored as additions on top of that codebase and are
|
| 11 |
+
likewise released under the Apache License, Version 2.0:
|
| 12 |
+
|
| 13 |
+
examples/LIBERO/smoke_tests/ (scenarios_10.yaml, run_10_smoke_tests.py,
|
| 14 |
+
_libero_rollout_worker.py, visualize_smoke_run.py,
|
| 15 |
+
make_video_montage.py, review_smoke_tests.py,
|
| 16 |
+
ACTIVATION_HOOK_NOTES.md, README.md)
|
| 17 |
+
tests/test_libero_smoke_tests.py
|
| 18 |
+
|
| 19 |
+
NOT redistributed in this repository:
|
| 20 |
+
- the NVIDIA Isaac-GR00T source tree (obtain from the URL above);
|
| 21 |
+
- the model checkpoint nvidia/GR00T-N1.7-LIBERO
|
| 22 |
+
(https://huggingface.co/nvidia/GR00T-N1.7-LIBERO);
|
| 23 |
+
- the gated VLM backbone nvidia/Cosmos-Reason2-2B
|
| 24 |
+
(https://huggingface.co/nvidia/Cosmos-Reason2-2B);
|
| 25 |
+
- any Python virtual environments or third-party dependencies.
|
| 26 |
+
|
| 27 |
+
The files under outputs/ are artifacts (rendered videos, frames, recorded
|
| 28 |
+
action chunks, logs, plots, HTML reports, summaries) produced by running the
|
| 29 |
+
nvidia/GR00T-N1.7-LIBERO model in the LIBERO simulator on CPU/GPU; they contain
|
| 30 |
+
no model weights and no credentials.
|
README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
pipeline_tag: robotics
|
| 6 |
+
tags:
|
| 7 |
+
- robotics
|
| 8 |
+
- vla
|
| 9 |
+
- vision-language-action
|
| 10 |
+
- libero
|
| 11 |
+
- gr00t
|
| 12 |
+
- isaac-gr00t
|
| 13 |
+
- imitation-learning
|
| 14 |
+
- sae
|
| 15 |
+
- mechanistic-interpretability
|
| 16 |
+
- simulation
|
| 17 |
+
pretty_name: "VLA-SAE — GR00T-N1.7-LIBERO LIBERO smoke-test tooling & run artifacts"
|
| 18 |
+
---
|
| 19 |
+
|
| 20 |
+
# vla-sae-libero — LIBERO smoke-test tooling & run artifacts for `nvidia/GR00T-N1.7-LIBERO`
|
| 21 |
+
|
| 22 |
+
This repo holds **only** the smoke-test *tooling* and the *artifacts* of one completed run from deploying
|
| 23 |
+
**[`nvidia/GR00T-N1.7-LIBERO`](https://huggingface.co/nvidia/GR00T-N1.7-LIBERO)** (the `libero_10` checkpoint)
|
| 24 |
+
and running **10 short, simulation-only LIBERO rollouts** to verify the deploy/eval plumbing — a stepping
|
| 25 |
+
stone toward activation-capture / SAE work on the GR00T VLA.
|
| 26 |
+
|
| 27 |
+
> ## Built on NVIDIA Isaac-GR00T — *not redistributed here*
|
| 28 |
+
> The tooling in this repo runs **on top of** [**NVIDIA Isaac-GR00T**](https://github.com/NVIDIA/Isaac-GR00T)
|
| 29 |
+
> (Apache-2.0, © NVIDIA CORPORATION & AFFILIATES). **This repo does NOT contain the upstream Isaac-GR00T
|
| 30 |
+
> source tree, the model weights, or the gated VLM backbone.** To use the tooling: clone Isaac-GR00T, install
|
| 31 |
+
> its `gr00t` package, drop `examples/LIBERO/smoke_tests/` (and `tests/test_libero_smoke_tests.py`) into the
|
| 32 |
+
> checkout, download the checkpoint, and follow the steps below.
|
| 33 |
+
>
|
| 34 |
+
> Specifically, **not included** (get them from upstream):
|
| 35 |
+
> - the Isaac-GR00T source tree (`gr00t/`, `examples/` except `examples/LIBERO/smoke_tests/`, `getting_started/`, `scripts/`, `docker/`, `demo_data/`, `media/`, …) — <https://github.com/NVIDIA/Isaac-GR00T>
|
| 36 |
+
> - the model checkpoint `nvidia/GR00T-N1.7-LIBERO` (~6.5 GB) — <https://huggingface.co/nvidia/GR00T-N1.7-LIBERO>
|
| 37 |
+
> - the **gated** VLM backbone `nvidia/Cosmos-Reason2-2B` — <https://huggingface.co/nvidia/Cosmos-Reason2-2B>
|
| 38 |
+
> - Python virtualenvs (`.venv/`, `gr00t/eval/sim/LIBERO/libero_uv/`) and the LIBERO submodule (`external_dependencies/LIBERO/`)
|
| 39 |
+
|
| 40 |
+
## Contents
|
| 41 |
+
|
| 42 |
+
```
|
| 43 |
+
examples/LIBERO/smoke_tests/ # the smoke-test tooling
|
| 44 |
+
├── scenarios_10.yaml # 10-scenario manifest (8 normal + 2 abnormal_probe)
|
| 45 |
+
├── run_10_smoke_tests.py # runner: validates model/server, runs each scenario via the official eval path, writes summaries
|
| 46 |
+
├── _libero_rollout_worker.py # single-episode worker (runs in the LIBERO venv): records action chunks, optional sim-only probes
|
| 47 |
+
├── visualize_smoke_run.py # builds visual_report.html + per-scenario action plots + visual_summary.{csv,json}
|
| 48 |
+
├── make_video_montage.py # builds review_montage.mp4 + per-scenario captioned clips + review_playlist.html (needs ffmpeg)
|
| 49 |
+
├── review_smoke_tests.py # CLI: list/play the rollouts of a run
|
| 50 |
+
├── ACTIVATION_HOOK_NOTES.md # where to insert future SAE activation capture (module names, hook points)
|
| 51 |
+
└── README.md # setup & run instructions
|
| 52 |
+
|
| 53 |
+
tests/test_libero_smoke_tests.py # lightweight tests for the manifest + summary writer
|
| 54 |
+
|
| 55 |
+
outputs/libero_smoke_tests/20260512_122756/ # the completed run (10/10 rollouts OK, 0 errors; success=false everywhere — these are 50-step smoke rollouts, not a benchmark)
|
| 56 |
+
├── summary.json, summary.md # run-level results + the printed comparison table
|
| 57 |
+
├── visual_report.html # self-contained report: embedded videos + plots + comparison table
|
| 58 |
+
├── visual_summary.csv, visual_summary.json # per-scenario action-stat table
|
| 59 |
+
├── review_montage.mp4 # ~22 s: all 10 scenarios back-to-back with captions
|
| 60 |
+
├── review_clips/<scenario_id>.mp4 # 10 short captioned clips
|
| 61 |
+
├── review_playlist.html # the montage + each clip + metadata + links
|
| 62 |
+
├── plots/<scenario_id>/ # action_norm / action_mean_per_dof / gripper_over_time / action_delta_norm (.png)
|
| 63 |
+
└── <scenario_id>/ # ×10
|
| 64 |
+
├── video.mp4 # rendered rollout (agentview | wrist, 512×256)
|
| 65 |
+
├── frames/ # decoded PNG frames (25 each; 8 for the short-timeout probe)
|
| 66 |
+
├── actions.npy # recorded action chunks, shape (n_calls, 1, 16, 7) float32 [DoF column order: gripper, pitch, roll, x, y, yaw, z]
|
| 67 |
+
├── metadata.json, rollout_summary.json # scenario config + results
|
| 68 |
+
└── stdout.log, stderr.log # the worker subprocess logs
|
| 69 |
+
|
| 70 |
+
outputs/_setup_logs/ # deployment record: uv install / LIBERO setup / server / smoke-run logs (no secrets)
|
| 71 |
+
|
| 72 |
+
LICENSE # Apache-2.0 (same license as upstream Isaac-GR00T)
|
| 73 |
+
NOTICE # attribution
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
## The run, in one line
|
| 77 |
+
|
| 78 |
+
10 LIBERO simulation rollouts of `nvidia/GR00T-N1.7-LIBERO` (`libero_10`), `max_episode_steps = 50` (16 for the short-timeout probe), `n_action_steps = 8`, `n_envs = 1`, **no physical hardware**. 8 `normal` long-horizon tasks + 2 `abnormal_probe` (mild Gaussian observation noise; deliberately shortened timeout). All 10 ran cleanly with actions + video saved; `success = false` everywhere — a 50-step rollout cannot complete these long-horizon tasks, and that's the point: this verifies the deploy/serve/client/sim/render/save plumbing, not task performance.
|
| 79 |
+
|
| 80 |
+
## Reproduce
|
| 81 |
+
|
| 82 |
+
Full instructions: [`examples/LIBERO/smoke_tests/README.md`](examples/LIBERO/smoke_tests/README.md). Summary:
|
| 83 |
+
|
| 84 |
+
```bash
|
| 85 |
+
# 0. start from a clone of NVIDIA Isaac-GR00T and copy this repo's files into it
|
| 86 |
+
git clone https://github.com/NVIDIA/Isaac-GR00T.git && cd Isaac-GR00T
|
| 87 |
+
git submodule update --init external_dependencies/LIBERO
|
| 88 |
+
# ... then place examples/LIBERO/smoke_tests/ and tests/test_libero_smoke_tests.py from THIS repo here ...
|
| 89 |
+
|
| 90 |
+
# install gr00t (uv sync, or the minimal recipe in examples/LIBERO/smoke_tests/README.md), then:
|
| 91 |
+
sudo apt install libegl1-mesa-dev libglu1-mesa cmake && bash gr00t/eval/sim/LIBERO/setup_libero.sh
|
| 92 |
+
uv run hf download nvidia/GR00T-N1.7-LIBERO --include "libero_10/*" --local-dir checkpoints/GR00T-N1.7-LIBERO
|
| 93 |
+
# request access to https://huggingface.co/nvidia/Cosmos-Reason2-2B and export HF_TOKEN=hf_...
|
| 94 |
+
|
| 95 |
+
# 1. server (terminal 1) — this pulls the gated nvidia/Cosmos-Reason2-2B backbone
|
| 96 |
+
uv run python gr00t/eval/run_gr00t_server.py \
|
| 97 |
+
--model-path checkpoints/GR00T-N1.7-LIBERO/libero_10 \
|
| 98 |
+
--embodiment-tag LIBERO_PANDA --use-sim-policy-wrapper
|
| 99 |
+
|
| 100 |
+
# 2. smoke tests (terminal 2)
|
| 101 |
+
uv run python examples/LIBERO/smoke_tests/run_10_smoke_tests.py \
|
| 102 |
+
--model-path checkpoints/GR00T-N1.7-LIBERO/libero_10 --host 127.0.0.1 --port 5555 \
|
| 103 |
+
--manifest examples/LIBERO/smoke_tests/scenarios_10.yaml --output-dir outputs/libero_smoke_tests \
|
| 104 |
+
--max-episode-steps 50 --save-video --render
|
| 105 |
+
|
| 106 |
+
# 3. review artifacts (no GPU, no rerun)
|
| 107 |
+
python examples/LIBERO/smoke_tests/visualize_smoke_run.py --run-dir outputs/libero_smoke_tests/<ts> --open
|
| 108 |
+
python examples/LIBERO/smoke_tests/make_video_montage.py --run-dir outputs/libero_smoke_tests/<ts> --mode sequential --open
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
## Next: activation capture / SAE
|
| 112 |
+
|
| 113 |
+
See [`examples/LIBERO/smoke_tests/ACTIVATION_HOOK_NOTES.md`](examples/LIBERO/smoke_tests/ACTIVATION_HOOK_NOTES.md). Suggested first step: register a `forward_hook` on `policy.model.backbone` (the `Qwen3Backbone` — its `forward` returns `backbone_features`, the last Qwen3-VL hidden state = the fused image+instruction representation that conditions action generation), dump that per `get_action` call alongside the already-saved `actions.npy` + frames, and train an SAE on it.
|
| 114 |
+
|
| 115 |
+
## License & attribution
|
| 116 |
+
|
| 117 |
+
This repo is released under **Apache-2.0**, matching upstream **NVIDIA Isaac-GR00T**
|
| 118 |
+
(<https://github.com/NVIDIA/Isaac-GR00T>, © NVIDIA CORPORATION & AFFILIATES) — see `LICENSE` and `NOTICE`.
|
| 119 |
+
The added tooling (`examples/LIBERO/smoke_tests/`, `tests/test_libero_smoke_tests.py`) is Apache-2.0 and
|
| 120 |
+
imports/wraps the upstream `gr00t` package. The artifacts under `outputs/` are outputs of the
|
| 121 |
+
`nvidia/GR00T-N1.7-LIBERO` model run in LIBERO simulation; **no model weights or upstream source files
|
| 122 |
+
(other than this `LICENSE`) are redistributed here.**
|
examples/LIBERO/smoke_tests/ACTIVATION_HOOK_NOTES.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Notes for future SAE activation capture (GR00T-N1.7-LIBERO)
|
| 2 |
+
|
| 3 |
+
> Status: **not implemented yet.** This file just records the hook points found
|
| 4 |
+
> while wiring up the LIBERO smoke tests, so a later change can add SAE
|
| 5 |
+
> activation collection with minimal surface area. Nothing here changes model
|
| 6 |
+
> behaviour.
|
| 7 |
+
|
| 8 |
+
## Where actions are produced (the model call chain)
|
| 9 |
+
|
| 10 |
+
1. `gr00t/eval/rollout_policy.py :: run_rollout_gymnasium_policy`
|
| 11 |
+
calls `policy.get_action(observations)` once per action chunk.
|
| 12 |
+
- For the smoke tests the `policy` is a `PolicyClient`
|
| 13 |
+
(`gr00t/policy/server_client.py`) talking over ZMQ to a server, so the
|
| 14 |
+
*actual* model lives in the server process. The
|
| 15 |
+
`examples/LIBERO/smoke_tests/_libero_rollout_worker.py` already wraps
|
| 16 |
+
`PolicyClient.get_action` to record the returned action chunks
|
| 17 |
+
(`actions.npy`); that wrapper is the cheapest place to record
|
| 18 |
+
*input/output* of the policy from the client side, but it cannot see
|
| 19 |
+
internal activations.
|
| 20 |
+
|
| 21 |
+
2. Server side: `gr00t/eval/run_gr00t_server.py` builds a
|
| 22 |
+
`Gr00tPolicy` (`gr00t/policy/gr00t_policy.py`), optionally wrapped in
|
| 23 |
+
`Gr00tSimPolicyWrapper`. The endpoint `get_action` →
|
| 24 |
+
`Gr00tPolicy._get_action()` (≈ line 371):
|
| 25 |
+
- builds `collated_inputs` from the observation (images + proprio state +
|
| 26 |
+
language instruction), casts to bf16, then:
|
| 27 |
+
- `model_pred = self.model.get_action(**collated_inputs)` ← **the forward pass**
|
| 28 |
+
- `normalized_action = model_pred["action_pred"]`, then
|
| 29 |
+
`self.processor.decode_action(...)` → physical-unit action dict returned.
|
| 30 |
+
|
| 31 |
+
3. The model: `gr00t/model/gr00t_n1d7/gr00t_n1d7.py :: GR00T_N1_7` (registered
|
| 32 |
+
`Gr00tN1d7`). `GR00T_N1_7.get_action(inputs)` (≈ line 589):
|
| 33 |
+
```
|
| 34 |
+
backbone_inputs, action_inputs = self.prepare_input(inputs)
|
| 35 |
+
backbone_outputs = self.backbone(backbone_inputs) # VLM
|
| 36 |
+
action_outputs = self.action_head.get_action(backbone_outputs, action_inputs, options)
|
| 37 |
+
return action_outputs # {"action_pred": ...}
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
## Likely hook points / module names
|
| 41 |
+
|
| 42 |
+
Top-level model attributes (`policy.model` on the server, an
|
| 43 |
+
`nn.Module` of class `Gr00tN1d7` / `GR00T_N1_7`):
|
| 44 |
+
|
| 45 |
+
| Attribute | Class / file | What it computes |
|
| 46 |
+
|---|---|---|
|
| 47 |
+
| `model.backbone` | `Qwen3Backbone` (`gr00t/model/modules/qwen3_backbone.py`) | VLM over images + text instruction. Wraps HF `Qwen3VLForConditionalGeneration` at `model.backbone.model`; transformer decoder layers at `model.backbone.model.language_model.layers[...]` (truncated to `select_layer`); vision tower at `model.backbone.model.visual` (name may vary with the HF Qwen3-VL version). `forward()` runs with `output_hidden_states=True`, takes `hidden_states[-1]`, and returns `BatchFeature({"backbone_features": <Tensor [B, seq, hidden]>, "backbone_attention_mask": ...})`. |
|
| 48 |
+
| `model.action_head` | `Gr00tN1d7ActionHead` (`gr00t/model/gr00t_n1d7/gr00t_n1d7.py`, ≈ line 38) | Flow-matching / diffusion action decoder. Inner net `model.action_head.model` is a `DiT` or `AlternateVLDiT` (`gr00t/model/modules/dit.py`, `gr00t/model/modules/flowmatching_modules.py`). Has `get_action(backbone_output, action_input, options)` and `get_action_with_features(...)` (≈ line 312) which is the natural place to also surface intermediate features. |
|
| 49 |
+
| `model.collator` | `Gr00tN1d7DataCollator` (`gr00t/model/gr00t_n1d7/processing_gr00t_n1d7.py`) | builds VLM batch inputs from `vlm_content`. |
|
| 50 |
+
|
| 51 |
+
**Recommended primary capture site:** the output of `model.backbone` — i.e.
|
| 52 |
+
the tensor stored under key `backbone_features` returned by
|
| 53 |
+
`Qwen3Backbone.forward` (last LLM hidden state, shape `[B, seq_len, hidden]`).
|
| 54 |
+
This is the single fused representation of *image + instruction + (implicit)
|
| 55 |
+
proprioception context* that conditions action generation, so it's the most
|
| 56 |
+
informative single activation to feed an SAE. Secondary sites: per-layer LLM
|
| 57 |
+
hidden states (`outputs.hidden_states[k]` inside `Qwen3Backbone.forward`),
|
| 58 |
+
and the DiT block activations inside `model.action_head.model`.
|
| 59 |
+
|
| 60 |
+
**Cheapest mechanism:** register `torch.nn.Module.register_forward_hook` on
|
| 61 |
+
`policy.model.backbone` (and/or specific `...language_model.layers[k]`) right
|
| 62 |
+
after the policy is constructed in `run_gr00t_server.py`, before
|
| 63 |
+
`server.run()`. Hooks can append detached CPU/float16 tensors to a buffer that
|
| 64 |
+
is flushed to disk per get_action call. No edits to model code required.
|
| 65 |
+
Alternative: subclass `Gr00tPolicy` and override `_get_action` to also stash
|
| 66 |
+
`self.model`'s intermediates.
|
| 67 |
+
|
| 68 |
+
## Where observation / instruction / proprioception enter the model
|
| 69 |
+
|
| 70 |
+
- **LIBERO env → observation dict**: `gr00t/eval/sim/LIBERO/libero_env.py ::
|
| 71 |
+
LiberoEnv._process_observation` produces:
|
| 72 |
+
- `video.image` (agentview 256×256×3), `video.wrist_image` (eye-in-hand),
|
| 73 |
+
- `state.x/y/z/roll/pitch/yaw` (EEF pose from `robot0_eef_pos` + axis-angle
|
| 74 |
+
of `robot0_eef_quat`), `state.gripper` (2-dim `robot0_gripper_qpos`),
|
| 75 |
+
- `annotation.human.action.task_description` (the **language instruction**,
|
| 76 |
+
a fixed string per task).
|
| 77 |
+
- These are batched/temporally-stacked by `MultiStepWrapper`
|
| 78 |
+
(`gr00t/eval/sim/wrapper/multistep_wrapper.py`) and then, in
|
| 79 |
+
`Gr00tPolicy._get_action`, turned into `VLAStepData` via
|
| 80 |
+
`self._to_vla_step_data(obs)` and run through `self.processor(messages)`
|
| 81 |
+
(`Gr00tN1d7Processor`, `gr00t/model/gr00t_n1d7/processing_gr00t_n1d7.py`):
|
| 82 |
+
- images + instruction → `vlm_content` → tokenized/pixel-processed by the
|
| 83 |
+
Qwen3-VL processor → `model.backbone`.
|
| 84 |
+
- proprio `state.*` → `action_inputs` → `model.action_head` (conditioning).
|
| 85 |
+
- The smoke-test worker can optionally perturb the image observations
|
| 86 |
+
*before* `policy.get_action` (`--obs-noise-std`); a future activation-capture
|
| 87 |
+
run could pair clean vs. perturbed activations from the same seeds.
|
| 88 |
+
|
| 89 |
+
## Suggested next step for activation capture
|
| 90 |
+
|
| 91 |
+
1. Add an opt-in flag to `run_gr00t_server.py` (e.g. `--capture-activations
|
| 92 |
+
DIR` / `--capture-layers backbone,llm.20`) that, after building the policy,
|
| 93 |
+
registers forward hooks on the chosen modules and writes one
|
| 94 |
+
`.npz`/`.safetensors` per `get_action` call (keyed by an episode/step id
|
| 95 |
+
passed through `options`).
|
| 96 |
+
2. Have `_libero_rollout_worker.py` thread a stable `(scenario_id, episode,
|
| 97 |
+
step)` tag into `policy.get_action(obs, options=...)` so captured
|
| 98 |
+
activations can be joined back to `actions.npy` and the saved video frames.
|
| 99 |
+
3. Train the SAE on `backbone_features` first (one SAE), then expand to
|
| 100 |
+
per-layer LLM hidden states if needed.
|
examples/LIBERO/smoke_tests/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# LIBERO simulation-only smoke tests for `nvidia/GR00T-N1.7-LIBERO`
|
| 2 |
+
|
| 3 |
+
These scripts deploy the `libero_10` checkpoint of
|
| 4 |
+
[`nvidia/GR00T-N1.7-LIBERO`](https://huggingface.co/nvidia/GR00T-N1.7-LIBERO)
|
| 5 |
+
and run **10 short, simulation-only LIBERO rollouts** to verify the deploy /
|
| 6 |
+
eval plumbing:
|
| 7 |
+
|
| 8 |
+
1. the model checkpoint is present and loads,
|
| 9 |
+
2. the GR00T inference server starts,
|
| 10 |
+
3. the LIBERO rollout client connects to it,
|
| 11 |
+
4. short rollouts run with **no physical robot hardware** (LIBERO sim only),
|
| 12 |
+
5. a video / rendered frames are saved per scenario for human review,
|
| 13 |
+
6. basic rollout metadata + recorded action chunks are saved,
|
| 14 |
+
7. activation hooks can be added later — see [`ACTIVATION_HOOK_NOTES.md`](ACTIVATION_HOOK_NOTES.md).
|
| 15 |
+
|
| 16 |
+
This is **not** a benchmark. `max_episode_steps` is deliberately tiny, so the
|
| 17 |
+
reported success rates are meaningless — only the plumbing matters here. The
|
| 18 |
+
scripts do **not** rewrite the official evaluation path: each scenario runs
|
| 19 |
+
through `gr00t.eval.rollout_policy.run_rollout_gymnasium_policy` via the thin
|
| 20 |
+
`_libero_rollout_worker.py`, which only adds action recording and two optional,
|
| 21 |
+
simulation-only perturbations (Gaussian pixel noise; action-chunk repeat).
|
| 22 |
+
|
| 23 |
+
## Files
|
| 24 |
+
|
| 25 |
+
| File | Purpose |
|
| 26 |
+
|---|---|
|
| 27 |
+
| `scenarios_10.yaml` | the 10-scenario manifest (8 `normal`, 2 `abnormal_probe`) |
|
| 28 |
+
| `run_10_smoke_tests.py` | runner: validates paths/server, runs all scenarios, writes summaries |
|
| 29 |
+
| `_libero_rollout_worker.py` | runs **one** rollout against the server (called by the runner; runs in the LIBERO venv) |
|
| 30 |
+
| `review_smoke_tests.py` | lists scenarios + video paths from a run dir, optionally plays videos |
|
| 31 |
+
| `ACTIVATION_HOOK_NOTES.md` | where to insert future SAE activation capture |
|
| 32 |
+
|
| 33 |
+
Tests live at `tests/test_libero_smoke_tests.py`.
|
| 34 |
+
|
| 35 |
+
## One-time setup
|
| 36 |
+
|
| 37 |
+
```bash
|
| 38 |
+
# 1. Install deps (project venv) — uses uv
|
| 39 |
+
uv sync # or: uv pip install -e . (see note below)
|
| 40 |
+
|
| 41 |
+
# 2. LIBERO simulation env (separate venv; only needed once)
|
| 42 |
+
sudo apt update && sudo apt install libegl1-mesa-dev libglu1-mesa
|
| 43 |
+
bash gr00t/eval/sim/LIBERO/setup_libero.sh
|
| 44 |
+
|
| 45 |
+
# 3. Download the libero_10 checkpoint
|
| 46 |
+
# (HuggingFace does not support nested repo paths directly)
|
| 47 |
+
uv run hf download nvidia/GR00T-N1.7-LIBERO \
|
| 48 |
+
--include "libero_10/config.json" \
|
| 49 |
+
"libero_10/embodiment_id.json" \
|
| 50 |
+
"libero_10/model-*.safetensors" \
|
| 51 |
+
"libero_10/model.safetensors.index.json" \
|
| 52 |
+
"libero_10/processor_config.json" \
|
| 53 |
+
"libero_10/statistics.json" \
|
| 54 |
+
--local-dir checkpoints/GR00T-N1.7-LIBERO
|
| 55 |
+
# If the glob skips config.json, fetch it explicitly:
|
| 56 |
+
uv run hf download nvidia/GR00T-N1.7-LIBERO libero_10/config.json \
|
| 57 |
+
--local-dir checkpoints/GR00T-N1.7-LIBERO
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
> **Note on `uv sync`:** the project `pyproject.toml` pins GPU-deployment-only
|
| 61 |
+
> packages (`flash-attn`, `deepspeed`, `tensorrt-*`) and the lockfile references
|
| 62 |
+
> aarch64 wheels that are stored via git-LFS. On a plain x86 box without git-LFS
|
| 63 |
+
> / CUDA toolkit, `uv sync` can fail on those. They are **not needed for the
|
| 64 |
+
> smoke test** (the model falls back to `sdpa` attention if `flash_attn` is
|
| 65 |
+
> missing). A working minimal install is:
|
| 66 |
+
> ```bash
|
| 67 |
+
> uv venv .venv --python 3.10
|
| 68 |
+
> uv pip install --python .venv/bin/python -e . --no-deps
|
| 69 |
+
> uv pip install --python .venv/bin/python \
|
| 70 |
+
> torch==2.7.1 torchvision==0.22.1 transformers==4.57.3 numpy==1.26.4 \
|
| 71 |
+
> albumentations==1.4.18 av==16.1.0 diffusers==0.35.1 dm-tree lmdb==1.7.5 \
|
| 72 |
+
> msgpack==1.1.0 msgpack-numpy==0.4.8 pandas==2.2.3 peft==0.17.1 termcolor==3.2.0 \
|
| 73 |
+
> tyro==0.9.17 click==8.1.8 datasets==3.6.0 cryptography einops==0.8.1 \
|
| 74 |
+
> gitpython==3.1.46 jsonlines==4.0.0 gymnasium==1.2.2 matplotlib==3.10.1 \
|
| 75 |
+
> omegaconf==2.3.0 scipy==1.15.3 torchcodec==0.4.0 wandb==0.23.0 pyzmq==27.0.1 \
|
| 76 |
+
> "huggingface-hub[cli]" "opencv-python-headless>=4.5,<4.13" safetensors accelerate \
|
| 77 |
+
> sentencepiece protobuf pyyaml tqdm
|
| 78 |
+
> ```
|
| 79 |
+
|
| 80 |
+
## Running the smoke tests
|
| 81 |
+
|
| 82 |
+
**Terminal 1 — start the GR00T inference server:**
|
| 83 |
+
|
| 84 |
+
```bash
|
| 85 |
+
uv run python gr00t/eval/run_gr00t_server.py \
|
| 86 |
+
--model-path checkpoints/GR00T-N1.7-LIBERO/libero_10 \
|
| 87 |
+
--embodiment-tag LIBERO_PANDA \
|
| 88 |
+
--use-sim-policy-wrapper
|
| 89 |
+
```
|
| 90 |
+
|
| 91 |
+
(Server defaults: `--host 0.0.0.0 --port 5555`. This is exactly the command
|
| 92 |
+
documented in `examples/LIBERO/README.md`.)
|
| 93 |
+
|
| 94 |
+
> **Gated backbone:** GR00T-N1.7 uses `nvidia/Cosmos-Reason2-2B` (a Qwen3-VL
|
| 95 |
+
> model) as its VLM backbone, and that repo is **gated** on HuggingFace.
|
| 96 |
+
> Starting the server pulls the base repo's config/processor, so it fails
|
| 97 |
+
> without HF auth. One-time fix:
|
| 98 |
+
> ```bash
|
| 99 |
+
> # 1. request access (one click): https://huggingface.co/nvidia/Cosmos-Reason2-2B
|
| 100 |
+
> # 2. authenticate:
|
| 101 |
+
> export HF_TOKEN=hf_xxx # or: uv run hf auth login
|
| 102 |
+
> ```
|
| 103 |
+
> The `run_10_smoke_tests.py` runner detects this failure mode (when launched
|
| 104 |
+
> with `--start-server`) and prints the same hint.
|
| 105 |
+
|
| 106 |
+
**Terminal 2 — run the 10 smoke tests:**
|
| 107 |
+
|
| 108 |
+
```bash
|
| 109 |
+
uv run python examples/LIBERO/smoke_tests/run_10_smoke_tests.py \
|
| 110 |
+
--model-path checkpoints/GR00T-N1.7-LIBERO/libero_10 \
|
| 111 |
+
--host 127.0.0.1 --port 5555 \
|
| 112 |
+
--manifest examples/LIBERO/smoke_tests/scenarios_10.yaml \
|
| 113 |
+
--output-dir outputs/libero_smoke_tests \
|
| 114 |
+
--max-episode-steps 50 \
|
| 115 |
+
--save-video --render
|
| 116 |
+
```
|
| 117 |
+
|
| 118 |
+
Other flags:
|
| 119 |
+
|
| 120 |
+
- `--start-server` — launch (and later kill) the server from the runner itself,
|
| 121 |
+
so you only need one terminal.
|
| 122 |
+
- `--dry-run` — validate the manifest, model path and (optionally) the server,
|
| 123 |
+
write `metadata.json` per scenario, but run no simulations.
|
| 124 |
+
- `--resume` + `--run-dir <output-dir>/<timestamp>` — re-enter an existing run
|
| 125 |
+
dir and skip scenarios that already produced a `rollout_summary.json`.
|
| 126 |
+
- `--libero-python <path>` — override the LIBERO venv python (default:
|
| 127 |
+
`gr00t/eval/sim/LIBERO/libero_uv/.venv/bin/python`, falls back to the current
|
| 128 |
+
interpreter with a warning).
|
| 129 |
+
|
| 130 |
+
If the model path is missing, or the server is unreachable, or the LIBERO sim
|
| 131 |
+
env is not importable, the runner stops with an actionable error — it does
|
| 132 |
+
**not** fake a successful rollout.
|
| 133 |
+
|
| 134 |
+
## Outputs
|
| 135 |
+
|
| 136 |
+
```
|
| 137 |
+
outputs/libero_smoke_tests/<timestamp>/
|
| 138 |
+
summary.json
|
| 139 |
+
summary.md
|
| 140 |
+
server.log # only if --start-server
|
| 141 |
+
<scenario_id>/
|
| 142 |
+
metadata.json # the manifest entry + resolved params
|
| 143 |
+
rollout_summary.json # env_name, seed, steps, success, reward, ...
|
| 144 |
+
actions.npy # recorded action chunks (if the rollout ran)
|
| 145 |
+
video.mp4 # rendered rollout (if recording succeeded)
|
| 146 |
+
frames/ # decoded PNG frames (only with --render)
|
| 147 |
+
stdout.log / stderr.log # the worker subprocess logs
|
| 148 |
+
```
|
| 149 |
+
|
| 150 |
+
At the end the runner prints a table:
|
| 151 |
+
|
| 152 |
+
```
|
| 153 |
+
scenario_id | label | seed | rollout_started | actions_produced | video_saved | success | output_dir | error_if_any
|
| 154 |
+
```
|
| 155 |
+
|
| 156 |
+
and writes the same to `summary.json` / `summary.md`. Review videos with:
|
| 157 |
+
|
| 158 |
+
```bash
|
| 159 |
+
python examples/LIBERO/smoke_tests/review_smoke_tests.py --run-dir outputs/libero_smoke_tests/<timestamp> --open
|
| 160 |
+
```
|
examples/LIBERO/smoke_tests/_libero_rollout_worker.py
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
| 5 |
+
"""Single-episode LIBERO rollout worker for the smoke-test harness.
|
| 6 |
+
|
| 7 |
+
This is a *thin* wrapper around the official evaluation path
|
| 8 |
+
(``gr00t.eval.rollout_policy.run_rollout_gymnasium_policy``). It exists only
|
| 9 |
+
so that, for one short rollout, we can additionally:
|
| 10 |
+
|
| 11 |
+
* connect to an already-running GR00T policy server (``PolicyClient``),
|
| 12 |
+
* record the action chunks the policy returns (-> ``actions.npy``),
|
| 13 |
+
* optionally apply a tiny, simulation-only observation perturbation
|
| 14 |
+
(Gaussian pixel noise) or repeat the previous action chunk -- these are
|
| 15 |
+
placeholders for future "abnormal" probes, not real anomaly logic,
|
| 16 |
+
* collect the recorded video into the per-scenario output directory.
|
| 17 |
+
|
| 18 |
+
It does **not** modify the official rollout/eval code. It is meant to be run
|
| 19 |
+
*inside the LIBERO uv venv* created by
|
| 20 |
+
``gr00t/eval/sim/LIBERO/setup_libero.sh``, e.g.::
|
| 21 |
+
|
| 22 |
+
gr00t/eval/sim/LIBERO/libero_uv/.venv/bin/python \
|
| 23 |
+
examples/LIBERO/smoke_tests/_libero_rollout_worker.py --help
|
| 24 |
+
|
| 25 |
+
Normally you do not call this directly -- ``run_10_smoke_tests.py`` invokes it.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
from __future__ import annotations
|
| 29 |
+
|
| 30 |
+
import argparse
|
| 31 |
+
import glob
|
| 32 |
+
import json
|
| 33 |
+
import os
|
| 34 |
+
from pathlib import Path
|
| 35 |
+
import shutil
|
| 36 |
+
import sys
|
| 37 |
+
import time
|
| 38 |
+
import traceback
|
| 39 |
+
|
| 40 |
+
# LIBERO renders headlessly via EGL; set this before importing the env modules.
|
| 41 |
+
os.environ.setdefault("MUJOCO_GL", "egl")
|
| 42 |
+
os.environ.setdefault("PYOPENGL_PLATFORM", "egl")
|
| 43 |
+
|
| 44 |
+
import numpy as np
|
| 45 |
+
|
| 46 |
+
RESULT_PREFIX = "SMOKE_RESULT_JSON:"
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def _emit_result(result: dict) -> None:
|
| 50 |
+
"""Print the machine-readable result line the parent runner parses."""
|
| 51 |
+
sys.stdout.flush()
|
| 52 |
+
print(RESULT_PREFIX + " " + json.dumps(result), flush=True)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def _action_to_numpy(action):
|
| 56 |
+
"""Best-effort conversion of a policy action (dict of arrays or array) to ndarray."""
|
| 57 |
+
if isinstance(action, dict):
|
| 58 |
+
# LIBERO action space: action.x, action.y, ..., action.gripper
|
| 59 |
+
keys = sorted(action.keys())
|
| 60 |
+
try:
|
| 61 |
+
parts = [np.asarray(action[k]) for k in keys]
|
| 62 |
+
return np.concatenate(parts, axis=-1)
|
| 63 |
+
except Exception:
|
| 64 |
+
return {k: np.asarray(v) for k, v in action.items()}
|
| 65 |
+
return np.asarray(action)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def main() -> int:
|
| 69 |
+
ap = argparse.ArgumentParser(description="Single LIBERO rollout against a running GR00T server.")
|
| 70 |
+
ap.add_argument("--env-name", required=True)
|
| 71 |
+
ap.add_argument("--host", default="127.0.0.1")
|
| 72 |
+
ap.add_argument("--port", type=int, default=5555)
|
| 73 |
+
ap.add_argument("--max-episode-steps", type=int, default=50)
|
| 74 |
+
ap.add_argument("--n-action-steps", type=int, default=8)
|
| 75 |
+
ap.add_argument("--seed", type=int, default=0)
|
| 76 |
+
ap.add_argument("--out-dir", required=True)
|
| 77 |
+
ap.add_argument("--save-video", dest="save_video", action="store_true", default=True)
|
| 78 |
+
ap.add_argument("--no-save-video", dest="save_video", action="store_false")
|
| 79 |
+
ap.add_argument("--save-frames", action="store_true", default=False,
|
| 80 |
+
help="Also dump decoded video frames into <out-dir>/frames/.")
|
| 81 |
+
ap.add_argument("--obs-noise-std", type=float, default=0.0,
|
| 82 |
+
help="Std-dev of zero-mean Gaussian noise added to image observations "
|
| 83 |
+
"before policy inference (0 = disabled). Simulation-only probe.")
|
| 84 |
+
ap.add_argument("--action-repeat", type=int, default=1,
|
| 85 |
+
help="If >1, re-use the previous action chunk instead of querying the "
|
| 86 |
+
"policy on (k-1)/k of the steps (1 = disabled). Simulation-only probe.")
|
| 87 |
+
ap.add_argument("--timeout-ms", type=int, default=120000,
|
| 88 |
+
help="ZMQ send/recv timeout for the policy client.")
|
| 89 |
+
args = ap.parse_args()
|
| 90 |
+
|
| 91 |
+
out_dir = Path(args.out_dir)
|
| 92 |
+
out_dir.mkdir(parents=True, exist_ok=True)
|
| 93 |
+
video_raw_dir = out_dir / "_video_raw"
|
| 94 |
+
|
| 95 |
+
result: dict = {
|
| 96 |
+
"ok": False,
|
| 97 |
+
"rollout_started": False,
|
| 98 |
+
"actions_produced": False,
|
| 99 |
+
"video_saved": False,
|
| 100 |
+
"success": None,
|
| 101 |
+
"error": None,
|
| 102 |
+
"env_name": args.env_name,
|
| 103 |
+
"seed": args.seed,
|
| 104 |
+
"n_action_calls": 0,
|
| 105 |
+
"episode_length": None,
|
| 106 |
+
"episode_reward": None,
|
| 107 |
+
"video_path": None,
|
| 108 |
+
"actions_path": None,
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
try:
|
| 112 |
+
from gr00t.eval.rollout_policy import (
|
| 113 |
+
MultiStepConfig,
|
| 114 |
+
VideoConfig,
|
| 115 |
+
WrapperConfigs,
|
| 116 |
+
run_rollout_gymnasium_policy,
|
| 117 |
+
)
|
| 118 |
+
from gr00t.eval.sim.env_utils import get_embodiment_tag_from_env_name # noqa: F401
|
| 119 |
+
from gr00t.policy.server_client import PolicyClient
|
| 120 |
+
|
| 121 |
+
try:
|
| 122 |
+
from gr00t.utils.determinism import seed_everything
|
| 123 |
+
|
| 124 |
+
seed_everything(args.seed)
|
| 125 |
+
except Exception:
|
| 126 |
+
pass
|
| 127 |
+
|
| 128 |
+
# --- connect to the running server -----------------------------------
|
| 129 |
+
# Quick reachability probe with a SHORT timeout (so we fail fast instead
|
| 130 |
+
# of blocking for `timeout_ms` when nothing is listening).
|
| 131 |
+
import socket as _socket
|
| 132 |
+
|
| 133 |
+
reachable = False
|
| 134 |
+
try:
|
| 135 |
+
with _socket.create_connection((args.host, args.port), timeout=4.0):
|
| 136 |
+
pass
|
| 137 |
+
probe = PolicyClient(host=args.host, port=args.port, timeout_ms=4000)
|
| 138 |
+
reachable = bool(probe.ping())
|
| 139 |
+
del probe
|
| 140 |
+
except OSError:
|
| 141 |
+
reachable = False
|
| 142 |
+
if not reachable:
|
| 143 |
+
result["error"] = (
|
| 144 |
+
f"GR00T policy server is not reachable at {args.host}:{args.port}. "
|
| 145 |
+
"Start it first (see run_10_smoke_tests.py output / smoke_tests/README.md). "
|
| 146 |
+
"Note: GR00T-N1.7's backbone 'nvidia/Cosmos-Reason2-2B' is a gated HF repo, so "
|
| 147 |
+
"the server needs HF auth (request access + export HF_TOKEN)."
|
| 148 |
+
)
|
| 149 |
+
_emit_result(result)
|
| 150 |
+
return 3
|
| 151 |
+
|
| 152 |
+
policy = PolicyClient(host=args.host, port=args.port, timeout_ms=args.timeout_ms)
|
| 153 |
+
|
| 154 |
+
# --- instrument get_action: record actions + optional perturbations ---
|
| 155 |
+
recorded_actions: list = []
|
| 156 |
+
rng = np.random.default_rng(args.seed)
|
| 157 |
+
n_calls = {"n": 0}
|
| 158 |
+
last_av = {"v": None}
|
| 159 |
+
orig_get_action = policy.get_action
|
| 160 |
+
|
| 161 |
+
def patched_get_action(observation, *a, **k):
|
| 162 |
+
if args.obs_noise_std and args.obs_noise_std > 0:
|
| 163 |
+
for key, val in list(observation.items()):
|
| 164 |
+
if isinstance(val, np.ndarray) and val.ndim >= 3 and "image" in key.lower():
|
| 165 |
+
noisy = val.astype(np.float32) + rng.normal(
|
| 166 |
+
0.0, args.obs_noise_std, size=val.shape
|
| 167 |
+
)
|
| 168 |
+
observation[key] = np.clip(noisy, 0, 255).astype(val.dtype)
|
| 169 |
+
if (
|
| 170 |
+
args.action_repeat
|
| 171 |
+
and args.action_repeat > 1
|
| 172 |
+
and last_av["v"] is not None
|
| 173 |
+
and (n_calls["n"] % args.action_repeat) != 0
|
| 174 |
+
):
|
| 175 |
+
action, info = last_av["v"]
|
| 176 |
+
else:
|
| 177 |
+
action, info = orig_get_action(observation, *a, **k)
|
| 178 |
+
last_av["v"] = (action, info)
|
| 179 |
+
n_calls["n"] += 1
|
| 180 |
+
recorded_actions.append(_action_to_numpy(action))
|
| 181 |
+
return action, info
|
| 182 |
+
|
| 183 |
+
policy.get_action = patched_get_action # type: ignore[assignment]
|
| 184 |
+
|
| 185 |
+
# --- run one short rollout via the official path ---------------------
|
| 186 |
+
wrapper_configs = WrapperConfigs(
|
| 187 |
+
video=VideoConfig(
|
| 188 |
+
video_dir=str(video_raw_dir) if args.save_video else None,
|
| 189 |
+
max_episode_steps=args.max_episode_steps,
|
| 190 |
+
),
|
| 191 |
+
multistep=MultiStepConfig(
|
| 192 |
+
n_action_steps=args.n_action_steps,
|
| 193 |
+
max_episode_steps=args.max_episode_steps,
|
| 194 |
+
terminate_on_success=True,
|
| 195 |
+
),
|
| 196 |
+
)
|
| 197 |
+
|
| 198 |
+
result["rollout_started"] = True
|
| 199 |
+
t0 = time.time()
|
| 200 |
+
env_name, episode_successes, episode_infos = run_rollout_gymnasium_policy(
|
| 201 |
+
env_name=args.env_name,
|
| 202 |
+
policy=policy,
|
| 203 |
+
wrapper_configs=wrapper_configs,
|
| 204 |
+
n_episodes=1,
|
| 205 |
+
n_envs=1,
|
| 206 |
+
seed=args.seed,
|
| 207 |
+
)
|
| 208 |
+
elapsed = time.time() - t0
|
| 209 |
+
|
| 210 |
+
# --- collect results -------------------------------------------------
|
| 211 |
+
success = bool(np.any(episode_successes)) if len(episode_successes) else None
|
| 212 |
+
ep_lengths = list(episode_infos.get("episode_lengths", []) or [])
|
| 213 |
+
ep_rewards = list(episode_infos.get("episode_rewards", []) or [])
|
| 214 |
+
episode_length = int(ep_lengths[0]) if ep_lengths else None
|
| 215 |
+
episode_reward = float(ep_rewards[0]) if ep_rewards else None
|
| 216 |
+
|
| 217 |
+
result["n_action_calls"] = n_calls["n"]
|
| 218 |
+
result["success"] = success
|
| 219 |
+
result["episode_length"] = episode_length
|
| 220 |
+
result["episode_reward"] = episode_reward
|
| 221 |
+
|
| 222 |
+
# --- save actions ----------------------------------------------------
|
| 223 |
+
actions_path = None
|
| 224 |
+
if recorded_actions:
|
| 225 |
+
actions_path = out_dir / "actions.npy"
|
| 226 |
+
try:
|
| 227 |
+
stacked = np.stack(recorded_actions, axis=0)
|
| 228 |
+
np.save(actions_path, stacked)
|
| 229 |
+
except Exception:
|
| 230 |
+
np.save(actions_path, np.array(recorded_actions, dtype=object), allow_pickle=True)
|
| 231 |
+
result["actions_produced"] = True
|
| 232 |
+
result["actions_path"] = str(actions_path)
|
| 233 |
+
|
| 234 |
+
# --- collect video / frames -----------------------------------------
|
| 235 |
+
video_path = None
|
| 236 |
+
if args.save_video and video_raw_dir.exists():
|
| 237 |
+
mp4s = sorted(
|
| 238 |
+
glob.glob(str(video_raw_dir / "**" / "*.mp4"), recursive=True),
|
| 239 |
+
key=lambda p: os.path.getsize(p),
|
| 240 |
+
reverse=True,
|
| 241 |
+
)
|
| 242 |
+
if mp4s:
|
| 243 |
+
video_path = out_dir / "video.mp4"
|
| 244 |
+
shutil.copyfile(mp4s[0], video_path)
|
| 245 |
+
result["video_saved"] = True
|
| 246 |
+
result["video_path"] = str(video_path)
|
| 247 |
+
# keep a record of the original (success-suffixed) filename
|
| 248 |
+
result["video_source_name"] = os.path.basename(mp4s[0])
|
| 249 |
+
if args.save_frames:
|
| 250 |
+
try:
|
| 251 |
+
import cv2
|
| 252 |
+
|
| 253 |
+
frames_dir = out_dir / "frames"
|
| 254 |
+
frames_dir.mkdir(exist_ok=True)
|
| 255 |
+
cap = cv2.VideoCapture(str(video_path))
|
| 256 |
+
idx = 0
|
| 257 |
+
while True:
|
| 258 |
+
ok, frame = cap.read()
|
| 259 |
+
if not ok:
|
| 260 |
+
break
|
| 261 |
+
cv2.imwrite(str(frames_dir / f"frame_{idx:05d}.png"), frame)
|
| 262 |
+
idx += 1
|
| 263 |
+
cap.release()
|
| 264 |
+
result["frames_dir"] = str(frames_dir)
|
| 265 |
+
result["n_frames"] = idx
|
| 266 |
+
except Exception as e: # noqa: BLE001
|
| 267 |
+
result["frames_error"] = repr(e)
|
| 268 |
+
|
| 269 |
+
# --- write per-scenario rollout summary ------------------------------
|
| 270 |
+
rollout_summary = {
|
| 271 |
+
"env_name": env_name,
|
| 272 |
+
"seed": args.seed,
|
| 273 |
+
"requested_max_episode_steps": args.max_episode_steps,
|
| 274 |
+
"n_action_steps": args.n_action_steps,
|
| 275 |
+
"n_get_action_calls": n_calls["n"],
|
| 276 |
+
"episode_successes": [bool(x) for x in episode_successes],
|
| 277 |
+
"success": success,
|
| 278 |
+
"episode_length": episode_length,
|
| 279 |
+
"episode_reward": episode_reward,
|
| 280 |
+
"elapsed_sec": elapsed,
|
| 281 |
+
"obs_noise_std": args.obs_noise_std,
|
| 282 |
+
"action_repeat": args.action_repeat,
|
| 283 |
+
"video_path": str(video_path) if video_path else None,
|
| 284 |
+
"actions_path": str(actions_path) if actions_path else None,
|
| 285 |
+
"policy_server": {"host": args.host, "port": args.port},
|
| 286 |
+
}
|
| 287 |
+
with open(out_dir / "rollout_summary.json", "w") as f:
|
| 288 |
+
json.dump(rollout_summary, f, indent=2)
|
| 289 |
+
|
| 290 |
+
result["ok"] = True
|
| 291 |
+
_emit_result(result)
|
| 292 |
+
return 0
|
| 293 |
+
|
| 294 |
+
except Exception: # noqa: BLE001
|
| 295 |
+
tb = traceback.format_exc()
|
| 296 |
+
result["error"] = tb.strip().splitlines()[-1] if tb.strip() else "unknown error"
|
| 297 |
+
result["traceback"] = tb
|
| 298 |
+
# also write a partial rollout_summary so the runner has something
|
| 299 |
+
try:
|
| 300 |
+
with open(out_dir / "rollout_summary.json", "w") as f:
|
| 301 |
+
json.dump({"error": result["error"], "traceback": tb,
|
| 302 |
+
"env_name": args.env_name, "seed": args.seed}, f, indent=2)
|
| 303 |
+
except Exception:
|
| 304 |
+
pass
|
| 305 |
+
print(tb, file=sys.stderr, flush=True)
|
| 306 |
+
_emit_result(result)
|
| 307 |
+
return 1
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
if __name__ == "__main__":
|
| 311 |
+
raise SystemExit(main())
|
examples/LIBERO/smoke_tests/make_video_montage.py
ADDED
|
@@ -0,0 +1,578 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
| 5 |
+
"""Build a compact human-review video montage for a completed LIBERO smoke run.
|
| 6 |
+
|
| 7 |
+
Reads every scenario directory under ``--run-dir``, takes a short clip from each
|
| 8 |
+
``video.mp4`` (default 5 s; full video if shorter), burns in an overlay
|
| 9 |
+
(scenario_id / label / seed / instruction / success), and writes:
|
| 10 |
+
|
| 11 |
+
<run-dir>/review_clips/<scenario_id>.mp4 one short, captioned clip per scenario
|
| 12 |
+
<run-dir>/review_montage.mp4 all scenarios back-to-back (sequential) or in a grid
|
| 13 |
+
<run-dir>/review_playlist.html the montage + each clip with metadata and a link to the full video
|
| 14 |
+
|
| 15 |
+
It does NOT rerun LIBERO and does NOT modify any existing rollout artifact —
|
| 16 |
+
it only reads ``video.mp4`` / ``metadata.json`` / ``rollout_summary.json`` and
|
| 17 |
+
writes the three review artifacts above. Requires ``ffmpeg`` (and ``ffprobe``)
|
| 18 |
+
on PATH; no GPU.
|
| 19 |
+
|
| 20 |
+
CLI::
|
| 21 |
+
|
| 22 |
+
python examples/LIBERO/smoke_tests/make_video_montage.py \
|
| 23 |
+
--run-dir outputs/libero_smoke_tests/20260512_122756 \
|
| 24 |
+
--clip-seconds 5 --mode sequential --open
|
| 25 |
+
|
| 26 |
+
Flags: --run-dir --clip-seconds --mode {sequential,grid} --hold-seconds
|
| 27 |
+
--instruction-chars --cols --open --dry-run
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
from __future__ import annotations
|
| 31 |
+
|
| 32 |
+
import argparse
|
| 33 |
+
import datetime as _dt
|
| 34 |
+
import html
|
| 35 |
+
import json
|
| 36 |
+
import math
|
| 37 |
+
import os
|
| 38 |
+
from pathlib import Path
|
| 39 |
+
import shutil
|
| 40 |
+
import subprocess
|
| 41 |
+
import sys
|
| 42 |
+
import tempfile
|
| 43 |
+
|
| 44 |
+
FONT_CANDIDATES = [
|
| 45 |
+
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
| 46 |
+
"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
|
| 47 |
+
"/usr/share/fonts/dejavu/DejaVuSans.ttf",
|
| 48 |
+
"/Library/Fonts/Arial.ttf",
|
| 49 |
+
"/System/Library/Fonts/Supplemental/Arial.ttf",
|
| 50 |
+
]
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# --------------------------------------------------------------------------- #
|
| 54 |
+
# small helpers
|
| 55 |
+
# --------------------------------------------------------------------------- #
|
| 56 |
+
def _die(msg: str, code: int = 2) -> None:
|
| 57 |
+
print(msg, file=sys.stderr)
|
| 58 |
+
raise SystemExit(code)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def _ensure_ffmpeg() -> tuple[str, str]:
|
| 62 |
+
ff = shutil.which("ffmpeg")
|
| 63 |
+
fp = shutil.which("ffprobe")
|
| 64 |
+
if ff and fp:
|
| 65 |
+
return ff, fp
|
| 66 |
+
missing = " and ".join(x for x, ok in [("ffmpeg", ff), ("ffprobe", fp)] if not ok)
|
| 67 |
+
_die(
|
| 68 |
+
f"ERROR: required tool(s) not found on PATH: {missing}.\n"
|
| 69 |
+
"Install ffmpeg, e.g.:\n"
|
| 70 |
+
" sudo apt-get update && sudo apt-get install -y ffmpeg # Debian/Ubuntu\n"
|
| 71 |
+
" conda install -c conda-forge ffmpeg # conda\n"
|
| 72 |
+
" brew install ffmpeg # macOS\n"
|
| 73 |
+
" uv pip install imageio-ffmpeg && export PATH=... (its bundled ffmpeg) # last resort\n"
|
| 74 |
+
"Then re-run this script."
|
| 75 |
+
)
|
| 76 |
+
raise AssertionError # unreachable
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _find_font() -> str | None:
|
| 80 |
+
for f in FONT_CANDIDATES:
|
| 81 |
+
if os.path.exists(f):
|
| 82 |
+
return f
|
| 83 |
+
return None
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def _load_json(path: Path):
|
| 87 |
+
try:
|
| 88 |
+
with open(path) as f:
|
| 89 |
+
return json.load(f)
|
| 90 |
+
except Exception:
|
| 91 |
+
return None
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def _probe_video(ffprobe: str, path: Path) -> dict | None:
|
| 95 |
+
try:
|
| 96 |
+
out = subprocess.run(
|
| 97 |
+
[ffprobe, "-v", "error", "-select_streams", "v:0",
|
| 98 |
+
"-show_entries", "stream=width,height,r_frame_rate,duration,nb_frames",
|
| 99 |
+
"-show_entries", "format=duration", "-of", "json", str(path)],
|
| 100 |
+
capture_output=True, text=True, check=True,
|
| 101 |
+
).stdout
|
| 102 |
+
info = json.loads(out)
|
| 103 |
+
st = (info.get("streams") or [{}])[0]
|
| 104 |
+
w = int(st.get("width") or 0)
|
| 105 |
+
h = int(st.get("height") or 0)
|
| 106 |
+
rfr = st.get("r_frame_rate") or "0/1"
|
| 107 |
+
try:
|
| 108 |
+
num, den = rfr.split("/")
|
| 109 |
+
fps = float(num) / float(den) if float(den) else 0.0
|
| 110 |
+
except Exception:
|
| 111 |
+
fps = 0.0
|
| 112 |
+
dur = st.get("duration") or (info.get("format") or {}).get("duration")
|
| 113 |
+
try:
|
| 114 |
+
dur = float(dur)
|
| 115 |
+
except (TypeError, ValueError):
|
| 116 |
+
dur = 0.0
|
| 117 |
+
nb = st.get("nb_frames")
|
| 118 |
+
try:
|
| 119 |
+
nb = int(nb)
|
| 120 |
+
except (TypeError, ValueError):
|
| 121 |
+
nb = None
|
| 122 |
+
if (not dur or dur <= 0) and nb and fps:
|
| 123 |
+
dur = nb / fps
|
| 124 |
+
return {"width": w, "height": h, "fps": fps or 20.0, "duration": dur, "nb_frames": nb}
|
| 125 |
+
except Exception:
|
| 126 |
+
return None
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def _truncate(s: str, n: int) -> str:
|
| 130 |
+
s = " ".join(str(s).split())
|
| 131 |
+
return s if len(s) <= n else s[: max(0, n - 1)].rstrip() + "…"
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def _drawtext_chain(font: str | None, lines: list[str], tmp_dir: Path,
|
| 135 |
+
box_h: int, fontsize: int = 15) -> str:
|
| 136 |
+
"""Build a filter chain: one translucent box + one drawtext per line (read from textfiles)."""
|
| 137 |
+
parts = [f"drawbox=x=0:y=0:w=iw:h={box_h}:color=black@0.55:t=fill"]
|
| 138 |
+
y = 5
|
| 139 |
+
for i, line in enumerate(lines):
|
| 140 |
+
tf = tmp_dir / f"line_{i}.txt"
|
| 141 |
+
tf.write_text(line if line else " ", encoding="utf-8")
|
| 142 |
+
# textfile path uses only safe chars (our tmp dir); expansion=none -> fully literal text.
|
| 143 |
+
fontspec = f"fontfile='{tf.parent / 'FONT'}'" # placeholder, replaced below
|
| 144 |
+
if font:
|
| 145 |
+
fontspec = f"fontfile={font}"
|
| 146 |
+
else:
|
| 147 |
+
fontspec = "font=sans"
|
| 148 |
+
parts.append(
|
| 149 |
+
f"drawtext={fontspec}:textfile={tf}:expansion=none:reload=0:"
|
| 150 |
+
f"fontsize={fontsize}:fontcolor=white:shadowcolor=black@0.8:shadowx=1:shadowy=1:"
|
| 151 |
+
f"x=6:y={y}"
|
| 152 |
+
)
|
| 153 |
+
y += fontsize + 7
|
| 154 |
+
return ",".join(parts)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def _run_ffmpeg(ffmpeg: str, args: list[str], desc: str) -> tuple[bool, str]:
|
| 158 |
+
try:
|
| 159 |
+
r = subprocess.run([ffmpeg, "-hide_banner", "-y", "-nostdin", *args],
|
| 160 |
+
capture_output=True, text=True)
|
| 161 |
+
if r.returncode != 0:
|
| 162 |
+
tail = "\n".join((r.stderr or "").strip().splitlines()[-12:])
|
| 163 |
+
return False, f"{desc}: ffmpeg exited {r.returncode}\n{tail}"
|
| 164 |
+
return True, ""
|
| 165 |
+
except Exception as e: # noqa: BLE001
|
| 166 |
+
return False, f"{desc}: {e!r}"
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
# --------------------------------------------------------------------------- #
|
| 170 |
+
# planning
|
| 171 |
+
# --------------------------------------------------------------------------- #
|
| 172 |
+
def discover_scenarios(run_dir: Path) -> tuple[list[str], dict]:
|
| 173 |
+
summary = _load_json(run_dir / "summary.json")
|
| 174 |
+
order: list[str] = []
|
| 175 |
+
rows_by_id: dict[str, dict] = {}
|
| 176 |
+
if summary and isinstance(summary.get("scenarios"), list):
|
| 177 |
+
for r in summary["scenarios"]:
|
| 178 |
+
sid = r.get("scenario_id")
|
| 179 |
+
if sid:
|
| 180 |
+
order.append(sid)
|
| 181 |
+
rows_by_id[sid] = r
|
| 182 |
+
skip = {"plots", "review_clips"}
|
| 183 |
+
for p in sorted(run_dir.iterdir()):
|
| 184 |
+
if p.is_dir() and p.name not in skip and p.name not in order:
|
| 185 |
+
order.append(p.name)
|
| 186 |
+
return order, {"summary": summary, "rows_by_id": rows_by_id}
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def build_plan(run_dir: Path, ffprobe: str, clip_seconds: float, instr_chars: int) -> list[dict]:
|
| 190 |
+
order, ctx = discover_scenarios(run_dir)
|
| 191 |
+
rows_by_id = ctx["rows_by_id"]
|
| 192 |
+
plan: list[dict] = []
|
| 193 |
+
for sid in order:
|
| 194 |
+
sc_dir = run_dir / sid
|
| 195 |
+
meta = _load_json(sc_dir / "metadata.json") or {}
|
| 196 |
+
roll = _load_json(sc_dir / "rollout_summary.json") or {}
|
| 197 |
+
srow = rows_by_id.get(sid, {})
|
| 198 |
+
manifest_entry = meta.get("manifest_entry", {}) or {}
|
| 199 |
+
resolved = meta.get("resolved", {}) or {}
|
| 200 |
+
label = manifest_entry.get("label") or meta.get("label") or srow.get("label") or "?"
|
| 201 |
+
seed = manifest_entry.get("seed", resolved.get("seed", srow.get("seed")))
|
| 202 |
+
instruction = manifest_entry.get("instruction") or roll.get("instruction") or "(unknown)"
|
| 203 |
+
env_name = resolved.get("env_name") or manifest_entry.get("env_name") or roll.get("env_name")
|
| 204 |
+
success = roll.get("success", srow.get("success"))
|
| 205 |
+
video = sc_dir / "video.mp4"
|
| 206 |
+
info = _probe_video(ffprobe, video) if video.exists() else None
|
| 207 |
+
src_dur = (info or {}).get("duration") or 0.0
|
| 208 |
+
clip_dur = src_dur if (src_dur and src_dur < clip_seconds) else clip_seconds
|
| 209 |
+
warn = None
|
| 210 |
+
if not video.exists():
|
| 211 |
+
warn = "video.mp4 not found"
|
| 212 |
+
elif info is None:
|
| 213 |
+
warn = "ffprobe could not read video.mp4"
|
| 214 |
+
elif src_dur <= 0:
|
| 215 |
+
warn = "video.mp4 has zero/unknown duration"
|
| 216 |
+
plan.append({
|
| 217 |
+
"scenario_id": sid, "label": label, "seed": seed,
|
| 218 |
+
"instruction": instruction, "instruction_short": _truncate(instruction, instr_chars),
|
| 219 |
+
"env_name": env_name, "success": success,
|
| 220 |
+
"video": video if video.exists() else None,
|
| 221 |
+
"video_rel": os.path.relpath(video, run_dir) if video.exists() else None,
|
| 222 |
+
"src_duration": round(float(src_dur), 3) if src_dur else 0.0,
|
| 223 |
+
"src_info": info,
|
| 224 |
+
"clip_duration": round(float(clip_dur), 3),
|
| 225 |
+
"clip_path": run_dir / "review_clips" / f"{sid}.mp4",
|
| 226 |
+
"clip_rel": os.path.join("review_clips", f"{sid}.mp4"),
|
| 227 |
+
"warning": warn,
|
| 228 |
+
"ok_source": video.exists() and info is not None and src_dur > 0,
|
| 229 |
+
})
|
| 230 |
+
return plan
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def _success_str(s) -> str:
|
| 234 |
+
if s is True:
|
| 235 |
+
return "true"
|
| 236 |
+
if s is False:
|
| 237 |
+
return "false"
|
| 238 |
+
return "unknown"
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
# --------------------------------------------------------------------------- #
|
| 242 |
+
# clip / montage generation
|
| 243 |
+
# --------------------------------------------------------------------------- #
|
| 244 |
+
def make_clip(ffmpeg: str, font: str | None, entry: dict, idx: int, n: int,
|
| 245 |
+
tmp_root: Path, target_w: int) -> tuple[bool, str]:
|
| 246 |
+
"""Trim entry['video'] to clip_duration, burn overlay, write entry['clip_path']."""
|
| 247 |
+
src = entry["video"]
|
| 248 |
+
out = entry["clip_path"]
|
| 249 |
+
out.parent.mkdir(parents=True, exist_ok=True)
|
| 250 |
+
tdir = tmp_root / f"clip_{idx:02d}"
|
| 251 |
+
tdir.mkdir(parents=True, exist_ok=True)
|
| 252 |
+
lines = [
|
| 253 |
+
f"[{idx + 1}/{n}] {entry['scenario_id']}",
|
| 254 |
+
f"label={entry['label']} seed={entry['seed']} success={_success_str(entry['success'])}",
|
| 255 |
+
f"task: {entry['instruction_short']}",
|
| 256 |
+
]
|
| 257 |
+
box_h = 5 + 3 * (15 + 7) + 4
|
| 258 |
+
overlay = _drawtext_chain(font, lines, tdir, box_h=box_h, fontsize=15)
|
| 259 |
+
# scale to a consistent width (keep aspect, force even dims), then overlay text.
|
| 260 |
+
vf = (f"scale={target_w}:-2:flags=bicubic,setsar=1,{overlay},format=yuv420p")
|
| 261 |
+
args = [
|
| 262 |
+
"-t", f"{entry['clip_duration']:.3f}", "-i", str(src),
|
| 263 |
+
"-an", "-vf", vf, "-r", "20",
|
| 264 |
+
"-c:v", "libx264", "-pix_fmt", "yuv420p", "-preset", "veryfast", "-crf", "23",
|
| 265 |
+
"-movflags", "+faststart", str(out),
|
| 266 |
+
]
|
| 267 |
+
return _run_ffmpeg(ffmpeg, args, f"clip[{entry['scenario_id']}]")
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
def make_montage_sequential(ffmpeg: str, clips: list[Path], out_path: Path,
|
| 271 |
+
hold_seconds: float, target_w: int) -> tuple[bool, str]:
|
| 272 |
+
if not clips:
|
| 273 |
+
return False, "no clips to montage"
|
| 274 |
+
inputs: list[str] = []
|
| 275 |
+
for c in clips:
|
| 276 |
+
inputs += ["-i", str(c)]
|
| 277 |
+
# pad the tail of each clip by hold_seconds (freeze last frame) so the overlay is readable,
|
| 278 |
+
# normalize fps/size/sar/pixfmt, then concat.
|
| 279 |
+
n = len(clips)
|
| 280 |
+
parts = []
|
| 281 |
+
for i in range(n):
|
| 282 |
+
parts.append(
|
| 283 |
+
f"[{i}:v]scale={target_w}:-2:flags=bicubic,setsar=1,fps=20,"
|
| 284 |
+
f"tpad=stop_mode=clone:stop_duration={hold_seconds:.3f},format=yuv420p[v{i}]"
|
| 285 |
+
)
|
| 286 |
+
concat_in = "".join(f"[v{i}]" for i in range(n))
|
| 287 |
+
parts.append(f"{concat_in}concat=n={n}:v=1:a=0[out]")
|
| 288 |
+
fc = ";".join(parts)
|
| 289 |
+
args = [*inputs, "-filter_complex", fc, "-map", "[out]", "-an",
|
| 290 |
+
"-c:v", "libx264", "-pix_fmt", "yuv420p", "-preset", "veryfast", "-crf", "23",
|
| 291 |
+
"-movflags", "+faststart", str(out_path)]
|
| 292 |
+
return _run_ffmpeg(ffmpeg, args, "montage(sequential)")
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
def make_montage_grid(ffmpeg: str, clips: list[Path], out_path: Path, cols: int,
|
| 296 |
+
cell_w: int, cell_h: int, ffprobe: str) -> tuple[bool, str]:
|
| 297 |
+
if not clips:
|
| 298 |
+
return False, "no clips to montage"
|
| 299 |
+
n = len(clips)
|
| 300 |
+
cols = max(1, min(cols, n))
|
| 301 |
+
rows = math.ceil(n / cols)
|
| 302 |
+
n_cells = cols * rows
|
| 303 |
+
# max duration across clips (to pad shorter ones)
|
| 304 |
+
durs = []
|
| 305 |
+
for c in clips:
|
| 306 |
+
info = _probe_video(ffprobe, c)
|
| 307 |
+
durs.append((info or {}).get("duration") or 0.0)
|
| 308 |
+
max_dur = max(durs) if durs else 0.0
|
| 309 |
+
if max_dur <= 0:
|
| 310 |
+
return False, "could not determine clip durations for grid"
|
| 311 |
+
|
| 312 |
+
inputs: list[str] = []
|
| 313 |
+
for c in clips:
|
| 314 |
+
inputs += ["-i", str(c)]
|
| 315 |
+
# extra black inputs to fill the grid
|
| 316 |
+
n_pad = n_cells - n
|
| 317 |
+
for _ in range(n_pad):
|
| 318 |
+
inputs += ["-f", "lavfi", "-t", f"{max_dur:.3f}", "-i",
|
| 319 |
+
f"color=c=black:s={cell_w}x{cell_h}:r=20"]
|
| 320 |
+
|
| 321 |
+
parts = []
|
| 322 |
+
for i in range(n):
|
| 323 |
+
# scale to fit the cell, pad to exact cell size (centered), freeze-pad to max_dur
|
| 324 |
+
parts.append(
|
| 325 |
+
f"[{i}:v]scale={cell_w}:{cell_h}:force_original_aspect_ratio=decrease,"
|
| 326 |
+
f"pad={cell_w}:{cell_h}:(ow-iw)/2:(oh-ih)/2:color=black,setsar=1,fps=20,"
|
| 327 |
+
f"tpad=stop_mode=clone:stop_duration={max(0.0, max_dur - durs[i]):.3f},format=yuv420p[v{i}]"
|
| 328 |
+
)
|
| 329 |
+
for j in range(n_pad):
|
| 330 |
+
k = n + j
|
| 331 |
+
parts.append(f"[{k}:v]setsar=1,fps=20,format=yuv420p[v{k}]")
|
| 332 |
+
layout = "|".join(f"{(i % cols) * cell_w}_{(i // cols) * cell_h}" for i in range(n_cells))
|
| 333 |
+
stack_in = "".join(f"[v{i}]" for i in range(n_cells))
|
| 334 |
+
parts.append(f"{stack_in}xstack=inputs={n_cells}:layout={layout}[out]")
|
| 335 |
+
fc = ";".join(parts)
|
| 336 |
+
args = [*inputs, "-filter_complex", fc, "-map", "[out]", "-an",
|
| 337 |
+
"-c:v", "libx264", "-pix_fmt", "yuv420p", "-preset", "veryfast", "-crf", "23",
|
| 338 |
+
"-movflags", "+faststart", str(out_path)]
|
| 339 |
+
return _run_ffmpeg(ffmpeg, args, "montage(grid)")
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
# --------------------------------------------------------------------------- #
|
| 343 |
+
# HTML
|
| 344 |
+
# --------------------------------------------------------------------------- #
|
| 345 |
+
_CSS = """
|
| 346 |
+
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;margin:0;padding:24px;background:#f4f5f7;color:#1a1a1a;}
|
| 347 |
+
h1{margin:0 0 4px 0;} .sub{color:#666;font-size:14px;margin-bottom:18px;}
|
| 348 |
+
.top{background:#fff;border:1px solid #e0e0e0;border-radius:10px;padding:16px;margin-bottom:22px;box-shadow:0 1px 3px rgba(0,0,0,.06);}
|
| 349 |
+
.top video{width:100%;max-width:760px;border-radius:8px;background:#000;display:block;}
|
| 350 |
+
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(360px,1fr));gap:16px;}
|
| 351 |
+
.card{background:#fff;border:1px solid #e0e0e0;border-radius:10px;padding:14px;box-shadow:0 1px 3px rgba(0,0,0,.05);}
|
| 352 |
+
.card h3{margin:0 0 6px 0;font-size:15px;}
|
| 353 |
+
.card video{width:100%;border-radius:6px;background:#000;}
|
| 354 |
+
.badge{display:inline-block;padding:1px 8px;border-radius:10px;font-size:11px;font-weight:600;}
|
| 355 |
+
.b-normal{background:#e3f0ff;color:#1a5fb4;} .b-abn{background:#ffe9d6;color:#b35a00;}
|
| 356 |
+
.b-ok{background:#e6f6ea;color:#1a7f37;} .b-fail{background:#fde8e8;color:#b42318;} .b-unk{background:#eee;color:#555;}
|
| 357 |
+
.kv{font-size:12.5px;line-height:1.6;margin:6px 0;} .kv b{color:#444;}
|
| 358 |
+
.warn{background:#fff8e1;border:1px solid #ffe082;border-radius:6px;padding:8px 10px;margin:6px 0;font-size:12.5px;color:#7a5c00;}
|
| 359 |
+
a{color:#1a5fb4;} code{background:#f0f0f0;padding:1px 4px;border-radius:3px;font-size:12px;}
|
| 360 |
+
"""
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
def _badge_label(label):
|
| 364 |
+
if label == "normal":
|
| 365 |
+
return '<span class="badge b-normal">normal</span>'
|
| 366 |
+
if label == "abnormal_probe":
|
| 367 |
+
return '<span class="badge b-abn">abnormal_probe</span>'
|
| 368 |
+
return f'<span class="badge b-unk">{html.escape(str(label))}</span>'
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
def _badge_success(s):
|
| 372 |
+
if s is True:
|
| 373 |
+
return '<span class="badge b-ok">success: true</span>'
|
| 374 |
+
if s is False:
|
| 375 |
+
return '<span class="badge b-fail">success: false</span>'
|
| 376 |
+
return '<span class="badge b-unk">success: unknown</span>'
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
def render_playlist_html(run_dir: Path, plan: list[dict], montage_rel: str | None,
|
| 380 |
+
montage_mode: str, clip_seconds: float, clips_ok: dict) -> str:
|
| 381 |
+
p = []
|
| 382 |
+
p.append("<!DOCTYPE html><html><head><meta charset='utf-8'>")
|
| 383 |
+
p.append(f"<title>LIBERO smoke-test review montage — {html.escape(run_dir.name)}</title>")
|
| 384 |
+
p.append(f"<style>{_CSS}</style></head><body>")
|
| 385 |
+
p.append("<h1>LIBERO smoke-test — video review</h1>")
|
| 386 |
+
p.append(f"<div class='sub'>run dir: <code>{html.escape(str(run_dir))}</code> | "
|
| 387 |
+
f"generated {html.escape(_dt.datetime.now().isoformat(timespec='seconds'))} | "
|
| 388 |
+
f"{len(plan)} scenarios | clip length ≤ {clip_seconds:g}s | montage mode: {html.escape(montage_mode)}</div>")
|
| 389 |
+
p.append("<div class='top'>")
|
| 390 |
+
if montage_rel:
|
| 391 |
+
p.append(f"<h3 style='margin-top:0'>review_montage.mp4</h3>")
|
| 392 |
+
p.append(f"<video controls preload='metadata' src='{html.escape(montage_rel)}'></video>")
|
| 393 |
+
p.append(f"<div class='kv'><a href='{html.escape(montage_rel)}'>{html.escape(montage_rel)}</a></div>")
|
| 394 |
+
else:
|
| 395 |
+
p.append("<div class='warn'>review_montage.mp4 was not produced (no usable clips, or ffmpeg failed — see console output).</div>")
|
| 396 |
+
p.append("</div>")
|
| 397 |
+
p.append("<div class='grid'>")
|
| 398 |
+
for i, e in enumerate(plan):
|
| 399 |
+
sid = e["scenario_id"]
|
| 400 |
+
p.append("<div class='card'>")
|
| 401 |
+
p.append(f"<h3>[{i + 1}/{len(plan)}] {html.escape(sid)}</h3>")
|
| 402 |
+
p.append("<div class='kv'>" + _badge_label(e["label"]) + " " + _badge_success(e["success"])
|
| 403 |
+
+ f" seed={html.escape(str(e['seed']))}</div>")
|
| 404 |
+
clip_ok = clips_ok.get(sid, False)
|
| 405 |
+
if clip_ok and (run_dir / e["clip_rel"]).exists():
|
| 406 |
+
p.append(f"<video controls preload='metadata' src='{html.escape(e['clip_rel'])}'></video>")
|
| 407 |
+
elif e["warning"]:
|
| 408 |
+
p.append(f"<div class='warn'>⚠ {html.escape(str(e['warning']))} — no review clip for this scenario.</div>")
|
| 409 |
+
else:
|
| 410 |
+
p.append("<div class='warn'>⚠ review clip not generated (ffmpeg failed for this scenario — see console).</div>")
|
| 411 |
+
p.append("<div class='kv'>"
|
| 412 |
+
f"<b>instruction:</b> {html.escape(str(e['instruction']))}<br>"
|
| 413 |
+
+ (f"<b>env:</b> <code>{html.escape(str(e['env_name']))}</code><br>" if e.get("env_name") else "")
|
| 414 |
+
+ f"<b>source video:</b> {e['src_duration']:g}s"
|
| 415 |
+
+ (f" <a href='{html.escape(e['video_rel'])}'>full video.mp4</a>" if e.get("video_rel") else " (missing)")
|
| 416 |
+
+ (f"<br><b>clip:</b> <a href='{html.escape(e['clip_rel'])}'>{html.escape(e['clip_rel'])}</a> ({e['clip_duration']:g}s)" if clip_ok else "")
|
| 417 |
+
+ "</div>")
|
| 418 |
+
p.append("</div>")
|
| 419 |
+
p.append("</div></body></html>")
|
| 420 |
+
return "\n".join(p)
|
| 421 |
+
|
| 422 |
+
|
| 423 |
+
# --------------------------------------------------------------------------- #
|
| 424 |
+
# main
|
| 425 |
+
# --------------------------------------------------------------------------- #
|
| 426 |
+
def main() -> int:
|
| 427 |
+
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
| 428 |
+
ap.add_argument("--run-dir", required=True)
|
| 429 |
+
ap.add_argument("--clip-seconds", type=float, default=5.0, help="Max clip length per scenario (full video if shorter).")
|
| 430 |
+
ap.add_argument("--mode", choices=["sequential", "grid"], default="sequential")
|
| 431 |
+
ap.add_argument("--hold-seconds", type=float, default=1.0,
|
| 432 |
+
help="In sequential mode, freeze the last frame of each clip this long so the overlay is readable.")
|
| 433 |
+
ap.add_argument("--instruction-chars", type=int, default=56, help="Truncate the overlaid instruction to this many chars.")
|
| 434 |
+
ap.add_argument("--cols", type=int, default=0, help="Grid mode: number of columns (0 = auto = ceil(sqrt(N))).")
|
| 435 |
+
ap.add_argument("--width", type=int, default=512, help="Clip width in px (height auto, aspect preserved).")
|
| 436 |
+
ap.add_argument("--open", action="store_true", help="Open review_playlist.html when done.")
|
| 437 |
+
ap.add_argument("--dry-run", action="store_true", help="Print the planned clips and exit without writing anything.")
|
| 438 |
+
args = ap.parse_args()
|
| 439 |
+
|
| 440 |
+
run_dir = Path(args.run_dir).resolve()
|
| 441 |
+
if not run_dir.is_dir():
|
| 442 |
+
_die(f"ERROR: --run-dir does not exist or is not a directory: {run_dir}")
|
| 443 |
+
|
| 444 |
+
ffmpeg, ffprobe = _ensure_ffmpeg()
|
| 445 |
+
font = _find_font()
|
| 446 |
+
|
| 447 |
+
plan = build_plan(run_dir, ffprobe, args.clip_seconds, args.instruction_chars)
|
| 448 |
+
if not plan:
|
| 449 |
+
_die(f"ERROR: no scenario directories found under {run_dir}")
|
| 450 |
+
|
| 451 |
+
n = len(plan)
|
| 452 |
+
n_with_video = sum(1 for e in plan if e["ok_source"])
|
| 453 |
+
n_missing = n - n_with_video
|
| 454 |
+
|
| 455 |
+
# --- dry run -------------------------------------------------------------
|
| 456 |
+
if args.dry_run:
|
| 457 |
+
print(f"[dry-run] run dir: {run_dir}")
|
| 458 |
+
print(f"[dry-run] {n} scenarios; {n_with_video} with usable video.mp4; {n_missing} missing/unreadable")
|
| 459 |
+
print(f"[dry-run] mode={args.mode} clip-seconds={args.clip_seconds:g} hold-seconds={args.hold_seconds:g} font={font or '(builtin sans)'}")
|
| 460 |
+
print(f"[dry-run] would write:")
|
| 461 |
+
print(f" {run_dir / 'review_clips'}/<scenario_id>.mp4")
|
| 462 |
+
print(f" {run_dir / 'review_montage.mp4'}")
|
| 463 |
+
print(f" {run_dir / 'review_playlist.html'}")
|
| 464 |
+
print()
|
| 465 |
+
hdr = f"{'#':>2} {'scenario_id':40s} {'label':14s} {'seed':>5s} {'src_s':>6s} {'clip_s':>6s} {'success':8s} note"
|
| 466 |
+
print(hdr)
|
| 467 |
+
print("-" * len(hdr))
|
| 468 |
+
for i, e in enumerate(plan):
|
| 469 |
+
print(f"{i + 1:>2} {e['scenario_id'][:40]:40s} {str(e['label'])[:14]:14s} "
|
| 470 |
+
f"{str(e['seed']):>5s} {e['src_duration']:>6.2f} {e['clip_duration']:>6.2f} "
|
| 471 |
+
f"{_success_str(e['success']):8s} {e['warning'] or 'ok -> ' + e['clip_rel']}")
|
| 472 |
+
print(f" instruction: {e['instruction_short']}")
|
| 473 |
+
return 0
|
| 474 |
+
|
| 475 |
+
if n_with_video == 0:
|
| 476 |
+
# still write an HTML so the user has something, but no montage.
|
| 477 |
+
print(f"WARNING: none of the {n} scenarios has a usable video.mp4 — writing an HTML with warnings only.", file=sys.stderr)
|
| 478 |
+
html_path = run_dir / "review_playlist.html"
|
| 479 |
+
html_path.write_text(render_playlist_html(run_dir, plan, None, args.mode, args.clip_seconds, {}), encoding="utf-8")
|
| 480 |
+
print("\n==== SUMMARY ====")
|
| 481 |
+
print(f"total scenarios : {n}")
|
| 482 |
+
print(f"clips generated : 0")
|
| 483 |
+
print(f"missing videos : {n_missing} ({', '.join(e['scenario_id'] for e in plan if not e['ok_source'])})")
|
| 484 |
+
print(f"montage path : (not produced)")
|
| 485 |
+
print(f"html path : {html_path}")
|
| 486 |
+
return 1
|
| 487 |
+
|
| 488 |
+
# --- generate per-scenario clips ----------------------------------------
|
| 489 |
+
(run_dir / "review_clips").mkdir(parents=True, exist_ok=True)
|
| 490 |
+
tmp_root = Path(tempfile.mkdtemp(prefix="libero_montage_"))
|
| 491 |
+
clips_ok: dict[str, bool] = {}
|
| 492 |
+
generated_clips: list[Path] = []
|
| 493 |
+
warnings: list[str] = []
|
| 494 |
+
target_w = args.width - (args.width % 2) # even
|
| 495 |
+
for i, e in enumerate(plan):
|
| 496 |
+
sid = e["scenario_id"]
|
| 497 |
+
if not e["ok_source"]:
|
| 498 |
+
clips_ok[sid] = False
|
| 499 |
+
warnings.append(f"[{sid}] {e['warning'] or 'no usable video'} — skipped")
|
| 500 |
+
print(f"[{i + 1}/{n}] {sid}: SKIP ({e['warning'] or 'no usable video'})")
|
| 501 |
+
continue
|
| 502 |
+
ok, err = make_clip(ffmpeg, font, e, i, n, tmp_root, target_w)
|
| 503 |
+
clips_ok[sid] = ok
|
| 504 |
+
if ok and e["clip_path"].exists():
|
| 505 |
+
generated_clips.append(e["clip_path"])
|
| 506 |
+
print(f"[{i + 1}/{n}] {sid}: clip -> {e['clip_rel']} ({e['clip_duration']:g}s)")
|
| 507 |
+
else:
|
| 508 |
+
warnings.append(f"[{sid}] clip generation failed: {err}")
|
| 509 |
+
print(f"[{i + 1}/{n}] {sid}: CLIP FAILED\n{err}", file=sys.stderr)
|
| 510 |
+
|
| 511 |
+
# --- montage -------------------------------------------------------------
|
| 512 |
+
montage_path = run_dir / "review_montage.mp4"
|
| 513 |
+
montage_rel = None
|
| 514 |
+
if generated_clips:
|
| 515 |
+
if args.mode == "grid":
|
| 516 |
+
cols = args.cols if args.cols and args.cols > 0 else max(1, math.ceil(math.sqrt(len(generated_clips))))
|
| 517 |
+
# cell size: keep clips' aspect (target_w x ~target_w/2), shrink so total width <= ~1920
|
| 518 |
+
src_info = next((e["src_info"] for e in plan if e["ok_source"] and e["src_info"]), {"width": 512, "height": 256})
|
| 519 |
+
ar = (src_info.get("height") or 256) / (src_info.get("width") or 512)
|
| 520 |
+
cell_w = min(target_w, max(160, (1920 // cols) - ((1920 // cols) % 2)))
|
| 521 |
+
cell_h = int(round(cell_w * ar))
|
| 522 |
+
cell_h -= cell_h % 2
|
| 523 |
+
ok, err = make_montage_grid(ffmpeg, generated_clips, montage_path, cols, cell_w, cell_h, ffprobe)
|
| 524 |
+
if not ok:
|
| 525 |
+
print(f"WARNING: grid montage failed ({err}); falling back to sequential montage.", file=sys.stderr)
|
| 526 |
+
warnings.append(f"grid montage failed: {err} (fell back to sequential)")
|
| 527 |
+
ok, err = make_montage_sequential(ffmpeg, generated_clips, montage_path, args.hold_seconds, target_w)
|
| 528 |
+
else:
|
| 529 |
+
ok, err = make_montage_sequential(ffmpeg, generated_clips, montage_path, args.hold_seconds, target_w)
|
| 530 |
+
if ok and montage_path.exists():
|
| 531 |
+
montage_rel = os.path.relpath(montage_path, run_dir)
|
| 532 |
+
print(f"montage -> {montage_rel}")
|
| 533 |
+
else:
|
| 534 |
+
warnings.append(f"montage generation failed: {err}")
|
| 535 |
+
print(f"WARNING: montage generation failed: {err}", file=sys.stderr)
|
| 536 |
+
else:
|
| 537 |
+
warnings.append("no clips were generated, so no montage was produced")
|
| 538 |
+
|
| 539 |
+
# --- HTML ----------------------------------------------------------------
|
| 540 |
+
html_path = run_dir / "review_playlist.html"
|
| 541 |
+
html_path.write_text(
|
| 542 |
+
render_playlist_html(run_dir, plan, montage_rel, args.mode, args.clip_seconds, clips_ok),
|
| 543 |
+
encoding="utf-8",
|
| 544 |
+
)
|
| 545 |
+
|
| 546 |
+
# cleanup temp dir
|
| 547 |
+
shutil.rmtree(tmp_root, ignore_errors=True)
|
| 548 |
+
|
| 549 |
+
# --- summary -------------------------------------------------------------
|
| 550 |
+
missing_ids = [e["scenario_id"] for e in plan if not e["ok_source"]]
|
| 551 |
+
failed_ids = [sid for sid, ok in clips_ok.items() if ok is False and sid not in missing_ids]
|
| 552 |
+
print("\n==== SUMMARY ====")
|
| 553 |
+
print(f"total scenarios : {n}")
|
| 554 |
+
print(f"clips generated : {len(generated_clips)} ({run_dir / 'review_clips'}/)")
|
| 555 |
+
if failed_ids:
|
| 556 |
+
print(f"clip failures : {len(failed_ids)} ({', '.join(failed_ids)})")
|
| 557 |
+
print(f"missing videos : {len(missing_ids)}" + (f" ({', '.join(missing_ids)})" if missing_ids else ""))
|
| 558 |
+
print(f"montage path : {montage_path if montage_rel else '(not produced)'}")
|
| 559 |
+
print(f"html path : {html_path}")
|
| 560 |
+
if warnings:
|
| 561 |
+
print(f"\n{len(warnings)} warning(s):")
|
| 562 |
+
for w in warnings:
|
| 563 |
+
print(f" - {w}")
|
| 564 |
+
|
| 565 |
+
if args.open:
|
| 566 |
+
import webbrowser
|
| 567 |
+
try:
|
| 568 |
+
webbrowser.open(html_path.as_uri())
|
| 569 |
+
print(f"\nOpened {html_path.as_uri()}")
|
| 570 |
+
except Exception:
|
| 571 |
+
print(f"\nCould not auto-open a browser; open manually:\n {html_path}")
|
| 572 |
+
|
| 573 |
+
# exit non-zero only if we produced *nothing* useful
|
| 574 |
+
return 0 if generated_clips else 1
|
| 575 |
+
|
| 576 |
+
|
| 577 |
+
if __name__ == "__main__":
|
| 578 |
+
raise SystemExit(main())
|
examples/LIBERO/smoke_tests/review_smoke_tests.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
| 5 |
+
"""List smoke-test scenarios + their video paths, and optionally play them.
|
| 6 |
+
|
| 7 |
+
Usage::
|
| 8 |
+
|
| 9 |
+
# most recent run under outputs/libero_smoke_tests/
|
| 10 |
+
python examples/LIBERO/smoke_tests/review_smoke_tests.py
|
| 11 |
+
|
| 12 |
+
# a specific run dir
|
| 13 |
+
python examples/LIBERO/smoke_tests/review_smoke_tests.py --run-dir outputs/libero_smoke_tests/20260512_120000
|
| 14 |
+
|
| 15 |
+
# try to open each video one by one (xdg-open / open / ffplay, if available)
|
| 16 |
+
python examples/LIBERO/smoke_tests/review_smoke_tests.py --open
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
from __future__ import annotations
|
| 20 |
+
|
| 21 |
+
import argparse
|
| 22 |
+
import json
|
| 23 |
+
from pathlib import Path
|
| 24 |
+
import shutil
|
| 25 |
+
import subprocess
|
| 26 |
+
import sys
|
| 27 |
+
|
| 28 |
+
REPO_ROOT = Path(__file__).resolve().parents[3]
|
| 29 |
+
DEFAULT_OUTPUT_DIR = REPO_ROOT / "outputs" / "libero_smoke_tests"
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _latest_run_dir(output_dir: Path) -> Path | None:
|
| 33 |
+
if not output_dir.exists():
|
| 34 |
+
return None
|
| 35 |
+
runs = sorted((p for p in output_dir.iterdir() if p.is_dir()), key=lambda p: p.name)
|
| 36 |
+
return runs[-1] if runs else None
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def _find_video(scenario_dir: Path) -> Path | None:
|
| 40 |
+
cand = scenario_dir / "video.mp4"
|
| 41 |
+
if cand.exists():
|
| 42 |
+
return cand
|
| 43 |
+
vids = sorted(scenario_dir.glob("**/*.mp4"))
|
| 44 |
+
return vids[0] if vids else None
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def _open_video(path: Path) -> None:
|
| 48 |
+
for opener in ("xdg-open", "open", "ffplay"):
|
| 49 |
+
exe = shutil.which(opener)
|
| 50 |
+
if exe:
|
| 51 |
+
args = [exe, str(path)]
|
| 52 |
+
if opener == "ffplay":
|
| 53 |
+
args = [exe, "-autoexit", "-loglevel", "error", str(path)]
|
| 54 |
+
print(f" opening with: {' '.join(args)}")
|
| 55 |
+
try:
|
| 56 |
+
subprocess.run(args, check=False)
|
| 57 |
+
except Exception as e: # noqa: BLE001
|
| 58 |
+
print(f" (failed to open: {e})")
|
| 59 |
+
return
|
| 60 |
+
print(" no video opener found (xdg-open / open / ffplay). Path printed above.")
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def main() -> int:
|
| 64 |
+
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
| 65 |
+
ap.add_argument("--output-dir", default=str(DEFAULT_OUTPUT_DIR))
|
| 66 |
+
ap.add_argument("--run-dir", default=None, help="Specific <output-dir>/<timestamp> dir.")
|
| 67 |
+
ap.add_argument("--open", action="store_true", help="Try to play each video, pausing between.")
|
| 68 |
+
args = ap.parse_args()
|
| 69 |
+
|
| 70 |
+
run_dir = Path(args.run_dir) if args.run_dir else _latest_run_dir(Path(args.output_dir))
|
| 71 |
+
if run_dir is None or not run_dir.exists():
|
| 72 |
+
print(f"No run directory found under {args.output_dir}. Run run_10_smoke_tests.py first.",
|
| 73 |
+
file=sys.stderr)
|
| 74 |
+
return 1
|
| 75 |
+
|
| 76 |
+
print(f"Run directory: {run_dir}\n")
|
| 77 |
+
|
| 78 |
+
summary_path = run_dir / "summary.json"
|
| 79 |
+
rows = []
|
| 80 |
+
if summary_path.exists():
|
| 81 |
+
try:
|
| 82 |
+
rows = json.loads(summary_path.read_text()).get("scenarios", [])
|
| 83 |
+
except Exception:
|
| 84 |
+
rows = []
|
| 85 |
+
|
| 86 |
+
scenario_dirs = sorted(p for p in run_dir.iterdir() if p.is_dir())
|
| 87 |
+
by_id = {r.get("scenario_id"): r for r in rows}
|
| 88 |
+
|
| 89 |
+
for i, sc_dir in enumerate(scenario_dirs, 1):
|
| 90 |
+
sid = sc_dir.name
|
| 91 |
+
row = by_id.get(sid, {})
|
| 92 |
+
meta = {}
|
| 93 |
+
mp = sc_dir / "metadata.json"
|
| 94 |
+
if mp.exists():
|
| 95 |
+
try:
|
| 96 |
+
meta = json.loads(mp.read_text())
|
| 97 |
+
except Exception:
|
| 98 |
+
meta = {}
|
| 99 |
+
label = row.get("label") or meta.get("label", "?")
|
| 100 |
+
success = row.get("success", "unknown")
|
| 101 |
+
video = _find_video(sc_dir)
|
| 102 |
+
actions = sc_dir / "actions.npy"
|
| 103 |
+
print(f"[{i:2d}] {sid}")
|
| 104 |
+
print(f" label={label} success={success} "
|
| 105 |
+
f"rollout_started={row.get('rollout_started')} "
|
| 106 |
+
f"actions={'yes' if actions.exists() else 'no'}")
|
| 107 |
+
env_name = (meta.get("resolved") or {}).get("env_name") or (meta.get("manifest_entry") or {}).get("env_name")
|
| 108 |
+
if env_name:
|
| 109 |
+
print(f" env: {env_name}")
|
| 110 |
+
if row.get("error_if_any"):
|
| 111 |
+
print(f" error: {row['error_if_any']}")
|
| 112 |
+
if video:
|
| 113 |
+
print(f" video: {video}")
|
| 114 |
+
else:
|
| 115 |
+
frames = sc_dir / "frames"
|
| 116 |
+
if frames.exists():
|
| 117 |
+
n = len(list(frames.glob('*.png')))
|
| 118 |
+
print(f" frames: {frames}/ ({n} png)")
|
| 119 |
+
else:
|
| 120 |
+
print(f" video: <none saved>")
|
| 121 |
+
print()
|
| 122 |
+
if args.open and video:
|
| 123 |
+
_open_video(video)
|
| 124 |
+
if i < len(scenario_dirs):
|
| 125 |
+
try:
|
| 126 |
+
input(" [enter] for next, Ctrl-C to stop... ")
|
| 127 |
+
except (EOFError, KeyboardInterrupt):
|
| 128 |
+
print()
|
| 129 |
+
break
|
| 130 |
+
|
| 131 |
+
print(f"Summary: {summary_path if summary_path.exists() else '(not found)'}")
|
| 132 |
+
md = run_dir / "summary.md"
|
| 133 |
+
if md.exists():
|
| 134 |
+
print(f"Markdown summary: {md}")
|
| 135 |
+
return 0
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
if __name__ == "__main__":
|
| 139 |
+
raise SystemExit(main())
|
examples/LIBERO/smoke_tests/run_10_smoke_tests.py
ADDED
|
@@ -0,0 +1,590 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
| 5 |
+
"""Run 10 short, simulation-only LIBERO smoke tests against a GR00T policy server.
|
| 6 |
+
|
| 7 |
+
What this does (and does NOT do):
|
| 8 |
+
* It does NOT use any physical robot hardware -- LIBERO simulation only.
|
| 9 |
+
* It does NOT rewrite the official evaluation path. Each scenario is run
|
| 10 |
+
through ``gr00t.eval.rollout_policy.run_rollout_gymnasium_policy`` via the
|
| 11 |
+
thin ``_libero_rollout_worker.py`` (which only adds action recording and a
|
| 12 |
+
couple of tiny, optional simulation-only perturbations).
|
| 13 |
+
* It does NOT fake successful rollouts. If the model checkpoint is missing,
|
| 14 |
+
or the server is unreachable, or the LIBERO sim env is not installed, it
|
| 15 |
+
fails with a clear, actionable error message.
|
| 16 |
+
|
| 17 |
+
Typical usage (two terminals):
|
| 18 |
+
|
| 19 |
+
Terminal 1 - start the GR00T inference server::
|
| 20 |
+
|
| 21 |
+
uv run python gr00t/eval/run_gr00t_server.py \
|
| 22 |
+
--model-path checkpoints/GR00T-N1.7-LIBERO/libero_10 \
|
| 23 |
+
--embodiment-tag LIBERO_PANDA \
|
| 24 |
+
--use-sim-policy-wrapper
|
| 25 |
+
|
| 26 |
+
Terminal 2 - run the smoke tests::
|
| 27 |
+
|
| 28 |
+
uv run python examples/LIBERO/smoke_tests/run_10_smoke_tests.py \
|
| 29 |
+
--model-path checkpoints/GR00T-N1.7-LIBERO/libero_10 \
|
| 30 |
+
--manifest examples/LIBERO/smoke_tests/scenarios_10.yaml \
|
| 31 |
+
--output-dir outputs/libero_smoke_tests \
|
| 32 |
+
--max-episode-steps 50 --save-video --render
|
| 33 |
+
|
| 34 |
+
Pass ``--start-server`` to have this script launch (and later kill) the server
|
| 35 |
+
itself, or ``--dry-run`` to validate the manifest / paths without running sims.
|
| 36 |
+
"""
|
| 37 |
+
|
| 38 |
+
from __future__ import annotations
|
| 39 |
+
|
| 40 |
+
import argparse
|
| 41 |
+
import datetime as _dt
|
| 42 |
+
import json
|
| 43 |
+
import os
|
| 44 |
+
from pathlib import Path
|
| 45 |
+
import shutil
|
| 46 |
+
import subprocess
|
| 47 |
+
import sys
|
| 48 |
+
import time
|
| 49 |
+
import traceback
|
| 50 |
+
|
| 51 |
+
REPO_ROOT = Path(__file__).resolve().parents[3]
|
| 52 |
+
WORKER = Path(__file__).resolve().parent / "_libero_rollout_worker.py"
|
| 53 |
+
DEFAULT_MANIFEST = Path(__file__).resolve().parent / "scenarios_10.yaml"
|
| 54 |
+
RESULT_PREFIX = "SMOKE_RESULT_JSON:"
|
| 55 |
+
|
| 56 |
+
# Path to the dedicated LIBERO uv venv created by setup_libero.sh.
|
| 57 |
+
LIBERO_VENV_PYTHON = (
|
| 58 |
+
REPO_ROOT / "gr00t" / "eval" / "sim" / "LIBERO" / "libero_uv" / ".venv" / "bin" / "python"
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
SERVER_CMD_TEMPLATE = [
|
| 62 |
+
"uv", "run", "python", "gr00t/eval/run_gr00t_server.py",
|
| 63 |
+
"--model-path", "{model_path}",
|
| 64 |
+
"--embodiment-tag", "LIBERO_PANDA",
|
| 65 |
+
"--use-sim-policy-wrapper",
|
| 66 |
+
]
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
# --------------------------------------------------------------------------- #
|
| 70 |
+
# Helpers
|
| 71 |
+
# --------------------------------------------------------------------------- #
|
| 72 |
+
def _load_manifest(path: Path) -> list[dict]:
|
| 73 |
+
try:
|
| 74 |
+
import yaml
|
| 75 |
+
except ImportError as e: # pragma: no cover
|
| 76 |
+
raise SystemExit(
|
| 77 |
+
"PyYAML is required to read the scenario manifest. Install it with "
|
| 78 |
+
"`uv pip install pyyaml` (it is a transitive dependency of the gr00t "
|
| 79 |
+
"package, so this normally just works inside the project venv)."
|
| 80 |
+
) from e
|
| 81 |
+
if not path.exists():
|
| 82 |
+
raise SystemExit(f"Manifest not found: {path}")
|
| 83 |
+
with open(path) as f:
|
| 84 |
+
data = yaml.safe_load(f)
|
| 85 |
+
scenarios = data.get("scenarios") if isinstance(data, dict) else data
|
| 86 |
+
if not isinstance(scenarios, list) or not scenarios:
|
| 87 |
+
raise SystemExit(f"Manifest {path} does not contain a non-empty 'scenarios' list.")
|
| 88 |
+
return scenarios
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def _check_model_path(model_path: Path) -> None:
|
| 92 |
+
if model_path.exists():
|
| 93 |
+
# Must look like a real checkpoint dir.
|
| 94 |
+
has_cfg = (model_path / "config.json").exists()
|
| 95 |
+
has_weights = any(model_path.glob("*.safetensors")) or (model_path / "pytorch_model.bin").exists()
|
| 96 |
+
if has_cfg and has_weights:
|
| 97 |
+
return
|
| 98 |
+
missing = []
|
| 99 |
+
if not has_cfg:
|
| 100 |
+
missing.append("config.json")
|
| 101 |
+
if not has_weights:
|
| 102 |
+
missing.append("model-*.safetensors / model.safetensors.index.json")
|
| 103 |
+
raise SystemExit(
|
| 104 |
+
f"Model path {model_path} exists but is missing: {', '.join(missing)}.\n"
|
| 105 |
+
"Re-download the checkpoint:\n\n" + _download_hint()
|
| 106 |
+
)
|
| 107 |
+
raise SystemExit(
|
| 108 |
+
f"Model checkpoint not found at: {model_path}\n\n"
|
| 109 |
+
"Download it first (HuggingFace does not support nested repo paths directly):\n\n"
|
| 110 |
+
+ _download_hint()
|
| 111 |
+
)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def _download_hint() -> str:
|
| 115 |
+
return (
|
| 116 |
+
" uv run hf download nvidia/GR00T-N1.7-LIBERO \\\n"
|
| 117 |
+
" --include \"libero_10/config.json\" \\\n"
|
| 118 |
+
" \"libero_10/embodiment_id.json\" \\\n"
|
| 119 |
+
" \"libero_10/model-*.safetensors\" \\\n"
|
| 120 |
+
" \"libero_10/model.safetensors.index.json\" \\\n"
|
| 121 |
+
" \"libero_10/processor_config.json\" \\\n"
|
| 122 |
+
" \"libero_10/statistics.json\" \\\n"
|
| 123 |
+
" --local-dir checkpoints/GR00T-N1.7-LIBERO\n"
|
| 124 |
+
" # (also fetch libero_10/config.json explicitly if the glob above skips it)\n"
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def _server_command(model_path: Path) -> list[str]:
|
| 129 |
+
return [tok.format(model_path=str(model_path)) for tok in SERVER_CMD_TEMPLATE]
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
# The GR00T-N1.7 backbone (nvidia/Cosmos-Reason2-2B, a Qwen3-VL model) is a
|
| 133 |
+
# *gated* HuggingFace repo. Loading any GR00T-N1.7 checkpoint pulls that base
|
| 134 |
+
# repo's config/processor, so the server will fail to start without HF auth +
|
| 135 |
+
# granted access. We surface a clear hint when we detect this.
|
| 136 |
+
GATED_BACKBONE_HINT = (
|
| 137 |
+
"The GR00T-N1.7 backbone 'nvidia/Cosmos-Reason2-2B' is a GATED HuggingFace repo.\n"
|
| 138 |
+
"To start the server you must:\n"
|
| 139 |
+
" 1. Request access at https://huggingface.co/nvidia/Cosmos-Reason2-2B (one click, usually instant).\n"
|
| 140 |
+
" 2. Authenticate, e.g. export HF_TOKEN=hf_xxx (or: uv run hf auth login)\n"
|
| 141 |
+
" 3. Re-run the server / smoke tests.\n"
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def _scan_log_for_gated_repo(log_path: Path) -> bool:
|
| 146 |
+
try:
|
| 147 |
+
text = log_path.read_text(errors="replace")
|
| 148 |
+
except OSError:
|
| 149 |
+
return False
|
| 150 |
+
return ("gated repo" in text) or ("Cosmos-Reason2-2B is restricted" in text) or (
|
| 151 |
+
"Access to model nvidia/Cosmos-Reason2-2B" in text
|
| 152 |
+
)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def _ping_server(host: str, port: int, timeout_ms: int = 3000) -> bool:
|
| 156 |
+
"""Return True if a GR00T policy server answers on host:port.
|
| 157 |
+
|
| 158 |
+
First does a cheap TCP connect (so we don't drag in the heavy torch/gr00t
|
| 159 |
+
import stack when nothing is listening); only if *something* is listening do
|
| 160 |
+
we import ``PolicyClient`` and validate the msgpack ``ping`` endpoint.
|
| 161 |
+
"""
|
| 162 |
+
import socket
|
| 163 |
+
|
| 164 |
+
try:
|
| 165 |
+
with socket.create_connection((host, port), timeout=timeout_ms / 1000.0):
|
| 166 |
+
pass
|
| 167 |
+
except OSError:
|
| 168 |
+
return False
|
| 169 |
+
# Something is listening -- confirm it actually speaks the GR00T protocol.
|
| 170 |
+
try:
|
| 171 |
+
from gr00t.policy.server_client import PolicyClient
|
| 172 |
+
|
| 173 |
+
client = PolicyClient(host=host, port=port, timeout_ms=timeout_ms)
|
| 174 |
+
return bool(client.ping())
|
| 175 |
+
except Exception:
|
| 176 |
+
# Reachable on TCP but the client/import failed; treat as "up enough".
|
| 177 |
+
return True
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def _resolve_libero_python(explicit: str | None) -> str:
|
| 181 |
+
if explicit:
|
| 182 |
+
p = Path(explicit)
|
| 183 |
+
if not p.exists():
|
| 184 |
+
raise SystemExit(f"--libero-python {explicit} does not exist.")
|
| 185 |
+
return str(p)
|
| 186 |
+
if LIBERO_VENV_PYTHON.exists():
|
| 187 |
+
return str(LIBERO_VENV_PYTHON)
|
| 188 |
+
# Fall back to the current interpreter, but warn -- LIBERO needs its own venv.
|
| 189 |
+
print(
|
| 190 |
+
"WARNING: the dedicated LIBERO uv venv was not found at\n"
|
| 191 |
+
f" {LIBERO_VENV_PYTHON}\n"
|
| 192 |
+
"Falling back to the current Python interpreter. If LIBERO / robosuite\n"
|
| 193 |
+
"are not importable there, set up the sim env first:\n"
|
| 194 |
+
" sudo apt update && sudo apt install libegl1-mesa-dev libglu1-mesa\n"
|
| 195 |
+
" bash gr00t/eval/sim/LIBERO/setup_libero.sh\n",
|
| 196 |
+
file=sys.stderr,
|
| 197 |
+
)
|
| 198 |
+
return sys.executable
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
def _start_server(model_path: Path, host: str, port: int, log_path: Path):
|
| 202 |
+
cmd = [
|
| 203 |
+
sys.executable, str(REPO_ROOT / "gr00t" / "eval" / "run_gr00t_server.py"),
|
| 204 |
+
"--model-path", str(model_path),
|
| 205 |
+
"--embodiment-tag", "LIBERO_PANDA",
|
| 206 |
+
"--use-sim-policy-wrapper",
|
| 207 |
+
"--host", host,
|
| 208 |
+
"--port", str(port),
|
| 209 |
+
]
|
| 210 |
+
log_f = open(log_path, "w")
|
| 211 |
+
print(f"Starting GR00T server (logging to {log_path}):\n {' '.join(cmd)}")
|
| 212 |
+
proc = subprocess.Popen(cmd, stdout=log_f, stderr=subprocess.STDOUT, cwd=str(REPO_ROOT))
|
| 213 |
+
return proc, log_f
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
# --------------------------------------------------------------------------- #
|
| 217 |
+
# Main
|
| 218 |
+
# --------------------------------------------------------------------------- #
|
| 219 |
+
def main() -> int:
|
| 220 |
+
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
| 221 |
+
ap.add_argument("--model-path", default="checkpoints/GR00T-N1.7-LIBERO/libero_10")
|
| 222 |
+
ap.add_argument("--host", default="127.0.0.1")
|
| 223 |
+
ap.add_argument("--port", type=int, default=5555)
|
| 224 |
+
ap.add_argument("--manifest", default=str(DEFAULT_MANIFEST))
|
| 225 |
+
ap.add_argument("--output-dir", default="outputs/libero_smoke_tests")
|
| 226 |
+
ap.add_argument("--max-episode-steps", type=int, default=50,
|
| 227 |
+
help="Upper cap (and default) on max_episode_steps for every scenario.")
|
| 228 |
+
ap.add_argument("--n-action-steps", type=int, default=8)
|
| 229 |
+
ap.add_argument("--save-video", action="store_true",
|
| 230 |
+
help="Force-enable video recording for every scenario.")
|
| 231 |
+
ap.add_argument("--render", action="store_true",
|
| 232 |
+
help="Also dump decoded frames into <scenario>/frames/.")
|
| 233 |
+
ap.add_argument("--dry-run", action="store_true",
|
| 234 |
+
help="Validate manifest/model path/server, write metadata, run no sims.")
|
| 235 |
+
ap.add_argument("--resume", action="store_true",
|
| 236 |
+
help="Skip scenarios that already have a rollout_summary.json in the run dir.")
|
| 237 |
+
ap.add_argument("--run-dir", default=None,
|
| 238 |
+
help="Existing <output-dir>/<timestamp> dir to resume into "
|
| 239 |
+
"(default: create a new timestamped dir).")
|
| 240 |
+
ap.add_argument("--libero-python", default=None,
|
| 241 |
+
help="Path to the LIBERO uv venv python (default: auto-detect).")
|
| 242 |
+
ap.add_argument("--start-server", action="store_true",
|
| 243 |
+
help="Launch the GR00T server in a subprocess and kill it at the end.")
|
| 244 |
+
ap.add_argument("--per-scenario-timeout", type=int, default=900,
|
| 245 |
+
help="Hard timeout (seconds) for each scenario subprocess.")
|
| 246 |
+
args = ap.parse_args()
|
| 247 |
+
|
| 248 |
+
os.chdir(REPO_ROOT)
|
| 249 |
+
|
| 250 |
+
model_path = Path(args.model_path)
|
| 251 |
+
manifest_path = Path(args.manifest)
|
| 252 |
+
scenarios = _load_manifest(manifest_path)
|
| 253 |
+
|
| 254 |
+
print(f"Loaded {len(scenarios)} scenarios from {manifest_path}")
|
| 255 |
+
n_normal = sum(1 for s in scenarios if s.get("label") == "normal")
|
| 256 |
+
n_abnormal = sum(1 for s in scenarios if s.get("label") == "abnormal_probe")
|
| 257 |
+
print(f" labels: {n_normal} normal, {n_abnormal} abnormal_probe")
|
| 258 |
+
|
| 259 |
+
server_cmd_str = " ".join(_server_command(model_path))
|
| 260 |
+
print("\nExpected GR00T server command (run this in a separate terminal):\n " + server_cmd_str + "\n")
|
| 261 |
+
print("NOTE: " + GATED_BACKBONE_HINT)
|
| 262 |
+
|
| 263 |
+
# ---- validate model path -------------------------------------------------
|
| 264 |
+
_check_model_path(model_path)
|
| 265 |
+
print(f"Model checkpoint OK: {model_path}")
|
| 266 |
+
|
| 267 |
+
# ---- output dir ----------------------------------------------------------
|
| 268 |
+
out_root = Path(args.output_dir)
|
| 269 |
+
if args.run_dir:
|
| 270 |
+
run_dir = Path(args.run_dir)
|
| 271 |
+
run_dir.mkdir(parents=True, exist_ok=True)
|
| 272 |
+
else:
|
| 273 |
+
ts = _dt.datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 274 |
+
run_dir = out_root / ts
|
| 275 |
+
run_dir.mkdir(parents=True, exist_ok=True)
|
| 276 |
+
print(f"Run directory: {run_dir}")
|
| 277 |
+
|
| 278 |
+
# ---- optionally start server --------------------------------------------
|
| 279 |
+
server_proc = None
|
| 280 |
+
server_log_f = None
|
| 281 |
+
if args.start_server and not args.dry_run:
|
| 282 |
+
server_proc, server_log_f = _start_server(model_path, args.host, args.port,
|
| 283 |
+
run_dir / "server.log")
|
| 284 |
+
|
| 285 |
+
# ---- check server reachability ------------------------------------------
|
| 286 |
+
reachable = _ping_server(args.host, args.port)
|
| 287 |
+
if args.start_server and not args.dry_run and not reachable:
|
| 288 |
+
# give the server time to load the (multi-GB) model
|
| 289 |
+
print("Waiting for the GR00T server to come up (loading model can take a few minutes)...")
|
| 290 |
+
for _ in range(120):
|
| 291 |
+
time.sleep(5)
|
| 292 |
+
if server_proc is not None and server_proc.poll() is not None:
|
| 293 |
+
hint = ""
|
| 294 |
+
if _scan_log_for_gated_repo(run_dir / "server.log"):
|
| 295 |
+
hint = "\n\n" + GATED_BACKBONE_HINT
|
| 296 |
+
raise SystemExit(
|
| 297 |
+
f"The GR00T server process exited (rc={server_proc.returncode}); "
|
| 298 |
+
f"see {run_dir / 'server.log'}.{hint}"
|
| 299 |
+
)
|
| 300 |
+
if _ping_server(args.host, args.port):
|
| 301 |
+
reachable = True
|
| 302 |
+
break
|
| 303 |
+
|
| 304 |
+
if not reachable and not args.dry_run:
|
| 305 |
+
gated_hint = ""
|
| 306 |
+
if args.start_server and _scan_log_for_gated_repo(run_dir / "server.log"):
|
| 307 |
+
gated_hint = "\n" + GATED_BACKBONE_HINT
|
| 308 |
+
msg = (
|
| 309 |
+
f"\nERROR: no GR00T policy server is reachable at {args.host}:{args.port}.\n\n"
|
| 310 |
+
"Start it first in a separate terminal:\n\n " + server_cmd_str + "\n\n"
|
| 311 |
+
"...then re-run this script. (Or pass --start-server to have this script\n"
|
| 312 |
+
"launch it for you, or --dry-run to validate everything without running sims.)\n"
|
| 313 |
+
+ gated_hint
|
| 314 |
+
)
|
| 315 |
+
print(msg, file=sys.stderr)
|
| 316 |
+
return 2
|
| 317 |
+
if reachable:
|
| 318 |
+
print(f"GR00T server reachable at {args.host}:{args.port}.")
|
| 319 |
+
else:
|
| 320 |
+
print("(dry-run) skipping server reachability requirement.")
|
| 321 |
+
|
| 322 |
+
libero_python = _resolve_libero_python(args.libero_python)
|
| 323 |
+
print(f"LIBERO rollout interpreter: {libero_python}")
|
| 324 |
+
|
| 325 |
+
# ---- run scenarios -------------------------------------------------------
|
| 326 |
+
rows: list[dict] = []
|
| 327 |
+
try:
|
| 328 |
+
for i, sc in enumerate(scenarios, 1):
|
| 329 |
+
sid = sc["id"]
|
| 330 |
+
label = sc.get("label", "normal")
|
| 331 |
+
seed = int(sc.get("seed", 0))
|
| 332 |
+
# Keep a scenario's own (smaller) budget, but cap larger ones at --max-episode-steps.
|
| 333 |
+
mes = min(int(sc.get("max_episode_steps", args.max_episode_steps)), args.max_episode_steps)
|
| 334 |
+
n_action_steps = int(sc.get("n_action_steps", args.n_action_steps))
|
| 335 |
+
env_name = sc["env_name"]
|
| 336 |
+
save_video = bool(sc.get("save_video", True)) or args.save_video
|
| 337 |
+
obs_noise_std = float(sc.get("obs_noise_std", 0.0) or 0.0)
|
| 338 |
+
action_repeat = int(sc.get("action_repeat", 1) or 1)
|
| 339 |
+
instruction_override = sc.get("instruction_override")
|
| 340 |
+
|
| 341 |
+
sc_dir = run_dir / sid
|
| 342 |
+
sc_dir.mkdir(parents=True, exist_ok=True)
|
| 343 |
+
|
| 344 |
+
print(f"\n[{i}/{len(scenarios)}] {sid} ({label}) env={env_name} "
|
| 345 |
+
f"seed={seed} max_episode_steps={mes}")
|
| 346 |
+
|
| 347 |
+
# metadata.json (always written)
|
| 348 |
+
metadata = {
|
| 349 |
+
"scenario_id": sid,
|
| 350 |
+
"label": label,
|
| 351 |
+
"manifest_entry": sc,
|
| 352 |
+
"resolved": {
|
| 353 |
+
"env_name": env_name,
|
| 354 |
+
"seed": seed,
|
| 355 |
+
"max_episode_steps": mes,
|
| 356 |
+
"n_action_steps": n_action_steps,
|
| 357 |
+
"save_video": save_video,
|
| 358 |
+
"save_frames": bool(args.render),
|
| 359 |
+
"obs_noise_std": obs_noise_std,
|
| 360 |
+
"action_repeat": action_repeat,
|
| 361 |
+
"instruction_override": instruction_override,
|
| 362 |
+
},
|
| 363 |
+
"model_path": str(model_path),
|
| 364 |
+
"policy_server": {"host": args.host, "port": args.port},
|
| 365 |
+
"libero_python": libero_python,
|
| 366 |
+
"timestamp": _dt.datetime.now().isoformat(timespec="seconds"),
|
| 367 |
+
}
|
| 368 |
+
with open(sc_dir / "metadata.json", "w") as f:
|
| 369 |
+
json.dump(metadata, f, indent=2)
|
| 370 |
+
|
| 371 |
+
if instruction_override:
|
| 372 |
+
print(f" NOTE: instruction_override is set ({instruction_override!r}) but is not "
|
| 373 |
+
"yet wired into the LIBERO env path; it will be ignored by this rollout.")
|
| 374 |
+
|
| 375 |
+
# resume?
|
| 376 |
+
if args.resume and (sc_dir / "rollout_summary.json").exists():
|
| 377 |
+
try:
|
| 378 |
+
prev = json.loads((sc_dir / "rollout_summary.json").read_text())
|
| 379 |
+
except Exception:
|
| 380 |
+
prev = {}
|
| 381 |
+
print(f" --resume: existing rollout_summary.json found, skipping.")
|
| 382 |
+
rows.append({
|
| 383 |
+
"scenario_id": sid, "label": label, "seed": seed,
|
| 384 |
+
"rollout_started": prev.get("error") is None,
|
| 385 |
+
"actions_produced": bool(prev.get("actions_path")),
|
| 386 |
+
"video_saved": bool(prev.get("video_path")),
|
| 387 |
+
"success": prev.get("success", "unknown"),
|
| 388 |
+
"output_dir": str(sc_dir),
|
| 389 |
+
"error_if_any": prev.get("error"),
|
| 390 |
+
"resumed": True,
|
| 391 |
+
})
|
| 392 |
+
continue
|
| 393 |
+
|
| 394 |
+
if args.dry_run:
|
| 395 |
+
with open(sc_dir / "rollout_summary.json", "w") as f:
|
| 396 |
+
json.dump({"status": "dry_run", "env_name": env_name, "seed": seed}, f, indent=2)
|
| 397 |
+
rows.append({
|
| 398 |
+
"scenario_id": sid, "label": label, "seed": seed,
|
| 399 |
+
"rollout_started": False, "actions_produced": False, "video_saved": False,
|
| 400 |
+
"success": "unknown", "output_dir": str(sc_dir),
|
| 401 |
+
"error_if_any": "dry-run (no rollout executed)",
|
| 402 |
+
})
|
| 403 |
+
continue
|
| 404 |
+
|
| 405 |
+
# build worker command
|
| 406 |
+
cmd = [
|
| 407 |
+
libero_python, str(WORKER),
|
| 408 |
+
"--env-name", env_name,
|
| 409 |
+
"--host", args.host, "--port", str(args.port),
|
| 410 |
+
"--max-episode-steps", str(mes),
|
| 411 |
+
"--n-action-steps", str(n_action_steps),
|
| 412 |
+
"--seed", str(seed),
|
| 413 |
+
"--out-dir", str(sc_dir),
|
| 414 |
+
"--obs-noise-std", str(obs_noise_std),
|
| 415 |
+
"--action-repeat", str(action_repeat),
|
| 416 |
+
]
|
| 417 |
+
cmd += ["--save-video"] if save_video else ["--no-save-video"]
|
| 418 |
+
if args.render and save_video:
|
| 419 |
+
cmd += ["--save-frames"]
|
| 420 |
+
|
| 421 |
+
stdout_path = sc_dir / "stdout.log"
|
| 422 |
+
stderr_path = sc_dir / "stderr.log"
|
| 423 |
+
t0 = time.time()
|
| 424 |
+
try:
|
| 425 |
+
with open(stdout_path, "w") as so, open(stderr_path, "w") as se:
|
| 426 |
+
proc = subprocess.run(
|
| 427 |
+
cmd, stdout=so, stderr=se, cwd=str(REPO_ROOT),
|
| 428 |
+
timeout=args.per_scenario_timeout,
|
| 429 |
+
)
|
| 430 |
+
rc = proc.returncode
|
| 431 |
+
except subprocess.TimeoutExpired:
|
| 432 |
+
rc = -9
|
| 433 |
+
with open(stderr_path, "a") as se:
|
| 434 |
+
se.write(f"\n[runner] scenario timed out after {args.per_scenario_timeout}s\n")
|
| 435 |
+
elapsed = time.time() - t0
|
| 436 |
+
|
| 437 |
+
# parse worker result
|
| 438 |
+
result = None
|
| 439 |
+
try:
|
| 440 |
+
for line in reversed(stdout_path.read_text().splitlines()):
|
| 441 |
+
line = line.strip()
|
| 442 |
+
if line.startswith(RESULT_PREFIX):
|
| 443 |
+
result = json.loads(line[len(RESULT_PREFIX):].strip())
|
| 444 |
+
break
|
| 445 |
+
except Exception:
|
| 446 |
+
pass
|
| 447 |
+
if result is None:
|
| 448 |
+
err_tail = ""
|
| 449 |
+
try:
|
| 450 |
+
err_tail = "\n".join(stderr_path.read_text().splitlines()[-5:])
|
| 451 |
+
except Exception:
|
| 452 |
+
pass
|
| 453 |
+
result = {
|
| 454 |
+
"ok": False, "rollout_started": False, "actions_produced": False,
|
| 455 |
+
"video_saved": False, "success": None,
|
| 456 |
+
"error": (f"worker exited rc={rc} with no result line; "
|
| 457 |
+
f"see {stderr_path}. tail:\n{err_tail}"),
|
| 458 |
+
}
|
| 459 |
+
|
| 460 |
+
status = "ok" if result.get("ok") else "FAILED"
|
| 461 |
+
print(f" -> {status} rc={rc} {elapsed:.1f}s "
|
| 462 |
+
f"actions={'yes' if result.get('actions_produced') else 'no'} "
|
| 463 |
+
f"video={'yes' if result.get('video_saved') else 'no'} "
|
| 464 |
+
f"success={result.get('success')}")
|
| 465 |
+
if result.get("error"):
|
| 466 |
+
print(f" error: {result['error']}")
|
| 467 |
+
|
| 468 |
+
rows.append({
|
| 469 |
+
"scenario_id": sid, "label": label, "seed": seed,
|
| 470 |
+
"rollout_started": bool(result.get("rollout_started")),
|
| 471 |
+
"actions_produced": bool(result.get("actions_produced")),
|
| 472 |
+
"video_saved": bool(result.get("video_saved")),
|
| 473 |
+
"success": result.get("success") if result.get("success") is not None else "unknown",
|
| 474 |
+
"output_dir": str(sc_dir),
|
| 475 |
+
"error_if_any": result.get("error"),
|
| 476 |
+
"rc": rc,
|
| 477 |
+
"elapsed_sec": round(elapsed, 1),
|
| 478 |
+
"n_action_calls": result.get("n_action_calls"),
|
| 479 |
+
"episode_length": result.get("episode_length"),
|
| 480 |
+
"video_path": result.get("video_path"),
|
| 481 |
+
"actions_path": result.get("actions_path"),
|
| 482 |
+
})
|
| 483 |
+
finally:
|
| 484 |
+
if server_proc is not None:
|
| 485 |
+
print("\nStopping GR00T server subprocess...")
|
| 486 |
+
server_proc.terminate()
|
| 487 |
+
try:
|
| 488 |
+
server_proc.wait(timeout=15)
|
| 489 |
+
except subprocess.TimeoutExpired:
|
| 490 |
+
server_proc.kill()
|
| 491 |
+
if server_log_f is not None:
|
| 492 |
+
server_log_f.close()
|
| 493 |
+
|
| 494 |
+
# ---- summary -------------------------------------------------------------
|
| 495 |
+
summary = {
|
| 496 |
+
"run_dir": str(run_dir),
|
| 497 |
+
"manifest": str(manifest_path),
|
| 498 |
+
"model_path": str(model_path),
|
| 499 |
+
"policy_server": {"host": args.host, "port": args.port},
|
| 500 |
+
"server_command": server_cmd_str,
|
| 501 |
+
"dry_run": args.dry_run,
|
| 502 |
+
"n_scenarios": len(scenarios),
|
| 503 |
+
"n_normal": n_normal,
|
| 504 |
+
"n_abnormal_probe": n_abnormal,
|
| 505 |
+
"timestamp": _dt.datetime.now().isoformat(timespec="seconds"),
|
| 506 |
+
"scenarios": rows,
|
| 507 |
+
"totals": {
|
| 508 |
+
"rollout_started": sum(1 for r in rows if r.get("rollout_started")),
|
| 509 |
+
"actions_produced": sum(1 for r in rows if r.get("actions_produced")),
|
| 510 |
+
"video_saved": sum(1 for r in rows if r.get("video_saved")),
|
| 511 |
+
"errors": sum(1 for r in rows if r.get("error_if_any") and "dry-run" not in str(r.get("error_if_any"))),
|
| 512 |
+
},
|
| 513 |
+
}
|
| 514 |
+
write_summary(run_dir, summary)
|
| 515 |
+
print_table(rows)
|
| 516 |
+
print(f"\nWrote: {run_dir / 'summary.json'}")
|
| 517 |
+
print(f"Wrote: {run_dir / 'summary.md'}")
|
| 518 |
+
print(f"\nReview the videos with:\n python {Path(__file__).resolve().parent / 'review_smoke_tests.py'} "
|
| 519 |
+
f"--run-dir {run_dir}\n")
|
| 520 |
+
|
| 521 |
+
# exit code: 0 if dry-run or every scenario at least started; else 1
|
| 522 |
+
if args.dry_run:
|
| 523 |
+
return 0
|
| 524 |
+
failed = summary["totals"]["errors"]
|
| 525 |
+
if failed == 0:
|
| 526 |
+
return 0
|
| 527 |
+
print(f"{failed}/{len(scenarios)} scenario(s) reported an error -- exiting non-zero.", file=sys.stderr)
|
| 528 |
+
return 1
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
def write_summary(run_dir: Path, summary: dict) -> None:
|
| 532 |
+
"""Write summary.json and summary.md. Returns nothing; raises on JSON errors."""
|
| 533 |
+
with open(run_dir / "summary.json", "w") as f:
|
| 534 |
+
json.dump(summary, f, indent=2)
|
| 535 |
+
# markdown
|
| 536 |
+
lines = []
|
| 537 |
+
lines.append(f"# LIBERO smoke-test summary\n")
|
| 538 |
+
lines.append(f"- run dir: `{summary['run_dir']}`")
|
| 539 |
+
lines.append(f"- model: `{summary['model_path']}`")
|
| 540 |
+
lines.append(f"- server: `{summary['policy_server']['host']}:{summary['policy_server']['port']}`")
|
| 541 |
+
lines.append(f"- server command: `{summary['server_command']}`")
|
| 542 |
+
lines.append(f"- scenarios: {summary['n_scenarios']} ({summary['n_normal']} normal, "
|
| 543 |
+
f"{summary['n_abnormal_probe']} abnormal_probe)")
|
| 544 |
+
if summary.get("dry_run"):
|
| 545 |
+
lines.append(f"- **DRY RUN** (no rollouts executed)")
|
| 546 |
+
t = summary["totals"]
|
| 547 |
+
lines.append(f"- totals: rollout_started={t['rollout_started']}, actions_produced={t['actions_produced']}, "
|
| 548 |
+
f"video_saved={t['video_saved']}, errors={t['errors']}\n")
|
| 549 |
+
lines.append("| scenario_id | label | seed | rollout_started | actions_produced | video_saved | success | output_dir | error_if_any |")
|
| 550 |
+
lines.append("|---|---|---|---|---|---|---|---|---|")
|
| 551 |
+
for r in summary["scenarios"]:
|
| 552 |
+
err = (str(r.get("error_if_any")) or "").replace("\n", " ").replace("|", "\\|")
|
| 553 |
+
if len(err) > 160:
|
| 554 |
+
err = err[:157] + "..."
|
| 555 |
+
lines.append("| {id} | {label} | {seed} | {rs} | {ap} | {vs} | {succ} | {od} | {err} |".format(
|
| 556 |
+
id=r["scenario_id"], label=r["label"], seed=r["seed"],
|
| 557 |
+
rs="yes" if r.get("rollout_started") else "no",
|
| 558 |
+
ap="yes" if r.get("actions_produced") else "no",
|
| 559 |
+
vs="yes" if r.get("video_saved") else "no",
|
| 560 |
+
succ=r.get("success"), od=r["output_dir"], err=err or "",
|
| 561 |
+
))
|
| 562 |
+
(run_dir / "summary.md").write_text("\n".join(lines) + "\n")
|
| 563 |
+
|
| 564 |
+
|
| 565 |
+
def print_table(rows: list[dict]) -> None:
|
| 566 |
+
headers = ["scenario_id", "label", "seed", "rollout_started", "actions_produced",
|
| 567 |
+
"video_saved", "success", "output_dir", "error_if_any"]
|
| 568 |
+
def cell(r, h):
|
| 569 |
+
if h == "rollout_started":
|
| 570 |
+
return "yes" if r.get("rollout_started") else "no"
|
| 571 |
+
if h == "actions_produced":
|
| 572 |
+
return "yes" if r.get("actions_produced") else "no"
|
| 573 |
+
if h == "video_saved":
|
| 574 |
+
return "yes" if r.get("video_saved") else "no"
|
| 575 |
+
if h == "error_if_any":
|
| 576 |
+
e = str(r.get("error_if_any") or "")
|
| 577 |
+
e = e.replace("\n", " ")
|
| 578 |
+
return (e[:57] + "...") if len(e) > 60 else e
|
| 579 |
+
return str(r.get(h, ""))
|
| 580 |
+
table = [headers] + [[cell(r, h) for h in headers] for r in rows]
|
| 581 |
+
widths = [max(len(row[i]) for row in table) for i in range(len(headers))]
|
| 582 |
+
print("\n" + "=" * 8 + " SMOKE-TEST SUMMARY " + "=" * 8)
|
| 583 |
+
for ri, row in enumerate(table):
|
| 584 |
+
print(" " + " | ".join(c.ljust(widths[i]) for i, c in enumerate(row)))
|
| 585 |
+
if ri == 0:
|
| 586 |
+
print(" " + "-+-".join("-" * w for w in widths))
|
| 587 |
+
|
| 588 |
+
|
| 589 |
+
if __name__ == "__main__":
|
| 590 |
+
raise SystemExit(main())
|
examples/LIBERO/smoke_tests/scenarios_10.yaml
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# LIBERO simulation-only smoke-test manifest (10 scenarios)
|
| 2 |
+
#
|
| 3 |
+
# These are *short* rollouts intended to verify the deploy/eval plumbing for
|
| 4 |
+
# nvidia/GR00T-N1.7-LIBERO (libero_10 checkpoint). They are NOT a benchmark:
|
| 5 |
+
# max_episode_steps is tiny on purpose, so success rates here are meaningless.
|
| 6 |
+
#
|
| 7 |
+
# Scenario fields
|
| 8 |
+
# id : unique short identifier (used as the output sub-directory name)
|
| 9 |
+
# label : "normal" | "abnormal_probe"
|
| 10 |
+
# env_name : gymnasium id registered by gr00t.eval.sim.LIBERO.libero_env
|
| 11 |
+
# (register_libero_envs registers libero_10 / spatial / object / goal / 90)
|
| 12 |
+
# instruction : human-readable task instruction (informational; the *real*
|
| 13 |
+
# instruction is supplied by the LIBERO env itself)
|
| 14 |
+
# instruction_override : optional string; if set, overrides the env's task_description
|
| 15 |
+
# (used for instruction-mismatch probes). null = use env default.
|
| 16 |
+
# seed : RNG seed forwarded to env.reset()/seed_everything for reproducibility
|
| 17 |
+
# max_episode_steps : max inner-env steps for this rollout (kept small for a smoke test)
|
| 18 |
+
# n_action_steps : action chunk size executed per policy.get_action() call
|
| 19 |
+
# save_video : whether to record an mp4 / frames for human review
|
| 20 |
+
# obs_noise_std : optional float; std-dev of Gaussian noise added to image
|
| 21 |
+
# observations *before* policy inference (0 = disabled)
|
| 22 |
+
# action_repeat : optional int >= 1; if > 1, the previous action chunk is re-used
|
| 23 |
+
# instead of querying the policy on (action_repeat-1)/action_repeat
|
| 24 |
+
# of the steps (1 = disabled, i.e. normal behaviour)
|
| 25 |
+
# notes : free-form description
|
| 26 |
+
|
| 27 |
+
scenarios:
|
| 28 |
+
|
| 29 |
+
# -------------------- 8 normal scenarios --------------------
|
| 30 |
+
- id: normal_kitchen3_moka_pot_on_stove
|
| 31 |
+
label: normal
|
| 32 |
+
env_name: libero_sim/KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it
|
| 33 |
+
instruction: "turn on the stove and put the moka pot on it"
|
| 34 |
+
instruction_override: null
|
| 35 |
+
seed: 1000
|
| 36 |
+
max_episode_steps: 50
|
| 37 |
+
n_action_steps: 8
|
| 38 |
+
save_video: true
|
| 39 |
+
obs_noise_std: 0.0
|
| 40 |
+
action_repeat: 1
|
| 41 |
+
notes: "Baseline libero_10 long-horizon task; short rollout to check the full server<->client loop."
|
| 42 |
+
|
| 43 |
+
- id: normal_kitchen4_bowl_in_drawer
|
| 44 |
+
label: normal
|
| 45 |
+
env_name: libero_sim/KITCHEN_SCENE4_put_the_black_bowl_in_the_bottom_drawer_of_the_cabinet_and_close_it
|
| 46 |
+
instruction: "put the black bowl in the bottom drawer of the cabinet and close it"
|
| 47 |
+
instruction_override: null
|
| 48 |
+
seed: 1001
|
| 49 |
+
max_episode_steps: 50
|
| 50 |
+
n_action_steps: 8
|
| 51 |
+
save_video: true
|
| 52 |
+
obs_noise_std: 0.0
|
| 53 |
+
action_repeat: 1
|
| 54 |
+
notes: "Drawer manipulation task; verifies action decoding + sim stepping."
|
| 55 |
+
|
| 56 |
+
- id: normal_living1_soup_and_cheese_in_basket
|
| 57 |
+
label: normal
|
| 58 |
+
env_name: libero_sim/LIVING_ROOM_SCENE1_put_both_the_alphabet_soup_and_the_cream_cheese_box_in_the_basket
|
| 59 |
+
instruction: "put both the alphabet soup and the cream cheese box in the basket"
|
| 60 |
+
instruction_override: null
|
| 61 |
+
seed: 1002
|
| 62 |
+
max_episode_steps: 50
|
| 63 |
+
n_action_steps: 8
|
| 64 |
+
save_video: true
|
| 65 |
+
obs_noise_std: 0.0
|
| 66 |
+
action_repeat: 1
|
| 67 |
+
notes: "Multi-object pick-and-place; checks video recording wrapper output."
|
| 68 |
+
|
| 69 |
+
- id: normal_living2_soup_and_tomato_in_basket
|
| 70 |
+
label: normal
|
| 71 |
+
env_name: libero_sim/LIVING_ROOM_SCENE2_put_both_the_alphabet_soup_and_the_tomato_sauce_in_the_basket
|
| 72 |
+
instruction: "put both the alphabet soup and the tomato sauce in the basket"
|
| 73 |
+
instruction_override: null
|
| 74 |
+
seed: 1003
|
| 75 |
+
max_episode_steps: 50
|
| 76 |
+
n_action_steps: 8
|
| 77 |
+
save_video: true
|
| 78 |
+
obs_noise_std: 0.0
|
| 79 |
+
action_repeat: 1
|
| 80 |
+
notes: "Second living-room scene; different seed for variety."
|
| 81 |
+
|
| 82 |
+
- id: normal_study1_book_in_caddy
|
| 83 |
+
label: normal
|
| 84 |
+
env_name: libero_sim/STUDY_SCENE1_pick_up_the_book_and_place_it_in_the_back_compartment_of_the_caddy
|
| 85 |
+
instruction: "pick up the book and place it in the back compartment of the caddy"
|
| 86 |
+
instruction_override: null
|
| 87 |
+
seed: 1004
|
| 88 |
+
max_episode_steps: 50
|
| 89 |
+
n_action_steps: 8
|
| 90 |
+
save_video: true
|
| 91 |
+
obs_noise_std: 0.0
|
| 92 |
+
action_repeat: 1
|
| 93 |
+
notes: "Study scene; thin/awkward object grasp."
|
| 94 |
+
|
| 95 |
+
- id: normal_kitchen8_both_moka_pots_on_stove
|
| 96 |
+
label: normal
|
| 97 |
+
env_name: libero_sim/KITCHEN_SCENE8_put_both_moka_pots_on_the_stove
|
| 98 |
+
instruction: "put both moka pots on the stove"
|
| 99 |
+
instruction_override: null
|
| 100 |
+
seed: 1005
|
| 101 |
+
max_episode_steps: 50
|
| 102 |
+
n_action_steps: 8
|
| 103 |
+
save_video: true
|
| 104 |
+
obs_noise_std: 0.0
|
| 105 |
+
action_repeat: 1
|
| 106 |
+
notes: "Two-object task; checks repeated subgoal handling within a short window."
|
| 107 |
+
|
| 108 |
+
- id: normal_kitchen6_mug_in_microwave
|
| 109 |
+
label: normal
|
| 110 |
+
env_name: libero_sim/KITCHEN_SCENE6_put_the_yellow_and_white_mug_in_the_microwave_and_close_it
|
| 111 |
+
instruction: "put the yellow and white mug in the microwave and close it"
|
| 112 |
+
instruction_override: null
|
| 113 |
+
seed: 1006
|
| 114 |
+
max_episode_steps: 50
|
| 115 |
+
n_action_steps: 8
|
| 116 |
+
save_video: true
|
| 117 |
+
obs_noise_std: 0.0
|
| 118 |
+
action_repeat: 1
|
| 119 |
+
notes: "Articulated-object (microwave door) task."
|
| 120 |
+
|
| 121 |
+
- id: normal_living5_two_mugs_on_plates
|
| 122 |
+
label: normal
|
| 123 |
+
env_name: libero_sim/LIVING_ROOM_SCENE5_put_the_white_mug_on_the_left_plate_and_put_the_yellow_and_white_mug_on_the_right_plate
|
| 124 |
+
instruction: "put the white mug on the left plate and put the yellow and white mug on the right plate"
|
| 125 |
+
instruction_override: null
|
| 126 |
+
seed: 1007
|
| 127 |
+
max_episode_steps: 50
|
| 128 |
+
n_action_steps: 8
|
| 129 |
+
save_video: true
|
| 130 |
+
obs_noise_std: 0.0
|
| 131 |
+
action_repeat: 1
|
| 132 |
+
notes: "Long-horizon dual placement; longest of the normal set but still capped short."
|
| 133 |
+
|
| 134 |
+
# -------------------- 2 abnormal_probe scenarios --------------------
|
| 135 |
+
- id: abnormal_probe_obs_noise
|
| 136 |
+
label: abnormal_probe
|
| 137 |
+
env_name: libero_sim/KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it
|
| 138 |
+
instruction: "turn on the stove and put the moka pot on it"
|
| 139 |
+
instruction_override: null
|
| 140 |
+
seed: 2000
|
| 141 |
+
max_episode_steps: 50
|
| 142 |
+
n_action_steps: 8
|
| 143 |
+
save_video: true
|
| 144 |
+
obs_noise_std: 6.0
|
| 145 |
+
action_repeat: 1
|
| 146 |
+
notes: >-
|
| 147 |
+
Simulation-only perturbation: mild zero-mean Gaussian noise (sigma=6 in
|
| 148 |
+
0-255 pixel units) is added to the image observations *before* they are
|
| 149 |
+
sent to the policy. Smoke test only - this is a placeholder for future,
|
| 150 |
+
more principled distribution-shift probes; no complex anomaly logic.
|
| 151 |
+
|
| 152 |
+
- id: abnormal_probe_short_timeout
|
| 153 |
+
label: abnormal_probe
|
| 154 |
+
env_name: libero_sim/LIVING_ROOM_SCENE2_put_both_the_alphabet_soup_and_the_tomato_sauce_in_the_basket
|
| 155 |
+
instruction: "put both the alphabet soup and the tomato sauce in the basket"
|
| 156 |
+
instruction_override: null
|
| 157 |
+
seed: 2001
|
| 158 |
+
max_episode_steps: 16
|
| 159 |
+
n_action_steps: 8
|
| 160 |
+
save_video: true
|
| 161 |
+
obs_noise_std: 0.0
|
| 162 |
+
action_repeat: 1
|
| 163 |
+
notes: >-
|
| 164 |
+
Simulation-only perturbation: deliberately shortened episode budget
|
| 165 |
+
(16 inner steps ~= 2 action chunks) so the long-horizon task cannot
|
| 166 |
+
possibly complete. Verifies that truncation / "unknown success" is
|
| 167 |
+
reported cleanly rather than crashing.
|
examples/LIBERO/smoke_tests/visualize_smoke_run.py
ADDED
|
@@ -0,0 +1,739 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
| 5 |
+
"""Build an HTML visual-review report for a completed LIBERO smoke-test run.
|
| 6 |
+
|
| 7 |
+
Reads the artifacts produced by ``run_10_smoke_tests.py`` (summary.json,
|
| 8 |
+
per-scenario metadata.json / rollout_summary.json / actions.npy / video.mp4 /
|
| 9 |
+
frames/), computes a few simple action statistics, renders one card per
|
| 10 |
+
scenario (with a video preview, links to the raw files, and 4 small plots),
|
| 11 |
+
and writes:
|
| 12 |
+
|
| 13 |
+
<run-dir>/visual_report.html
|
| 14 |
+
<run-dir>/visual_summary.csv
|
| 15 |
+
<run-dir>/visual_summary.json
|
| 16 |
+
<run-dir>/plots/<scenario_id>/{action_norm,action_mean_per_dof,gripper_over_time,action_delta_norm}.png
|
| 17 |
+
|
| 18 |
+
It does NOT touch any existing rollout output, does NOT need a GPU, and does
|
| 19 |
+
NOT rerun LIBERO. Missing files become a warning inside the relevant card
|
| 20 |
+
instead of crashing.
|
| 21 |
+
|
| 22 |
+
CLI::
|
| 23 |
+
|
| 24 |
+
python examples/LIBERO/smoke_tests/visualize_smoke_run.py \
|
| 25 |
+
--run-dir outputs/libero_smoke_tests/20260512_122756 --open
|
| 26 |
+
|
| 27 |
+
Flags: --run-dir --open --no-plots --no-video-embed
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
from __future__ import annotations
|
| 31 |
+
|
| 32 |
+
import argparse
|
| 33 |
+
import base64
|
| 34 |
+
import csv
|
| 35 |
+
import datetime as _dt
|
| 36 |
+
import html
|
| 37 |
+
import json
|
| 38 |
+
import os
|
| 39 |
+
from pathlib import Path
|
| 40 |
+
import sys
|
| 41 |
+
import tempfile
|
| 42 |
+
import traceback
|
| 43 |
+
|
| 44 |
+
# matplotlib must be configured before import; use a headless backend + a
|
| 45 |
+
# writable config dir (the default ~/.config/matplotlib may be read-only).
|
| 46 |
+
os.environ.setdefault("MPLCONFIGDIR", tempfile.mkdtemp(prefix="mpl-visualize-"))
|
| 47 |
+
import numpy as np
|
| 48 |
+
|
| 49 |
+
_MPL_OK = True
|
| 50 |
+
_MPL_ERR = None
|
| 51 |
+
try:
|
| 52 |
+
import matplotlib
|
| 53 |
+
|
| 54 |
+
matplotlib.use("Agg")
|
| 55 |
+
import matplotlib.pyplot as plt
|
| 56 |
+
except Exception as e: # noqa: BLE001
|
| 57 |
+
_MPL_OK = False
|
| 58 |
+
_MPL_ERR = repr(e)
|
| 59 |
+
|
| 60 |
+
# The LIBERO action dict has 7 keys; `_libero_rollout_worker._action_to_numpy`
|
| 61 |
+
# concatenates them in *sorted (alphabetical)* key order, so the columns of
|
| 62 |
+
# actions.npy for this run are exactly:
|
| 63 |
+
LIBERO_DOF_NAMES = [
|
| 64 |
+
"action.gripper", # col 0
|
| 65 |
+
"action.pitch", # col 1
|
| 66 |
+
"action.roll", # col 2
|
| 67 |
+
"action.x", # col 3
|
| 68 |
+
"action.y", # col 4
|
| 69 |
+
"action.yaw", # col 5
|
| 70 |
+
"action.z", # col 6
|
| 71 |
+
]
|
| 72 |
+
LIBERO_GRIPPER_COL = 0
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
# --------------------------------------------------------------------------- #
|
| 76 |
+
# small IO helpers
|
| 77 |
+
# --------------------------------------------------------------------------- #
|
| 78 |
+
def _load_json(path: Path):
|
| 79 |
+
try:
|
| 80 |
+
with open(path) as f:
|
| 81 |
+
return json.load(f), None
|
| 82 |
+
except FileNotFoundError:
|
| 83 |
+
return None, f"missing file: {path.name}"
|
| 84 |
+
except Exception as e: # noqa: BLE001
|
| 85 |
+
return None, f"failed to read {path.name}: {e!r}"
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def _rel(path: Path, start: Path) -> str:
|
| 89 |
+
try:
|
| 90 |
+
return os.path.relpath(path, start)
|
| 91 |
+
except ValueError:
|
| 92 |
+
return str(path)
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def _fmt(v) -> str:
|
| 96 |
+
if v is None:
|
| 97 |
+
return "—"
|
| 98 |
+
if isinstance(v, float):
|
| 99 |
+
return f"{v:.4g}"
|
| 100 |
+
return str(v)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def _b64_data_uri(path: Path, mime: str) -> str | None:
|
| 104 |
+
try:
|
| 105 |
+
data = path.read_bytes()
|
| 106 |
+
except OSError:
|
| 107 |
+
return None
|
| 108 |
+
return f"data:{mime};base64," + base64.b64encode(data).decode("ascii")
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
# --------------------------------------------------------------------------- #
|
| 112 |
+
# action statistics
|
| 113 |
+
# --------------------------------------------------------------------------- #
|
| 114 |
+
def _squeeze_actions(arr: np.ndarray) -> np.ndarray:
|
| 115 |
+
"""Best-effort normalize an actions.npy array to shape (n_calls, horizon, n_dof).
|
| 116 |
+
|
| 117 |
+
The worker saves shape (n_calls, batch=1, horizon, n_dof). We also tolerate
|
| 118 |
+
(n_calls, horizon, n_dof) and (n_calls, n_dof).
|
| 119 |
+
"""
|
| 120 |
+
a = np.asarray(arr)
|
| 121 |
+
if a.dtype == object:
|
| 122 |
+
# list of heterogeneous things saved with allow_pickle; try to stack
|
| 123 |
+
try:
|
| 124 |
+
a = np.stack([np.asarray(x) for x in a], axis=0)
|
| 125 |
+
except Exception:
|
| 126 |
+
raise ValueError(f"cannot interpret object-dtype actions array of shape {arr.shape}")
|
| 127 |
+
if a.ndim == 4 and a.shape[1] == 1:
|
| 128 |
+
a = a[:, 0, :, :]
|
| 129 |
+
if a.ndim == 2: # (n_calls, n_dof) -> add a length-1 horizon
|
| 130 |
+
a = a[:, None, :]
|
| 131 |
+
if a.ndim != 3:
|
| 132 |
+
raise ValueError(f"unexpected actions array shape {arr.shape} (squeezed to {a.shape})")
|
| 133 |
+
return a.astype(np.float64)
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def _detect_gripper_col(acts3: np.ndarray) -> tuple[int, list[str]]:
|
| 137 |
+
"""Return (gripper_col_index, dof_names)."""
|
| 138 |
+
n_dof = acts3.shape[-1]
|
| 139 |
+
if n_dof == 7:
|
| 140 |
+
return LIBERO_GRIPPER_COL, list(LIBERO_DOF_NAMES)
|
| 141 |
+
names = [f"dof_{i}" for i in range(n_dof)]
|
| 142 |
+
# generic heuristic: the DoF whose values most often saturate near +/-1,
|
| 143 |
+
# falling back to the last column ("7th-style" gripper convention).
|
| 144 |
+
flat = acts3.reshape(-1, n_dof)
|
| 145 |
+
sat = (np.abs(flat) > 0.5).mean(axis=0)
|
| 146 |
+
col = int(np.argmax(sat)) if sat.size and sat.max() > 0.5 else n_dof - 1
|
| 147 |
+
return col, names
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def compute_action_stats(acts3: np.ndarray) -> dict:
|
| 151 |
+
"""acts3: (n_calls, horizon, n_dof) -> dict of arrays + scalars."""
|
| 152 |
+
n_calls, horizon, n_dof = acts3.shape
|
| 153 |
+
gripper_col, dof_names = _detect_gripper_col(acts3)
|
| 154 |
+
|
| 155 |
+
# per-policy-call norms
|
| 156 |
+
step0_norm = np.linalg.norm(acts3[:, 0, :], axis=-1) # (n_calls,) norm of the first executed step
|
| 157 |
+
chunk_mean = acts3.mean(axis=1) # (n_calls, n_dof) mean over the chunk
|
| 158 |
+
chunk_mean_norm = np.linalg.norm(chunk_mean, axis=-1) # (n_calls,)
|
| 159 |
+
per_step_norm = np.linalg.norm(acts3.reshape(-1, n_dof), axis=-1) # (n_calls*horizon,)
|
| 160 |
+
|
| 161 |
+
# mean (and std) action value per DoF over all calls x steps
|
| 162 |
+
flat = acts3.reshape(-1, n_dof)
|
| 163 |
+
mean_per_dof = flat.mean(axis=0)
|
| 164 |
+
std_per_dof = flat.std(axis=0)
|
| 165 |
+
|
| 166 |
+
# gripper command over time: value at chunk-step 0 per call
|
| 167 |
+
gripper_step0 = acts3[:, 0, gripper_col] # (n_calls,)
|
| 168 |
+
gripper_all = flat[:, gripper_col]
|
| 169 |
+
|
| 170 |
+
# action-delta norm between consecutive *chunks* (Frobenius over horizon x dof)
|
| 171 |
+
if n_calls >= 2:
|
| 172 |
+
delta = acts3[1:] - acts3[:-1] # (n_calls-1, horizon, n_dof)
|
| 173 |
+
delta_norm = np.linalg.norm(delta.reshape(n_calls - 1, -1), axis=-1)
|
| 174 |
+
else:
|
| 175 |
+
delta_norm = np.zeros((0,), dtype=np.float64)
|
| 176 |
+
|
| 177 |
+
return {
|
| 178 |
+
"n_calls": n_calls,
|
| 179 |
+
"horizon": horizon,
|
| 180 |
+
"n_dof": n_dof,
|
| 181 |
+
"dof_names": dof_names,
|
| 182 |
+
"gripper_col": gripper_col,
|
| 183 |
+
"step0_norm": step0_norm,
|
| 184 |
+
"chunk_mean_norm": chunk_mean_norm,
|
| 185 |
+
"per_step_norm": per_step_norm,
|
| 186 |
+
"mean_per_dof": mean_per_dof,
|
| 187 |
+
"std_per_dof": std_per_dof,
|
| 188 |
+
"gripper_step0": gripper_step0,
|
| 189 |
+
"gripper_all": gripper_all,
|
| 190 |
+
"delta_norm": delta_norm,
|
| 191 |
+
# scalars for the comparison table
|
| 192 |
+
"mean_action_norm": float(per_step_norm.mean()) if per_step_norm.size else None,
|
| 193 |
+
"max_action_norm": float(per_step_norm.max()) if per_step_norm.size else None,
|
| 194 |
+
"mean_delta_norm": float(delta_norm.mean()) if delta_norm.size else 0.0,
|
| 195 |
+
"gripper_min": float(gripper_all.min()) if gripper_all.size else None,
|
| 196 |
+
"gripper_max": float(gripper_all.max()) if gripper_all.size else None,
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
# --------------------------------------------------------------------------- #
|
| 201 |
+
# plotting
|
| 202 |
+
# --------------------------------------------------------------------------- #
|
| 203 |
+
def _save_fig(fig, path: Path) -> None:
|
| 204 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 205 |
+
fig.tight_layout()
|
| 206 |
+
fig.savefig(path, dpi=110)
|
| 207 |
+
plt.close(fig)
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
def make_plots(stats: dict, out_dir: Path, scenario_id: str) -> dict:
|
| 211 |
+
"""Write the 4 PNGs; return {logical_name: Path}. Requires matplotlib."""
|
| 212 |
+
out_dir.mkdir(parents=True, exist_ok=True)
|
| 213 |
+
calls = np.arange(stats["n_calls"])
|
| 214 |
+
paths: dict[str, Path] = {}
|
| 215 |
+
|
| 216 |
+
# 1) action norm over policy calls
|
| 217 |
+
fig, ax = plt.subplots(figsize=(5.6, 3.2))
|
| 218 |
+
ax.plot(calls, stats["step0_norm"], "o-", label="‖action‖ at chunk step 0")
|
| 219 |
+
ax.plot(calls, stats["chunk_mean_norm"], "s--", label="‖mean action over 16-step chunk‖")
|
| 220 |
+
ax.set_xlabel("policy call index")
|
| 221 |
+
ax.set_ylabel("L2 norm")
|
| 222 |
+
ax.set_title(f"{scenario_id}\naction L2 norm per policy call")
|
| 223 |
+
ax.grid(alpha=0.3)
|
| 224 |
+
ax.legend(fontsize=8)
|
| 225 |
+
p = out_dir / "action_norm.png"
|
| 226 |
+
_save_fig(fig, p)
|
| 227 |
+
paths["action_norm"] = p
|
| 228 |
+
|
| 229 |
+
# 2) mean action value per DoF (bar + std error bars)
|
| 230 |
+
fig, ax = plt.subplots(figsize=(5.6, 3.2))
|
| 231 |
+
x = np.arange(stats["n_dof"])
|
| 232 |
+
ax.bar(x, stats["mean_per_dof"], yerr=stats["std_per_dof"], capsize=3, color="#4C78A8")
|
| 233 |
+
ax.axhline(0.0, color="k", lw=0.6)
|
| 234 |
+
ax.set_xticks(x)
|
| 235 |
+
ax.set_xticklabels(stats["dof_names"], rotation=35, ha="right", fontsize=8)
|
| 236 |
+
ax.set_ylabel("mean value (±std) over all calls×steps")
|
| 237 |
+
gname = stats["dof_names"][stats["gripper_col"]]
|
| 238 |
+
ax.set_title(f"{scenario_id}\nmean action per DoF (gripper DoF = {gname}, col {stats['gripper_col']})")
|
| 239 |
+
ax.grid(alpha=0.3, axis="y")
|
| 240 |
+
p = out_dir / "action_mean_per_dof.png"
|
| 241 |
+
_save_fig(fig, p)
|
| 242 |
+
paths["action_mean_per_dof"] = p
|
| 243 |
+
|
| 244 |
+
# 3) gripper command over time
|
| 245 |
+
fig, ax = plt.subplots(figsize=(5.6, 3.2))
|
| 246 |
+
ax.step(calls, stats["gripper_step0"], where="post", marker="o", color="#E45756")
|
| 247 |
+
ax.set_ylim(min(-1.1, float(stats["gripper_step0"].min()) - 0.1) if stats["gripper_step0"].size else -1.1,
|
| 248 |
+
max(1.1, float(stats["gripper_step0"].max()) + 0.1) if stats["gripper_step0"].size else 1.1)
|
| 249 |
+
ax.set_xlabel("policy call index")
|
| 250 |
+
ax.set_ylabel(f"{gname} (chunk step 0)")
|
| 251 |
+
ax.set_title(f"{scenario_id}\ngripper command over time (DoF '{gname}', col {stats['gripper_col']})")
|
| 252 |
+
ax.grid(alpha=0.3)
|
| 253 |
+
p = out_dir / "gripper_over_time.png"
|
| 254 |
+
_save_fig(fig, p)
|
| 255 |
+
paths["gripper_over_time"] = p
|
| 256 |
+
|
| 257 |
+
# 4) action delta norm between consecutive chunks
|
| 258 |
+
fig, ax = plt.subplots(figsize=(5.6, 3.2))
|
| 259 |
+
if stats["delta_norm"].size:
|
| 260 |
+
ax.plot(np.arange(1, stats["n_calls"]), stats["delta_norm"], "o-", color="#54A24B")
|
| 261 |
+
else:
|
| 262 |
+
ax.text(0.5, 0.5, "only one policy call\n(no consecutive delta)", ha="center", va="center",
|
| 263 |
+
transform=ax.transAxes, fontsize=10)
|
| 264 |
+
ax.set_xlabel("policy call index i (delta between chunk i and i-1)")
|
| 265 |
+
ax.set_ylabel("‖Δ chunk‖_F (Frobenius over 16×n_dof)")
|
| 266 |
+
ax.set_title(f"{scenario_id}\naction delta norm between consecutive chunks")
|
| 267 |
+
ax.grid(alpha=0.3)
|
| 268 |
+
p = out_dir / "action_delta_norm.png"
|
| 269 |
+
_save_fig(fig, p)
|
| 270 |
+
paths["action_delta_norm"] = p
|
| 271 |
+
|
| 272 |
+
return paths
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
# --------------------------------------------------------------------------- #
|
| 276 |
+
# per-scenario processing
|
| 277 |
+
# --------------------------------------------------------------------------- #
|
| 278 |
+
def process_scenario(scenario_id: str, run_dir: Path, summary_row: dict | None,
|
| 279 |
+
make_plots_flag: bool, embed_video: bool) -> dict:
|
| 280 |
+
"""Return a dict with everything needed to render the card + table row."""
|
| 281 |
+
sc_dir = run_dir / scenario_id
|
| 282 |
+
warnings: list[str] = []
|
| 283 |
+
rec: dict = {"scenario_id": scenario_id, "scenario_dir": sc_dir, "warnings": warnings}
|
| 284 |
+
|
| 285 |
+
if not sc_dir.is_dir():
|
| 286 |
+
warnings.append(f"scenario directory not found: {sc_dir}")
|
| 287 |
+
rec.update(label=summary_row.get("label") if summary_row else None, seed=None,
|
| 288 |
+
instruction=None, success=summary_row.get("success") if summary_row else None,
|
| 289 |
+
max_episode_steps=None, num_policy_calls=None, actions_shape=None,
|
| 290 |
+
error=(summary_row or {}).get("error_if_any"), stats=None, plot_paths={},
|
| 291 |
+
video_path=None, frames_dir=None, n_frames=0,
|
| 292 |
+
files={}, video_data_uri=None)
|
| 293 |
+
return rec
|
| 294 |
+
|
| 295 |
+
meta, err = _load_json(sc_dir / "metadata.json")
|
| 296 |
+
if err:
|
| 297 |
+
warnings.append(err)
|
| 298 |
+
meta = {}
|
| 299 |
+
roll, err = _load_json(sc_dir / "rollout_summary.json")
|
| 300 |
+
if err:
|
| 301 |
+
warnings.append(err)
|
| 302 |
+
roll = {}
|
| 303 |
+
|
| 304 |
+
manifest_entry = (meta or {}).get("manifest_entry", {}) or {}
|
| 305 |
+
resolved = (meta or {}).get("resolved", {}) or {}
|
| 306 |
+
label = manifest_entry.get("label") or (meta or {}).get("label") or (summary_row or {}).get("label")
|
| 307 |
+
seed = manifest_entry.get("seed", resolved.get("seed", (summary_row or {}).get("seed")))
|
| 308 |
+
instruction = manifest_entry.get("instruction") or roll.get("instruction")
|
| 309 |
+
env_name = resolved.get("env_name") or manifest_entry.get("env_name") or roll.get("env_name")
|
| 310 |
+
max_episode_steps = (resolved.get("max_episode_steps")
|
| 311 |
+
or manifest_entry.get("max_episode_steps")
|
| 312 |
+
or (roll or {}).get("requested_max_episode_steps"))
|
| 313 |
+
success = roll.get("success", (summary_row or {}).get("success"))
|
| 314 |
+
error = (roll or {}).get("error") or (summary_row or {}).get("error_if_any")
|
| 315 |
+
num_policy_calls = (roll or {}).get("n_get_action_calls") or (summary_row or {}).get("n_action_calls")
|
| 316 |
+
obs_noise_std = resolved.get("obs_noise_std", manifest_entry.get("obs_noise_std"))
|
| 317 |
+
action_repeat = resolved.get("action_repeat", manifest_entry.get("action_repeat"))
|
| 318 |
+
notes = manifest_entry.get("notes")
|
| 319 |
+
|
| 320 |
+
# actions.npy
|
| 321 |
+
actions_shape = None
|
| 322 |
+
stats = None
|
| 323 |
+
plot_paths: dict[str, Path] = {}
|
| 324 |
+
actions_path = sc_dir / "actions.npy"
|
| 325 |
+
if actions_path.exists():
|
| 326 |
+
try:
|
| 327 |
+
arr = np.load(actions_path, allow_pickle=True)
|
| 328 |
+
actions_shape = tuple(int(x) for x in np.asarray(arr).shape) if np.asarray(arr).dtype != object else f"object[{len(arr)}]"
|
| 329 |
+
acts3 = _squeeze_actions(arr)
|
| 330 |
+
stats = compute_action_stats(acts3)
|
| 331 |
+
if num_policy_calls is None:
|
| 332 |
+
num_policy_calls = stats["n_calls"]
|
| 333 |
+
if make_plots_flag and _MPL_OK:
|
| 334 |
+
try:
|
| 335 |
+
plot_paths = make_plots(stats, run_dir / "plots" / scenario_id, scenario_id)
|
| 336 |
+
except Exception as e: # noqa: BLE001
|
| 337 |
+
warnings.append(f"plot generation failed: {e!r}")
|
| 338 |
+
elif make_plots_flag and not _MPL_OK:
|
| 339 |
+
warnings.append(f"matplotlib unavailable, plots skipped: {_MPL_ERR}")
|
| 340 |
+
except Exception as e: # noqa: BLE001
|
| 341 |
+
warnings.append(f"failed to load/parse actions.npy: {e!r}")
|
| 342 |
+
else:
|
| 343 |
+
warnings.append("missing file: actions.npy")
|
| 344 |
+
|
| 345 |
+
# video
|
| 346 |
+
video_path = sc_dir / "video.mp4"
|
| 347 |
+
video_present = video_path.exists()
|
| 348 |
+
if not video_present:
|
| 349 |
+
warnings.append("missing file: video.mp4")
|
| 350 |
+
video_data_uri = None
|
| 351 |
+
if video_present and embed_video:
|
| 352 |
+
video_data_uri = _b64_data_uri(video_path, "video/mp4")
|
| 353 |
+
if video_data_uri is None:
|
| 354 |
+
warnings.append("failed to base64-embed video.mp4")
|
| 355 |
+
|
| 356 |
+
# frames/
|
| 357 |
+
frames_dir = sc_dir / "frames"
|
| 358 |
+
n_frames = 0
|
| 359 |
+
first_frame_uri = None
|
| 360 |
+
if frames_dir.is_dir():
|
| 361 |
+
frame_files = sorted(frames_dir.glob("*.png"))
|
| 362 |
+
n_frames = len(frame_files)
|
| 363 |
+
if not video_present and frame_files:
|
| 364 |
+
first_frame_uri = _b64_data_uri(frame_files[0], "image/png")
|
| 365 |
+
# (frames/ is optional; only warn if neither video nor frames exist)
|
| 366 |
+
if not video_present and n_frames == 0:
|
| 367 |
+
warnings.append("no video.mp4 and no frames/ — nothing to preview")
|
| 368 |
+
|
| 369 |
+
# raw-file links (present-or-not)
|
| 370 |
+
files = {}
|
| 371 |
+
for name in ("metadata.json", "rollout_summary.json", "stdout.log", "stderr.log"):
|
| 372 |
+
p = sc_dir / name
|
| 373 |
+
files[name] = {"rel": _rel(p, run_dir), "exists": p.exists()}
|
| 374 |
+
if not p.exists():
|
| 375 |
+
warnings.append(f"missing file: {name}")
|
| 376 |
+
|
| 377 |
+
rec.update(
|
| 378 |
+
label=label, seed=seed, instruction=instruction, env_name=env_name,
|
| 379 |
+
success=success, max_episode_steps=max_episode_steps, num_policy_calls=num_policy_calls,
|
| 380 |
+
actions_shape=actions_shape, error=error, obs_noise_std=obs_noise_std,
|
| 381 |
+
action_repeat=action_repeat, notes=notes, stats=stats, plot_paths=plot_paths,
|
| 382 |
+
video_path=video_path if video_present else None, video_present=video_present,
|
| 383 |
+
video_data_uri=video_data_uri, frames_dir=frames_dir if frames_dir.is_dir() else None,
|
| 384 |
+
n_frames=n_frames, first_frame_uri=first_frame_uri, files=files,
|
| 385 |
+
output_dir=str((summary_row or {}).get("output_dir") or _rel(sc_dir, run_dir.parent.parent)),
|
| 386 |
+
elapsed_sec=(roll or {}).get("elapsed_sec", (summary_row or {}).get("elapsed_sec")),
|
| 387 |
+
video_saved=video_present,
|
| 388 |
+
)
|
| 389 |
+
return rec
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
# --------------------------------------------------------------------------- #
|
| 393 |
+
# HTML rendering
|
| 394 |
+
# --------------------------------------------------------------------------- #
|
| 395 |
+
_CSS = """
|
| 396 |
+
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
| 397 |
+
margin:0;padding:24px;background:#f4f5f7;color:#1a1a1a;}
|
| 398 |
+
h1{margin:0 0 4px 0;} .sub{color:#666;margin-bottom:20px;font-size:14px;}
|
| 399 |
+
.meta-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:6px 18px;
|
| 400 |
+
background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:14px 18px;margin-bottom:22px;font-size:13px;}
|
| 401 |
+
.meta-grid b{color:#444;}
|
| 402 |
+
table.cmp{border-collapse:collapse;width:100%;background:#fff;font-size:12.5px;margin-bottom:28px;
|
| 403 |
+
box-shadow:0 1px 3px rgba(0,0,0,.08);border-radius:8px;overflow:hidden;}
|
| 404 |
+
table.cmp th,table.cmp td{border-bottom:1px solid #eee;padding:7px 10px;text-align:left;white-space:nowrap;}
|
| 405 |
+
table.cmp th{background:#2d3748;color:#fff;position:sticky;top:0;}
|
| 406 |
+
table.cmp tr:hover{background:#f7f9fc;}
|
| 407 |
+
.badge{display:inline-block;padding:1px 8px;border-radius:10px;font-size:11px;font-weight:600;}
|
| 408 |
+
.b-normal{background:#e3f0ff;color:#1a5fb4;} .b-abn{background:#ffe9d6;color:#b35a00;}
|
| 409 |
+
.b-ok{background:#e6f6ea;color:#1a7f37;} .b-fail{background:#fde8e8;color:#b42318;} .b-unk{background:#eee;color:#555;}
|
| 410 |
+
.card{background:#fff;border:1px solid #e0e0e0;border-radius:10px;padding:18px;margin-bottom:22px;
|
| 411 |
+
box-shadow:0 1px 3px rgba(0,0,0,.06);}
|
| 412 |
+
.card h2{margin:0 0 2px 0;font-size:18px;} .card .cmeta{color:#555;font-size:13px;margin-bottom:10px;}
|
| 413 |
+
.card .cols{display:grid;grid-template-columns:minmax(280px,360px) 1fr;gap:18px;}
|
| 414 |
+
.kv{font-size:13px;line-height:1.7;} .kv b{color:#444;display:inline-block;min-width:130px;}
|
| 415 |
+
.plots{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:10px;}
|
| 416 |
+
.plots img{width:100%;border:1px solid #eee;border-radius:6px;background:#fff;}
|
| 417 |
+
video{width:100%;max-width:340px;border-radius:6px;background:#000;}
|
| 418 |
+
.warn{background:#fff8e1;border:1px solid #ffe082;border-radius:6px;padding:8px 12px;margin:8px 0;font-size:12.5px;color:#7a5c00;}
|
| 419 |
+
.err{background:#fdecea;border:1px solid #f5c6c0;border-radius:6px;padding:8px 12px;margin:8px 0;font-size:12.5px;color:#7a1f17;font-family:monospace;white-space:pre-wrap;}
|
| 420 |
+
.files a{margin-right:12px;font-size:12.5px;} .files .missing{color:#aaa;text-decoration:line-through;}
|
| 421 |
+
.notes{font-size:12.5px;color:#555;font-style:italic;margin-top:8px;}
|
| 422 |
+
code{background:#f0f0f0;padding:1px 4px;border-radius:3px;font-size:12px;}
|
| 423 |
+
"""
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
def _badge_label(label: str | None) -> str:
|
| 427 |
+
if label == "normal":
|
| 428 |
+
return '<span class="badge b-normal">normal</span>'
|
| 429 |
+
if label == "abnormal_probe":
|
| 430 |
+
return '<span class="badge b-abn">abnormal_probe</span>'
|
| 431 |
+
return f'<span class="badge b-unk">{html.escape(str(label))}</span>'
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
def _badge_success(s) -> str:
|
| 435 |
+
if s is True:
|
| 436 |
+
return '<span class="badge b-ok">success: true</span>'
|
| 437 |
+
if s is False:
|
| 438 |
+
return '<span class="badge b-fail">success: false</span>'
|
| 439 |
+
return '<span class="badge b-unk">success: unknown</span>'
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
def render_html(run_dir: Path, summary: dict | None, records: list[dict], table_rows: list[dict],
|
| 443 |
+
args) -> str:
|
| 444 |
+
parts: list[str] = []
|
| 445 |
+
parts.append("<!DOCTYPE html><html><head><meta charset='utf-8'>")
|
| 446 |
+
parts.append(f"<title>LIBERO smoke-test visual report — {html.escape(run_dir.name)}</title>")
|
| 447 |
+
parts.append(f"<style>{_CSS}</style></head><body>")
|
| 448 |
+
parts.append("<h1>LIBERO smoke-test visual review</h1>")
|
| 449 |
+
parts.append(f"<div class='sub'>run dir: <code>{html.escape(str(run_dir))}</code> | "
|
| 450 |
+
f"generated {html.escape(_dt.datetime.now().isoformat(timespec='seconds'))} | "
|
| 451 |
+
f"scenarios: {len(records)}</div>")
|
| 452 |
+
|
| 453 |
+
# run-level metadata
|
| 454 |
+
if summary:
|
| 455 |
+
t = summary.get("totals", {})
|
| 456 |
+
parts.append("<div class='meta-grid'>")
|
| 457 |
+
for k, v in [
|
| 458 |
+
("model_path", summary.get("model_path")),
|
| 459 |
+
("policy_server", f"{summary.get('policy_server',{}).get('host')}:{summary.get('policy_server',{}).get('port')}"),
|
| 460 |
+
("manifest", summary.get("manifest")),
|
| 461 |
+
("server_command", summary.get("server_command")),
|
| 462 |
+
("n_scenarios", summary.get("n_scenarios")),
|
| 463 |
+
("n_normal / n_abnormal_probe", f"{summary.get('n_normal')} / {summary.get('n_abnormal_probe')}"),
|
| 464 |
+
("rollout_started / actions_produced / video_saved / errors",
|
| 465 |
+
f"{t.get('rollout_started')} / {t.get('actions_produced')} / {t.get('video_saved')} / {t.get('errors')}"),
|
| 466 |
+
("run timestamp", summary.get("timestamp")),
|
| 467 |
+
("dry_run", summary.get("dry_run")),
|
| 468 |
+
]:
|
| 469 |
+
parts.append(f"<div><b>{html.escape(str(k))}:</b> {html.escape(_fmt(v))}</div>")
|
| 470 |
+
parts.append("</div>")
|
| 471 |
+
if not _MPL_OK:
|
| 472 |
+
parts.append(f"<div class='warn'>matplotlib not available ({html.escape(str(_MPL_ERR))}) — plots were skipped.</div>")
|
| 473 |
+
if args.no_plots:
|
| 474 |
+
parts.append("<div class='warn'>--no-plots was set — no PNG plots were generated this run.</div>")
|
| 475 |
+
|
| 476 |
+
# comparison table
|
| 477 |
+
parts.append("<h2>Comparison across all scenarios</h2>")
|
| 478 |
+
cols = ["scenario_id", "label", "seed", "success", "num_policy_calls", "video_saved",
|
| 479 |
+
"mean_action_norm", "max_action_norm", "mean_delta_norm", "gripper_min", "gripper_max", "output_dir"]
|
| 480 |
+
parts.append("<table class='cmp'><thead><tr>" + "".join(f"<th>{html.escape(c)}</th>" for c in cols) + "</tr></thead><tbody>")
|
| 481 |
+
for row in table_rows:
|
| 482 |
+
parts.append("<tr>")
|
| 483 |
+
for c in cols:
|
| 484 |
+
v = row.get(c)
|
| 485 |
+
if c == "label":
|
| 486 |
+
cell = _badge_label(v)
|
| 487 |
+
elif c == "success":
|
| 488 |
+
cell = _badge_success(v)
|
| 489 |
+
elif c == "scenario_id":
|
| 490 |
+
cell = f"<a href='#{html.escape(str(v))}'>{html.escape(str(v))}</a>"
|
| 491 |
+
elif c in ("mean_action_norm", "max_action_norm", "mean_delta_norm", "gripper_min", "gripper_max"):
|
| 492 |
+
cell = "—" if v is None else f"{float(v):.4g}"
|
| 493 |
+
else:
|
| 494 |
+
cell = html.escape(_fmt(v))
|
| 495 |
+
parts.append(f"<td>{cell}</td>")
|
| 496 |
+
parts.append("</tr>")
|
| 497 |
+
parts.append("</tbody></table>")
|
| 498 |
+
|
| 499 |
+
# one card per scenario
|
| 500 |
+
for rec in records:
|
| 501 |
+
sid = rec["scenario_id"]
|
| 502 |
+
parts.append(f"<div class='card' id='{html.escape(sid)}'>")
|
| 503 |
+
parts.append(f"<h2>{html.escape(sid)}</h2>")
|
| 504 |
+
parts.append("<div class='cmeta'>" + _badge_label(rec.get("label")) + " " + _badge_success(rec.get("success"))
|
| 505 |
+
+ f" seed={html.escape(_fmt(rec.get('seed')))}"
|
| 506 |
+
+ (f" env=<code>{html.escape(str(rec.get('env_name')))}</code>" if rec.get("env_name") else "")
|
| 507 |
+
+ "</div>")
|
| 508 |
+
if rec.get("error"):
|
| 509 |
+
parts.append(f"<div class='err'>error: {html.escape(str(rec['error']))}</div>")
|
| 510 |
+
for w in rec.get("warnings", []):
|
| 511 |
+
parts.append(f"<div class='warn'>⚠ {html.escape(str(w))}</div>")
|
| 512 |
+
|
| 513 |
+
parts.append("<div class='cols'>")
|
| 514 |
+
|
| 515 |
+
# left column: key/values + video + file links
|
| 516 |
+
parts.append("<div>")
|
| 517 |
+
st = rec.get("stats") or {}
|
| 518 |
+
kv = [
|
| 519 |
+
("scenario_id", sid),
|
| 520 |
+
("label", rec.get("label")),
|
| 521 |
+
("seed", rec.get("seed")),
|
| 522 |
+
("instruction", rec.get("instruction")),
|
| 523 |
+
("success", rec.get("success")),
|
| 524 |
+
("max_episode_steps", rec.get("max_episode_steps")),
|
| 525 |
+
("num policy calls", rec.get("num_policy_calls")),
|
| 526 |
+
("actions.npy shape", rec.get("actions_shape")),
|
| 527 |
+
("action horizon × DoF", f"{st.get('horizon')} × {st.get('n_dof')}" if st else "—"),
|
| 528 |
+
("gripper DoF (col)", (f"{st['dof_names'][st['gripper_col']]} (col {st['gripper_col']})" if st else "—")),
|
| 529 |
+
("mean / max ‖action‖", f"{_fmt(st.get('mean_action_norm'))} / {_fmt(st.get('max_action_norm'))}" if st else "—"),
|
| 530 |
+
("mean ‖Δ chunk‖", _fmt(st.get("mean_delta_norm")) if st else "—"),
|
| 531 |
+
("gripper min / max", f"{_fmt(st.get('gripper_min'))} / {_fmt(st.get('gripper_max'))}" if st else "—"),
|
| 532 |
+
("obs_noise_std", rec.get("obs_noise_std")),
|
| 533 |
+
("action_repeat", rec.get("action_repeat")),
|
| 534 |
+
("elapsed_sec", rec.get("elapsed_sec")),
|
| 535 |
+
("frames", (f"{rec.get('n_frames')} PNGs" if rec.get("n_frames") else "none")),
|
| 536 |
+
]
|
| 537 |
+
parts.append("<div class='kv'>")
|
| 538 |
+
for k, v in kv:
|
| 539 |
+
parts.append(f"<div><b>{html.escape(str(k))}:</b> {html.escape(_fmt(v))}</div>")
|
| 540 |
+
parts.append("</div>")
|
| 541 |
+
|
| 542 |
+
# video preview
|
| 543 |
+
parts.append("<div style='margin-top:10px'>")
|
| 544 |
+
if rec.get("video_data_uri"):
|
| 545 |
+
parts.append(f"<video controls preload='metadata' src='{rec['video_data_uri']}'></video>")
|
| 546 |
+
elif rec.get("video_present"):
|
| 547 |
+
vrel = _rel(rec["video_path"], run_dir)
|
| 548 |
+
parts.append(f"<video controls preload='metadata' src='{html.escape(vrel)}'></video>"
|
| 549 |
+
f"<div style='font-size:12px'><a href='{html.escape(vrel)}'>{html.escape(vrel)}</a></div>")
|
| 550 |
+
elif rec.get("first_frame_uri"):
|
| 551 |
+
parts.append(f"<img src='{rec['first_frame_uri']}' style='max-width:340px;border-radius:6px'/>"
|
| 552 |
+
f"<div class='warn'>no video.mp4 — showing first frame only</div>")
|
| 553 |
+
else:
|
| 554 |
+
parts.append("<div class='warn'>no video / frames available to preview</div>")
|
| 555 |
+
parts.append("</div>")
|
| 556 |
+
|
| 557 |
+
# file links
|
| 558 |
+
parts.append("<div class='files' style='margin-top:10px'>")
|
| 559 |
+
for name, info in (rec.get("files") or {}).items():
|
| 560 |
+
if info["exists"]:
|
| 561 |
+
parts.append(f"<a href='{html.escape(info['rel'])}'>{html.escape(name)}</a>")
|
| 562 |
+
else:
|
| 563 |
+
parts.append(f"<span class='missing'>{html.escape(name)}</span>")
|
| 564 |
+
if rec.get("frames_dir"):
|
| 565 |
+
parts.append(f"<a href='{html.escape(_rel(rec['frames_dir'], run_dir))}/'>frames/ ({rec.get('n_frames')})</a>")
|
| 566 |
+
parts.append("</div>")
|
| 567 |
+
if rec.get("notes"):
|
| 568 |
+
parts.append(f"<div class='notes'>note: {html.escape(str(rec['notes']))}</div>")
|
| 569 |
+
parts.append("</div>") # end left column
|
| 570 |
+
|
| 571 |
+
# right column: plots
|
| 572 |
+
parts.append("<div>")
|
| 573 |
+
if rec.get("plot_paths"):
|
| 574 |
+
parts.append("<div class='plots'>")
|
| 575 |
+
for logical in ("action_norm", "action_mean_per_dof", "gripper_over_time", "action_delta_norm"):
|
| 576 |
+
p = rec["plot_paths"].get(logical)
|
| 577 |
+
if p and Path(p).exists():
|
| 578 |
+
parts.append(f"<a href='{html.escape(_rel(Path(p), run_dir))}'>"
|
| 579 |
+
f"<img src='{html.escape(_rel(Path(p), run_dir))}' alt='{logical}'/></a>")
|
| 580 |
+
else:
|
| 581 |
+
parts.append(f"<div class='warn'>plot '{logical}.png' not available</div>")
|
| 582 |
+
parts.append("</div>")
|
| 583 |
+
elif rec.get("stats") is None:
|
| 584 |
+
parts.append("<div class='warn'>no actions.npy — no action statistics / plots</div>")
|
| 585 |
+
else:
|
| 586 |
+
parts.append("<div class='warn'>plots not generated (use without --no-plots, and ensure matplotlib is installed)</div>")
|
| 587 |
+
parts.append("</div>") # end right column
|
| 588 |
+
|
| 589 |
+
parts.append("</div>") # end cols
|
| 590 |
+
parts.append("</div>") # end card
|
| 591 |
+
|
| 592 |
+
parts.append("</body></html>")
|
| 593 |
+
return "\n".join(parts)
|
| 594 |
+
|
| 595 |
+
|
| 596 |
+
# --------------------------------------------------------------------------- #
|
| 597 |
+
# main
|
| 598 |
+
# --------------------------------------------------------------------------- #
|
| 599 |
+
def _discover_scenarios(run_dir: Path, summary: dict | None) -> tuple[list[str], dict[str, dict]]:
|
| 600 |
+
rows_by_id: dict[str, dict] = {}
|
| 601 |
+
order: list[str] = []
|
| 602 |
+
if summary and isinstance(summary.get("scenarios"), list):
|
| 603 |
+
for r in summary["scenarios"]:
|
| 604 |
+
sid = r.get("scenario_id")
|
| 605 |
+
if sid:
|
| 606 |
+
rows_by_id[sid] = r
|
| 607 |
+
order.append(sid)
|
| 608 |
+
# add any scenario subdirs not already listed
|
| 609 |
+
skip = {"plots"}
|
| 610 |
+
for p in sorted(run_dir.iterdir()):
|
| 611 |
+
if p.is_dir() and p.name not in skip and p.name not in order:
|
| 612 |
+
order.append(p.name)
|
| 613 |
+
return order, rows_by_id
|
| 614 |
+
|
| 615 |
+
|
| 616 |
+
def write_table_outputs(run_dir: Path, table_rows: list[dict]) -> tuple[Path, Path]:
|
| 617 |
+
cols = ["scenario_id", "label", "seed", "success", "num_policy_calls", "video_saved",
|
| 618 |
+
"mean_action_norm", "max_action_norm", "mean_delta_norm", "gripper_min", "gripper_max", "output_dir"]
|
| 619 |
+
csv_path = run_dir / "visual_summary.csv"
|
| 620 |
+
with open(csv_path, "w", newline="") as f:
|
| 621 |
+
w = csv.DictWriter(f, fieldnames=cols)
|
| 622 |
+
w.writeheader()
|
| 623 |
+
for row in table_rows:
|
| 624 |
+
w.writerow({c: ("" if row.get(c) is None else row.get(c)) for c in cols})
|
| 625 |
+
json_path = run_dir / "visual_summary.json"
|
| 626 |
+
with open(json_path, "w") as f:
|
| 627 |
+
json.dump({"run_dir": str(run_dir), "n_scenarios": len(table_rows),
|
| 628 |
+
"generated": _dt.datetime.now().isoformat(timespec="seconds"),
|
| 629 |
+
"columns": cols, "rows": table_rows}, f, indent=2)
|
| 630 |
+
return csv_path, json_path
|
| 631 |
+
|
| 632 |
+
|
| 633 |
+
def main() -> int:
|
| 634 |
+
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
| 635 |
+
ap.add_argument("--run-dir", required=True, help="Path to the completed run dir (contains summary.json + scenario subdirs).")
|
| 636 |
+
ap.add_argument("--open", action="store_true", help="Open the generated HTML report in a browser.")
|
| 637 |
+
ap.add_argument("--no-plots", action="store_true", help="Do not (re)generate the PNG plots.")
|
| 638 |
+
ap.add_argument("--no-video-embed", action="store_true", help="Link videos by relative path instead of base64-embedding them in the HTML.")
|
| 639 |
+
args = ap.parse_args()
|
| 640 |
+
|
| 641 |
+
run_dir = Path(args.run_dir).resolve()
|
| 642 |
+
if not run_dir.is_dir():
|
| 643 |
+
print(f"ERROR: --run-dir does not exist or is not a directory: {run_dir}", file=sys.stderr)
|
| 644 |
+
return 2
|
| 645 |
+
|
| 646 |
+
summary, serr = _load_json(run_dir / "summary.json")
|
| 647 |
+
if serr:
|
| 648 |
+
print(f"WARNING: {serr} — proceeding by scanning scenario subdirectories.", file=sys.stderr)
|
| 649 |
+
# summary.md is read only to confirm it exists / surface it; we don't parse it.
|
| 650 |
+
summary_md_present = (run_dir / "summary.md").exists()
|
| 651 |
+
if not summary_md_present:
|
| 652 |
+
print("WARNING: summary.md not found (continuing).", file=sys.stderr)
|
| 653 |
+
|
| 654 |
+
order, rows_by_id = _discover_scenarios(run_dir, summary)
|
| 655 |
+
if not order:
|
| 656 |
+
print(f"ERROR: no scenarios found under {run_dir}", file=sys.stderr)
|
| 657 |
+
return 2
|
| 658 |
+
print(f"Found {len(order)} scenario(s) in {run_dir}")
|
| 659 |
+
|
| 660 |
+
make_plots_flag = not args.no_plots
|
| 661 |
+
embed_video = not args.no_video_embed
|
| 662 |
+
|
| 663 |
+
records: list[dict] = []
|
| 664 |
+
table_rows: list[dict] = []
|
| 665 |
+
all_warnings: list[str] = []
|
| 666 |
+
n_videos_embedded = 0
|
| 667 |
+
n_videos_present = 0
|
| 668 |
+
for sid in order:
|
| 669 |
+
try:
|
| 670 |
+
rec = process_scenario(sid, run_dir, rows_by_id.get(sid), make_plots_flag, embed_video)
|
| 671 |
+
except Exception as e: # noqa: BLE001
|
| 672 |
+
tb = traceback.format_exc()
|
| 673 |
+
print(f"WARNING: scenario {sid} raised {e!r}; recording as failed card.", file=sys.stderr)
|
| 674 |
+
rec = {"scenario_id": sid, "warnings": [f"internal error: {e!r}", tb], "stats": None,
|
| 675 |
+
"plot_paths": {}, "files": {}, "label": None, "seed": None, "success": None,
|
| 676 |
+
"video_present": False, "video_data_uri": None, "n_frames": 0}
|
| 677 |
+
records.append(rec)
|
| 678 |
+
all_warnings += [f"[{sid}] {w}" for w in rec.get("warnings", [])]
|
| 679 |
+
if rec.get("video_present"):
|
| 680 |
+
n_videos_present += 1
|
| 681 |
+
if rec.get("video_data_uri"):
|
| 682 |
+
n_videos_embedded += 1
|
| 683 |
+
st = rec.get("stats") or {}
|
| 684 |
+
table_rows.append({
|
| 685 |
+
"scenario_id": sid,
|
| 686 |
+
"label": rec.get("label"),
|
| 687 |
+
"seed": rec.get("seed"),
|
| 688 |
+
"success": rec.get("success"),
|
| 689 |
+
"num_policy_calls": rec.get("num_policy_calls"),
|
| 690 |
+
"video_saved": bool(rec.get("video_present")),
|
| 691 |
+
"mean_action_norm": st.get("mean_action_norm"),
|
| 692 |
+
"max_action_norm": st.get("max_action_norm"),
|
| 693 |
+
"mean_delta_norm": st.get("mean_delta_norm"),
|
| 694 |
+
"gripper_min": st.get("gripper_min"),
|
| 695 |
+
"gripper_max": st.get("gripper_max"),
|
| 696 |
+
"output_dir": rec.get("output_dir") or str(run_dir / sid),
|
| 697 |
+
})
|
| 698 |
+
|
| 699 |
+
csv_path, json_path = write_table_outputs(run_dir, table_rows)
|
| 700 |
+
html_str = render_html(run_dir, summary, records, table_rows, args)
|
| 701 |
+
html_path = run_dir / "visual_report.html"
|
| 702 |
+
html_path.write_text(html_str, encoding="utf-8")
|
| 703 |
+
|
| 704 |
+
# console summary
|
| 705 |
+
print(f"\nWrote: {html_path}")
|
| 706 |
+
print(f"Wrote: {csv_path}")
|
| 707 |
+
print(f"Wrote: {json_path}")
|
| 708 |
+
if make_plots_flag and _MPL_OK:
|
| 709 |
+
print(f"Plots: {run_dir / 'plots'}/<scenario_id>/*.png")
|
| 710 |
+
print(f"Videos: {n_videos_present}/{len(order)} present; "
|
| 711 |
+
f"{'all ' if n_videos_embedded == len(order) and embed_video else ''}"
|
| 712 |
+
f"{n_videos_embedded}/{len(order)} embedded in HTML"
|
| 713 |
+
+ ("" if embed_video else " (--no-video-embed: linked by path instead)"))
|
| 714 |
+
if all_warnings:
|
| 715 |
+
print(f"\n{len(all_warnings)} warning(s):")
|
| 716 |
+
for w in all_warnings:
|
| 717 |
+
print(f" - {w}")
|
| 718 |
+
else:
|
| 719 |
+
print("\nNo missing files or warnings.")
|
| 720 |
+
|
| 721 |
+
if args.open:
|
| 722 |
+
import webbrowser
|
| 723 |
+
|
| 724 |
+
url = html_path.as_uri()
|
| 725 |
+
opened = False
|
| 726 |
+
try:
|
| 727 |
+
opened = webbrowser.open(url)
|
| 728 |
+
except Exception:
|
| 729 |
+
opened = False
|
| 730 |
+
if opened:
|
| 731 |
+
print(f"\nOpened {url} in a browser.")
|
| 732 |
+
else:
|
| 733 |
+
print(f"\nCould not auto-open a browser. Open this file manually:\n {html_path}")
|
| 734 |
+
|
| 735 |
+
return 0
|
| 736 |
+
|
| 737 |
+
|
| 738 |
+
if __name__ == "__main__":
|
| 739 |
+
raise SystemExit(main())
|
outputs/_setup_logs/_dryrun.log
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Loaded 10 scenarios from /home/ubuntu/VLA_SAE/examples/LIBERO/smoke_tests/scenarios_10.yaml
|
| 2 |
+
labels: 8 normal, 2 abnormal_probe
|
| 3 |
+
|
| 4 |
+
Expected GR00T server command (run this in a separate terminal):
|
| 5 |
+
uv run python gr00t/eval/run_gr00t_server.py --model-path checkpoints/GR00T-N1.7-LIBERO/libero_10 --embodiment-tag LIBERO_PANDA --use-sim-policy-wrapper
|
| 6 |
+
|
| 7 |
+
NOTE: The GR00T-N1.7 backbone 'nvidia/Cosmos-Reason2-2B' is a GATED HuggingFace repo.
|
| 8 |
+
To start the server you must:
|
| 9 |
+
1. Request access at https://huggingface.co/nvidia/Cosmos-Reason2-2B (one click, usually instant).
|
| 10 |
+
2. Authenticate, e.g. export HF_TOKEN=hf_xxx (or: uv run hf auth login)
|
| 11 |
+
3. Re-run the server / smoke tests.
|
| 12 |
+
|
| 13 |
+
Model checkpoint OK: checkpoints/GR00T-N1.7-LIBERO/libero_10
|
| 14 |
+
Run directory: outputs/libero_smoke_tests/20260512_120217
|
| 15 |
+
(dry-run) skipping server reachability requirement.
|
| 16 |
+
LIBERO rollout interpreter: /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/bin/python
|
| 17 |
+
|
| 18 |
+
[1/10] normal_kitchen3_moka_pot_on_stove (normal) env=libero_sim/KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it seed=1000 max_episode_steps=50
|
| 19 |
+
|
| 20 |
+
[2/10] normal_kitchen4_bowl_in_drawer (normal) env=libero_sim/KITCHEN_SCENE4_put_the_black_bowl_in_the_bottom_drawer_of_the_cabinet_and_close_it seed=1001 max_episode_steps=50
|
| 21 |
+
|
| 22 |
+
[3/10] normal_living1_soup_and_cheese_in_basket (normal) env=libero_sim/LIVING_ROOM_SCENE1_put_both_the_alphabet_soup_and_the_cream_cheese_box_in_the_basket seed=1002 max_episode_steps=50
|
| 23 |
+
|
| 24 |
+
[4/10] normal_living2_soup_and_tomato_in_basket (normal) env=libero_sim/LIVING_ROOM_SCENE2_put_both_the_alphabet_soup_and_the_tomato_sauce_in_the_basket seed=1003 max_episode_steps=50
|
| 25 |
+
|
| 26 |
+
[5/10] normal_study1_book_in_caddy (normal) env=libero_sim/STUDY_SCENE1_pick_up_the_book_and_place_it_in_the_back_compartment_of_the_caddy seed=1004 max_episode_steps=50
|
| 27 |
+
|
| 28 |
+
[6/10] normal_kitchen8_both_moka_pots_on_stove (normal) env=libero_sim/KITCHEN_SCENE8_put_both_moka_pots_on_the_stove seed=1005 max_episode_steps=50
|
| 29 |
+
|
| 30 |
+
[7/10] normal_kitchen6_mug_in_microwave (normal) env=libero_sim/KITCHEN_SCENE6_put_the_yellow_and_white_mug_in_the_microwave_and_close_it seed=1006 max_episode_steps=50
|
| 31 |
+
|
| 32 |
+
[8/10] normal_living5_two_mugs_on_plates (normal) env=libero_sim/LIVING_ROOM_SCENE5_put_the_white_mug_on_the_left_plate_and_put_the_yellow_and_white_mug_on_the_right_plate seed=1007 max_episode_steps=50
|
| 33 |
+
|
| 34 |
+
[9/10] abnormal_probe_obs_noise (abnormal_probe) env=libero_sim/KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it seed=2000 max_episode_steps=50
|
| 35 |
+
|
| 36 |
+
[10/10] abnormal_probe_short_timeout (abnormal_probe) env=libero_sim/LIVING_ROOM_SCENE2_put_both_the_alphabet_soup_and_the_tomato_sauce_in_the_basket seed=2001 max_episode_steps=16
|
| 37 |
+
|
| 38 |
+
======== SMOKE-TEST SUMMARY ========
|
| 39 |
+
scenario_id | label | seed | rollout_started | actions_produced | video_saved | success | output_dir | error_if_any
|
| 40 |
+
-----------------------------------------+----------------+------+-----------------+------------------+-------------+---------+-------------------------------------------------------------------------------------+------------------------------
|
| 41 |
+
normal_kitchen3_moka_pot_on_stove | normal | 1000 | no | no | no | unknown | outputs/libero_smoke_tests/20260512_120217/normal_kitchen3_moka_pot_on_stove | dry-run (no rollout executed)
|
| 42 |
+
normal_kitchen4_bowl_in_drawer | normal | 1001 | no | no | no | unknown | outputs/libero_smoke_tests/20260512_120217/normal_kitchen4_bowl_in_drawer | dry-run (no rollout executed)
|
| 43 |
+
normal_living1_soup_and_cheese_in_basket | normal | 1002 | no | no | no | unknown | outputs/libero_smoke_tests/20260512_120217/normal_living1_soup_and_cheese_in_basket | dry-run (no rollout executed)
|
| 44 |
+
normal_living2_soup_and_tomato_in_basket | normal | 1003 | no | no | no | unknown | outputs/libero_smoke_tests/20260512_120217/normal_living2_soup_and_tomato_in_basket | dry-run (no rollout executed)
|
| 45 |
+
normal_study1_book_in_caddy | normal | 1004 | no | no | no | unknown | outputs/libero_smoke_tests/20260512_120217/normal_study1_book_in_caddy | dry-run (no rollout executed)
|
| 46 |
+
normal_kitchen8_both_moka_pots_on_stove | normal | 1005 | no | no | no | unknown | outputs/libero_smoke_tests/20260512_120217/normal_kitchen8_both_moka_pots_on_stove | dry-run (no rollout executed)
|
| 47 |
+
normal_kitchen6_mug_in_microwave | normal | 1006 | no | no | no | unknown | outputs/libero_smoke_tests/20260512_120217/normal_kitchen6_mug_in_microwave | dry-run (no rollout executed)
|
| 48 |
+
normal_living5_two_mugs_on_plates | normal | 1007 | no | no | no | unknown | outputs/libero_smoke_tests/20260512_120217/normal_living5_two_mugs_on_plates | dry-run (no rollout executed)
|
| 49 |
+
abnormal_probe_obs_noise | abnormal_probe | 2000 | no | no | no | unknown | outputs/libero_smoke_tests/20260512_120217/abnormal_probe_obs_noise | dry-run (no rollout executed)
|
| 50 |
+
abnormal_probe_short_timeout | abnormal_probe | 2001 | no | no | no | unknown | outputs/libero_smoke_tests/20260512_120217/abnormal_probe_short_timeout | dry-run (no rollout executed)
|
| 51 |
+
|
| 52 |
+
Wrote: outputs/libero_smoke_tests/20260512_120217/summary.json
|
| 53 |
+
Wrote: outputs/libero_smoke_tests/20260512_120217/summary.md
|
| 54 |
+
|
| 55 |
+
Review the videos with:
|
| 56 |
+
python /home/ubuntu/VLA_SAE/examples/LIBERO/smoke_tests/review_smoke_tests.py --run-dir outputs/libero_smoke_tests/20260512_120217
|
| 57 |
+
|
outputs/_setup_logs/_hf_download.log
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/home/ubuntu/.local/lib/python3.10/site-packages/huggingface_hub/cli/download.py:146: UserWarning: Ignoring `--include` since filenames have being explicitly set.
|
| 2 |
+
warnings.warn("Ignoring `--include` since filenames have being explicitly set.")
|
| 3 |
+
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
|
| 4 |
+
path=/home/ubuntu/VLA_SAE/checkpoints/GR00T-N1.7-LIBERO
|
outputs/_setup_logs/_libero_setup.log
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
+++ dirname gr00t/eval/sim/LIBERO/setup_libero.sh
|
| 2 |
+
++ cd gr00t/eval/sim/LIBERO
|
| 3 |
+
++ pwd
|
| 4 |
+
+ SCRIPT_DIR=/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO
|
| 5 |
+
+ LIBERO_REPO=/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/../../../../external_dependencies/LIBERO
|
| 6 |
+
+ PROJECT_REPO=/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/../../../..
|
| 7 |
+
+ LIBERO_UV_ENV=/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv
|
| 8 |
+
+ git submodule update --init /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/../../../../external_dependencies/LIBERO
|
| 9 |
+
+ rm -rf /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv
|
| 10 |
+
+ mkdir -p /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv
|
| 11 |
+
+ uv venv /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv --python 3.10
|
| 12 |
+
Using CPython 3.10.12 interpreter at: /usr/bin/python3.10
|
| 13 |
+
Creating virtual environment at: gr00t/eval/sim/LIBERO/libero_uv/.venv
|
| 14 |
+
Activate with: source gr00t/eval/sim/LIBERO/libero_uv/.venv/bin/activate
|
| 15 |
+
+ source /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/bin/activate
|
| 16 |
+
++ '[' -z '' ']'
|
| 17 |
+
++ '[' -n x ']'
|
| 18 |
+
++ SCRIPT_PATH=/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/bin/activate
|
| 19 |
+
++ '[' /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/bin/activate = gr00t/eval/sim/LIBERO/setup_libero.sh ']'
|
| 20 |
+
++ deactivate nondestructive
|
| 21 |
+
++ unset -f pydoc
|
| 22 |
+
++ '[' -z '' ']'
|
| 23 |
+
++ '[' -z '' ']'
|
| 24 |
+
++ hash -r
|
| 25 |
+
++ '[' -z '' ']'
|
| 26 |
+
++ unset VIRTUAL_ENV
|
| 27 |
+
++ unset VIRTUAL_ENV_PROMPT
|
| 28 |
+
++ '[' '!' nondestructive = nondestructive ']'
|
| 29 |
+
++ VIRTUAL_ENV=/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv
|
| 30 |
+
++ '[' linux-gnu = cygwin ']'
|
| 31 |
+
++ '[' linux-gnu = msys ']'
|
| 32 |
+
++ export VIRTUAL_ENV
|
| 33 |
+
++ '[' -z '' ']'
|
| 34 |
+
++ unset SCRIPT_PATH
|
| 35 |
+
++ _OLD_VIRTUAL_PATH=/home/ubuntu/.local/bin:/home/ubuntu/.vscode-server/cli/servers/Stable-034f571df509819cc10b0c8129f66ef77a542f0e/server/bin/remote-cli:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
| 36 |
+
++ PATH=/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/bin:/home/ubuntu/.local/bin:/home/ubuntu/.vscode-server/cli/servers/Stable-034f571df509819cc10b0c8129f66ef77a542f0e/server/bin/remote-cli:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
| 37 |
+
++ export PATH
|
| 38 |
+
++ '[' x '!=' x ']'
|
| 39 |
+
+++ basename /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv
|
| 40 |
+
++ VIRTUAL_ENV_PROMPT=.venv
|
| 41 |
+
++ export VIRTUAL_ENV_PROMPT
|
| 42 |
+
++ '[' -z '' ']'
|
| 43 |
+
++ '[' -z '' ']'
|
| 44 |
+
++ _OLD_VIRTUAL_PS1=
|
| 45 |
+
++ PS1='(.venv) '
|
| 46 |
+
++ export PS1
|
| 47 |
+
++ alias pydoc
|
| 48 |
+
++ true
|
| 49 |
+
++ hash -r
|
| 50 |
+
+ uv pip install --requirements /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/../../../../external_dependencies/LIBERO/requirements.txt
|
| 51 |
+
Using Python 3.10.12 environment at: gr00t/eval/sim/LIBERO/libero_uv/.venv
|
| 52 |
+
Resolved 120 packages in 34ms
|
| 53 |
+
Building gym==0.25.2
|
| 54 |
+
Building bddl==1.0.1
|
| 55 |
+
Building promise==2.3
|
| 56 |
+
Building future==0.18.2
|
| 57 |
+
Building egl-probe==1.0.2
|
| 58 |
+
Building robomimic==0.2.0
|
| 59 |
+
Building pathtools==0.1.2
|
| 60 |
+
Building easydict==1.9
|
| 61 |
+
Downloading wandb (1.7MiB)
|
| 62 |
+
Downloading pyopengl (3.0MiB)
|
| 63 |
+
Downloading transformers (4.4MiB)
|
| 64 |
+
Downloading nvidia-cuda-runtime (2.1MiB)
|
| 65 |
+
Downloading cuda-bindings (6.0MiB)
|
| 66 |
+
Downloading tokenizers (6.3MiB)
|
| 67 |
+
Downloading nvidia-cuda-nvrtc (86.0MiB)
|
| 68 |
+
Downloading nvidia-cuda-cupti (10.2MiB)
|
| 69 |
+
Downloading h5py (4.8MiB)
|
| 70 |
+
Downloading imageio-ffmpeg (28.1MiB)
|
| 71 |
+
Downloading triton (179.4MiB)
|
| 72 |
+
Downloading nvidia-cusparse (139.2MiB)
|
| 73 |
+
Downloading numba (3.6MiB)
|
| 74 |
+
Downloading torch (506.0MiB)
|
| 75 |
+
Downloading nvidia-cublas (403.5MiB)
|
| 76 |
+
Downloading numpy (16.0MiB)
|
| 77 |
+
Downloading torchvision (7.2MiB)
|
| 78 |
+
Downloading llvmlite (53.7MiB)
|
| 79 |
+
Downloading nvidia-cufft (204.2MiB)
|
| 80 |
+
Downloading scipy (36.8MiB)
|
| 81 |
+
Downloading setuptools (1.0MiB)
|
| 82 |
+
Downloading nvidia-cusolver (191.6MiB)
|
| 83 |
+
Downloading opencv-python (58.1MiB)
|
| 84 |
+
Downloading mujoco (6.9MiB)
|
| 85 |
+
Downloading tensorboard-data-server (6.3MiB)
|
| 86 |
+
Downloading nvidia-cudnn-cu13 (349.1MiB)
|
| 87 |
+
Downloading nvidia-curand (56.8MiB)
|
| 88 |
+
Downloading nvidia-cusparselt-cu13 (162.0MiB)
|
| 89 |
+
Downloading nvidia-nvjitlink (38.8MiB)
|
| 90 |
+
Downloading nvidia-nccl-cu13 (187.4MiB)
|
| 91 |
+
Downloading robosuite (184.5MiB)
|
| 92 |
+
Downloading matplotlib (11.3MiB)
|
| 93 |
+
Downloading grpcio (6.5MiB)
|
| 94 |
+
Downloading tensorboard (5.3MiB)
|
| 95 |
+
Downloading nvidia-nvshmem-cu13 (57.6MiB)
|
| 96 |
+
Built easydict==1.9
|
| 97 |
+
Built pathtools==0.1.2
|
| 98 |
+
Built promise==2.3
|
| 99 |
+
Built robomimic==0.2.0
|
| 100 |
+
Built bddl==1.0.1
|
| 101 |
+
Built gym==0.25.2
|
| 102 |
+
Built future==0.18.2
|
| 103 |
+
Downloaded nvidia-cuda-runtime
|
| 104 |
+
Downloaded setuptools
|
| 105 |
+
Downloaded wandb
|
| 106 |
+
Built egl-probe==1.0.2
|
| 107 |
+
Downloaded h5py
|
| 108 |
+
Downloaded numba
|
| 109 |
+
Downloaded transformers
|
| 110 |
+
Downloaded tensorboard
|
| 111 |
+
Downloaded cuda-bindings
|
| 112 |
+
Downloaded tensorboard-data-server
|
| 113 |
+
Downloaded tokenizers
|
| 114 |
+
Downloaded grpcio
|
| 115 |
+
Downloaded mujoco
|
| 116 |
+
Downloaded torchvision
|
| 117 |
+
Downloaded nvidia-cuda-cupti
|
| 118 |
+
Downloaded matplotlib
|
| 119 |
+
Downloaded numpy
|
| 120 |
+
Downloaded imageio-ffmpeg
|
| 121 |
+
Downloaded nvidia-nvjitlink
|
| 122 |
+
Downloaded scipy
|
| 123 |
+
Downloaded pyopengl
|
| 124 |
+
Downloaded llvmlite
|
| 125 |
+
Downloaded nvidia-curand
|
| 126 |
+
Downloaded nvidia-nvshmem-cu13
|
| 127 |
+
Downloaded opencv-python
|
| 128 |
+
Downloaded nvidia-cuda-nvrtc
|
| 129 |
+
Downloaded nvidia-cusparse
|
| 130 |
+
Downloaded nvidia-cusparselt-cu13
|
| 131 |
+
Downloaded triton
|
| 132 |
+
Downloaded nvidia-nccl-cu13
|
| 133 |
+
Downloaded nvidia-cusolver
|
| 134 |
+
Downloaded nvidia-cufft
|
| 135 |
+
Downloaded nvidia-cudnn-cu13
|
| 136 |
+
Downloaded nvidia-cublas
|
| 137 |
+
Downloaded robosuite
|
| 138 |
+
Downloaded torch
|
| 139 |
+
Prepared 43 packages in 17.97s
|
| 140 |
+
Installed 120 packages in 192ms
|
| 141 |
+
+ absl-py==2.4.0
|
| 142 |
+
+ antlr4-python3-runtime==4.9.3
|
| 143 |
+
+ attrs==26.1.0
|
| 144 |
+
+ bddl==1.0.1
|
| 145 |
+
+ certifi==2026.4.22
|
| 146 |
+
+ charset-normalizer==3.4.7
|
| 147 |
+
+ click==8.3.3
|
| 148 |
+
+ cloudpickle==2.1.0
|
| 149 |
+
+ cuda-bindings==13.2.0
|
| 150 |
+
+ cuda-pathfinder==1.5.4
|
| 151 |
+
+ cuda-toolkit==13.0.2
|
| 152 |
+
+ cycler==0.12.1
|
| 153 |
+
+ docker-pycreds==0.4.0
|
| 154 |
+
+ easydict==1.9
|
| 155 |
+
+ egl-probe==1.0.2
|
| 156 |
+
+ einops==0.4.1
|
| 157 |
+
+ etils==1.13.0
|
| 158 |
+
+ exceptiongroup==1.3.1
|
| 159 |
+
+ fastjsonschema==2.21.2
|
| 160 |
+
+ filelock==3.29.0
|
| 161 |
+
+ fonttools==4.62.1
|
| 162 |
+
+ fsspec==2026.4.0
|
| 163 |
+
+ future==0.18.2
|
| 164 |
+
+ gitdb==4.0.12
|
| 165 |
+
+ gitpython==3.1.50
|
| 166 |
+
+ glfw==2.10.0
|
| 167 |
+
+ grpcio==1.80.0
|
| 168 |
+
+ gym==0.25.2
|
| 169 |
+
+ gym-notices==0.1.0
|
| 170 |
+
+ h5py==3.16.0
|
| 171 |
+
+ hf-xet==1.5.0
|
| 172 |
+
+ huggingface-hub==0.36.2
|
| 173 |
+
+ hydra-core==1.2.0
|
| 174 |
+
+ idna==3.14
|
| 175 |
+
+ imageio==2.37.3
|
| 176 |
+
+ imageio-ffmpeg==0.6.0
|
| 177 |
+
+ importlib-resources==7.1.0
|
| 178 |
+
+ iniconfig==2.3.0
|
| 179 |
+
+ jinja2==3.1.6
|
| 180 |
+
+ jsonschema==4.26.0
|
| 181 |
+
+ jsonschema-specifications==2025.9.1
|
| 182 |
+
+ jupyter-core==5.9.1
|
| 183 |
+
+ jupytext==1.19.2
|
| 184 |
+
+ kiwisolver==1.5.0
|
| 185 |
+
+ llvmlite==0.47.0
|
| 186 |
+
+ markdown==3.10.2
|
| 187 |
+
+ markdown-it-py==4.2.0
|
| 188 |
+
+ markupsafe==3.0.3
|
| 189 |
+
+ matplotlib==3.5.3
|
| 190 |
+
+ mdit-py-plugins==0.6.0
|
| 191 |
+
+ mdurl==0.1.2
|
| 192 |
+
+ mpmath==1.3.0
|
| 193 |
+
+ mujoco==3.8.1
|
| 194 |
+
+ nbformat==5.10.4
|
| 195 |
+
+ networkx==3.4.2
|
| 196 |
+
+ numba==0.65.1
|
| 197 |
+
+ numpy==1.22.4
|
| 198 |
+
+ nvidia-cublas==13.1.0.3
|
| 199 |
+
+ nvidia-cuda-cupti==13.0.85
|
| 200 |
+
+ nvidia-cuda-nvrtc==13.0.88
|
| 201 |
+
+ nvidia-cuda-runtime==13.0.96
|
| 202 |
+
+ nvidia-cudnn-cu13==9.19.0.56
|
| 203 |
+
+ nvidia-cufft==12.0.0.61
|
| 204 |
+
+ nvidia-cufile==1.15.1.6
|
| 205 |
+
+ nvidia-curand==10.4.0.35
|
| 206 |
+
+ nvidia-cusolver==12.0.4.66
|
| 207 |
+
+ nvidia-cusparse==12.6.3.3
|
| 208 |
+
+ nvidia-cusparselt-cu13==0.8.0
|
| 209 |
+
+ nvidia-nccl-cu13==2.28.9
|
| 210 |
+
+ nvidia-nvjitlink==13.0.88
|
| 211 |
+
+ nvidia-nvshmem-cu13==3.4.5
|
| 212 |
+
+ nvidia-nvtx==13.0.85
|
| 213 |
+
+ omegaconf==2.3.0
|
| 214 |
+
+ opencv-python==4.6.0.66
|
| 215 |
+
+ packaging==26.2
|
| 216 |
+
+ pathtools==0.1.2
|
| 217 |
+
+ pillow==12.2.0
|
| 218 |
+
+ platformdirs==4.9.6
|
| 219 |
+
+ pluggy==1.6.0
|
| 220 |
+
+ promise==2.3
|
| 221 |
+
+ protobuf==3.20.3
|
| 222 |
+
+ psutil==7.2.2
|
| 223 |
+
+ pygments==2.20.0
|
| 224 |
+
+ pyopengl==3.1.10
|
| 225 |
+
+ pyparsing==3.3.2
|
| 226 |
+
+ pytest==9.0.3
|
| 227 |
+
+ python-dateutil==2.9.0.post0
|
| 228 |
+
+ pyyaml==6.0.3
|
| 229 |
+
+ referencing==0.37.0
|
| 230 |
+
+ regex==2026.5.9
|
| 231 |
+
+ requests==2.34.0
|
| 232 |
+
+ robomimic==0.2.0
|
| 233 |
+
+ robosuite==1.4.0
|
| 234 |
+
+ rpds-py==0.30.0
|
| 235 |
+
+ scipy==1.13.1
|
| 236 |
+
+ sentry-sdk==2.59.0
|
| 237 |
+
+ setproctitle==1.3.7
|
| 238 |
+
+ setuptools==81.0.0
|
| 239 |
+
+ shortuuid==1.0.13
|
| 240 |
+
+ six==1.17.0
|
| 241 |
+
+ smmap==5.0.3
|
| 242 |
+
+ sympy==1.14.0
|
| 243 |
+
+ tensorboard==2.20.0
|
| 244 |
+
+ tensorboard-data-server==0.7.2
|
| 245 |
+
+ tensorboardx==2.6.5
|
| 246 |
+
+ termcolor==3.3.0
|
| 247 |
+
+ thop==0.1.1.post2209072238
|
| 248 |
+
+ tokenizers==0.12.1
|
| 249 |
+
+ tomli==2.4.1
|
| 250 |
+
+ torch==2.11.0
|
| 251 |
+
+ torchvision==0.26.0
|
| 252 |
+
+ tqdm==4.67.3
|
| 253 |
+
+ traitlets==5.15.0
|
| 254 |
+
+ transformers==4.21.1
|
| 255 |
+
+ triton==3.6.0
|
| 256 |
+
+ typing-extensions==4.15.0
|
| 257 |
+
+ urllib3==2.7.0
|
| 258 |
+
+ wandb==0.13.1
|
| 259 |
+
+ werkzeug==3.1.8
|
| 260 |
+
+ zipp==3.23.1
|
| 261 |
+
+ uv pip install -e /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/../../../../external_dependencies/LIBERO --config-settings editable_mode=compat
|
| 262 |
+
Using Python 3.10.12 environment at: gr00t/eval/sim/LIBERO/libero_uv/.venv
|
| 263 |
+
Resolved 1 package in 440ms
|
| 264 |
+
Building libero @ file:///home/ubuntu/VLA_SAE/external_dependencies/LIBERO
|
| 265 |
+
Built libero @ file:///home/ubuntu/VLA_SAE/external_dependencies/LIBERO
|
| 266 |
+
Prepared 1 package in 211ms
|
| 267 |
+
Installed 1 package in 1ms
|
| 268 |
+
+ libero==0.1.0 (from file:///home/ubuntu/VLA_SAE/external_dependencies/LIBERO)
|
| 269 |
+
+ uv pip install --editable /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/../../../.. --no-deps
|
| 270 |
+
Using Python 3.10.12 environment at: gr00t/eval/sim/LIBERO/libero_uv/.venv
|
| 271 |
+
Resolved 1 package in 1ms
|
| 272 |
+
Building gr00t @ file:///home/ubuntu/VLA_SAE
|
| 273 |
+
Built gr00t @ file:///home/ubuntu/VLA_SAE
|
| 274 |
+
Prepared 1 package in 569ms
|
| 275 |
+
Installed 1 package in 0.62ms
|
| 276 |
+
+ gr00t==0.1.0 (from file:///home/ubuntu/VLA_SAE)
|
| 277 |
+
+ uv pip install torch==2.5.1 torchvision==0.20.1 pydantic av tianshou==0.5.1 tyro pandas dm_tree einops==0.8.1 albumentations==1.4.18 zmq
|
| 278 |
+
Using Python 3.10.12 environment at: gr00t/eval/sim/LIBERO/libero_uv/.venv
|
| 279 |
+
Resolved 72 packages in 543ms
|
| 280 |
+
Building zmq==0.0.0
|
| 281 |
+
Downloading pandas (12.2MiB)
|
| 282 |
+
Downloading nvidia-cuda-cupti-cu12 (13.2MiB)
|
| 283 |
+
Downloading opencv-python-headless (57.6MiB)
|
| 284 |
+
Downloading sympy (5.9MiB)
|
| 285 |
+
Downloading av (35.9MiB)
|
| 286 |
+
Downloading numpy (16.0MiB)
|
| 287 |
+
Downloading torchvision (6.9MiB)
|
| 288 |
+
Downloading nvidia-cufft-cu12 (201.7MiB)
|
| 289 |
+
Downloading nvidia-cublas-cu12 (346.6MiB)
|
| 290 |
+
Downloading nvidia-cusparse-cu12 (197.8MiB)
|
| 291 |
+
Downloading nvidia-cuda-nvrtc-cu12 (23.5MiB)
|
| 292 |
+
Downloading nvidia-cusolver-cu12 (122.0MiB)
|
| 293 |
+
Downloading nvidia-nvjitlink-cu12 (20.1MiB)
|
| 294 |
+
Downloading nvidia-nccl-cu12 (179.9MiB)
|
| 295 |
+
Downloading nvidia-curand-cu12 (53.7MiB)
|
| 296 |
+
Downloading nvidia-cudnn-cu12 (634.0MiB)
|
| 297 |
+
Downloading torch (864.5MiB)
|
| 298 |
+
Downloading triton (199.8MiB)
|
| 299 |
+
Built zmq==0.0.0
|
| 300 |
+
Downloaded torchvision
|
| 301 |
+
Downloaded nvidia-cuda-cupti-cu12
|
| 302 |
+
Downloaded numpy
|
| 303 |
+
Downloaded nvidia-nvjitlink-cu12
|
| 304 |
+
Downloaded nvidia-cuda-nvrtc-cu12
|
| 305 |
+
Downloaded sympy
|
| 306 |
+
Downloaded av
|
| 307 |
+
Downloaded nvidia-curand-cu12
|
| 308 |
+
Downloaded opencv-python-headless
|
| 309 |
+
Downloaded nvidia-cusolver-cu12
|
| 310 |
+
Downloaded pandas
|
| 311 |
+
Downloaded nvidia-nccl-cu12
|
| 312 |
+
Downloaded triton
|
| 313 |
+
Downloaded nvidia-cufft-cu12
|
| 314 |
+
Downloaded nvidia-cusparse-cu12
|
| 315 |
+
Downloaded nvidia-cublas-cu12
|
| 316 |
+
Downloaded nvidia-cudnn-cu12
|
| 317 |
+
Downloaded torch
|
| 318 |
+
Prepared 26 packages in 25.57s
|
| 319 |
+
Uninstalled 6 packages in 141ms
|
| 320 |
+
Installed 44 packages in 266ms
|
| 321 |
+
+ albucore==0.0.17
|
| 322 |
+
+ albumentations==1.4.18
|
| 323 |
+
+ annotated-types==0.7.0
|
| 324 |
+
+ av==17.0.1
|
| 325 |
+
+ dm-tree==0.1.10
|
| 326 |
+
+ docstring-parser==0.18.0
|
| 327 |
+
- einops==0.4.1
|
| 328 |
+
+ einops==0.8.1
|
| 329 |
+
+ eval-type-backport==0.3.1
|
| 330 |
+
+ farama-notifications==0.0.6
|
| 331 |
+
+ gymnasium==1.3.0
|
| 332 |
+
+ lazy-loader==0.5
|
| 333 |
+
- numpy==1.22.4
|
| 334 |
+
+ numpy==2.2.6
|
| 335 |
+
+ nvidia-cublas-cu12==12.4.5.8
|
| 336 |
+
+ nvidia-cuda-cupti-cu12==12.4.127
|
| 337 |
+
+ nvidia-cuda-nvrtc-cu12==12.4.127
|
| 338 |
+
+ nvidia-cuda-runtime-cu12==12.4.127
|
| 339 |
+
+ nvidia-cudnn-cu12==9.1.0.70
|
| 340 |
+
+ nvidia-cufft-cu12==11.2.1.3
|
| 341 |
+
+ nvidia-curand-cu12==10.3.5.147
|
| 342 |
+
+ nvidia-cusolver-cu12==11.6.1.9
|
| 343 |
+
+ nvidia-cusparse-cu12==12.3.1.170
|
| 344 |
+
+ nvidia-nccl-cu12==2.21.5
|
| 345 |
+
+ nvidia-nvjitlink-cu12==12.4.127
|
| 346 |
+
+ nvidia-nvtx-cu12==12.4.127
|
| 347 |
+
+ opencv-python-headless==4.13.0.92
|
| 348 |
+
+ pandas==2.3.3
|
| 349 |
+
+ pettingzoo==1.26.1
|
| 350 |
+
+ pydantic==2.13.4
|
| 351 |
+
+ pydantic-core==2.46.4
|
| 352 |
+
+ pytz==2026.2
|
| 353 |
+
+ pyzmq==27.1.0
|
| 354 |
+
+ scikit-image==0.25.2
|
| 355 |
+
- sympy==1.14.0
|
| 356 |
+
+ sympy==1.13.1
|
| 357 |
+
+ tianshou==0.5.1
|
| 358 |
+
+ tifffile==2025.5.10
|
| 359 |
+
- torch==2.11.0
|
| 360 |
+
+ torch==2.5.1
|
| 361 |
+
- torchvision==0.26.0
|
| 362 |
+
+ torchvision==0.20.1
|
| 363 |
+
- triton==3.6.0
|
| 364 |
+
+ triton==3.1.0
|
| 365 |
+
+ typeguard==4.5.1
|
| 366 |
+
+ typing-inspection==0.4.2
|
| 367 |
+
+ tyro==1.0.13
|
| 368 |
+
+ tzdata==2026.2
|
| 369 |
+
+ wrapt==2.1.2
|
| 370 |
+
+ zmq==0.0.0
|
| 371 |
+
+ uv pip install transformers==4.57.3 msgpack==1.1.0 msgpack-numpy==0.4.8 gymnasium==0.29.1
|
| 372 |
+
Using Python 3.10.12 environment at: gr00t/eval/sim/LIBERO/libero_uv/.venv
|
| 373 |
+
Resolved 23 packages in 109ms
|
| 374 |
+
Prepared 1 package in 95ms
|
| 375 |
+
Uninstalled 3 packages in 19ms
|
| 376 |
+
Installed 6 packages in 40ms
|
| 377 |
+
- gymnasium==1.3.0
|
| 378 |
+
+ gymnasium==0.29.1
|
| 379 |
+
+ msgpack==1.1.0
|
| 380 |
+
+ msgpack-numpy==0.4.8
|
| 381 |
+
+ safetensors==0.7.0
|
| 382 |
+
- tokenizers==0.12.1
|
| 383 |
+
+ tokenizers==0.22.2
|
| 384 |
+
- transformers==4.21.1
|
| 385 |
+
+ transformers==4.57.3
|
| 386 |
+
+ uv pip install numpy==1.26.4
|
| 387 |
+
Using Python 3.10.12 environment at: gr00t/eval/sim/LIBERO/libero_uv/.venv
|
| 388 |
+
Resolved 1 package in 6ms
|
| 389 |
+
Uninstalled 1 package in 11ms
|
| 390 |
+
Installed 1 package in 16ms
|
| 391 |
+
- numpy==2.2.6
|
| 392 |
+
+ numpy==1.26.4
|
| 393 |
+
+ uv pip install --editable /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/../../../.. --no-deps
|
| 394 |
+
Using Python 3.10.12 environment at: gr00t/eval/sim/LIBERO/libero_uv/.venv
|
| 395 |
+
Resolved 1 package in 1ms
|
| 396 |
+
Building gr00t @ file:///home/ubuntu/VLA_SAE
|
| 397 |
+
Built gr00t @ file:///home/ubuntu/VLA_SAE
|
| 398 |
+
Prepared 1 package in 682ms
|
| 399 |
+
Uninstalled 1 package in 0.33ms
|
| 400 |
+
Installed 1 package in 0.55ms
|
| 401 |
+
~ gr00t==0.1.0 (from file:///home/ubuntu/VLA_SAE)
|
| 402 |
+
+ rm -rf /home/ubuntu/.libero
|
| 403 |
+
+ printf 'n\n'
|
| 404 |
+
+ python -c 'from gr00t.eval.sim.LIBERO.libero_env import register_libero_envs'
|
| 405 |
+
Do you want to specify a custom path for the dataset folder? (Y/N): [robosuite WARNING] No private macro file found! (__init__.py:7)
|
| 406 |
+
[robosuite WARNING] It is recommended to use a private macro file (__init__.py:8)
|
| 407 |
+
[robosuite WARNING] To setup, run: python /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/robosuite/scripts/setup_macros.py (__init__.py:9)
|
| 408 |
+
Matplotlib created a temporary config/cache directory at /tmp/matplotlib-igojt7pq because the default path (/home/ubuntu/.config/matplotlib) is not a writable directory; it is highly recommended to set the MPLCONFIGDIR environment variable to a writable directory, in particular to speed up the import of Matplotlib and to better support multiprocessing.
|
| 409 |
+
Gym has been unmaintained since 2022 and does not support NumPy 2.0 amongst other critical functionality.
|
| 410 |
+
Please upgrade to Gymnasium, the maintained drop-in replacement of Gym, or contact the authors of your software and request that they upgrade.
|
| 411 |
+
See the migration guide at https://gymnasium.farama.org/introduction/migration_guide/ for additional information.
|
| 412 |
+
Initializing the default config file...
|
| 413 |
+
The following information is stored in the config file: /home/ubuntu/.libero/config.yaml
|
| 414 |
+
benchmark_root: /home/ubuntu/VLA_SAE/external_dependencies/LIBERO/libero/libero
|
| 415 |
+
bddl_files: /home/ubuntu/VLA_SAE/external_dependencies/LIBERO/libero/libero/./bddl_files
|
| 416 |
+
init_states: /home/ubuntu/VLA_SAE/external_dependencies/LIBERO/libero/libero/./init_files
|
| 417 |
+
datasets: /home/ubuntu/VLA_SAE/external_dependencies/LIBERO/libero/libero/../datasets
|
| 418 |
+
assets: /home/ubuntu/VLA_SAE/external_dependencies/LIBERO/libero/libero/./assets
|
| 419 |
+
+ python -
|
| 420 |
+
[robosuite WARNING] No private macro file found! (__init__.py:7)
|
| 421 |
+
[robosuite WARNING] It is recommended to use a private macro file (__init__.py:8)
|
| 422 |
+
[robosuite WARNING] To setup, run: python /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/robosuite/scripts/setup_macros.py (__init__.py:9)
|
| 423 |
+
Matplotlib created a temporary config/cache directory at /tmp/matplotlib-fwatww__ because the default path (/home/ubuntu/.config/matplotlib) is not a writable directory; it is highly recommended to set the MPLCONFIGDIR environment variable to a writable directory, in particular to speed up the import of Matplotlib and to better support multiprocessing.
|
| 424 |
+
Gym has been unmaintained since 2022 and does not support NumPy 2.0 amongst other critical functionality.
|
| 425 |
+
Please upgrade to Gymnasium, the maintained drop-in replacement of Gym, or contact the authors of your software and request that they upgrade.
|
| 426 |
+
See the migration guide at https://gymnasium.farama.org/introduction/migration_guide/ for additional information.
|
| 427 |
+
/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/gymnasium/utils/passive_env_checker.py:159: UserWarning: [33mWARN: The obs returned by the `reset()` method is not within the observation space.[0m
|
| 428 |
+
logger.warn(f"{pre} is not within the observation space.")
|
| 429 |
+
/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/gymnasium/utils/passive_env_checker.py:135: UserWarning: [33mWARN: The obs returned by the `reset()` method was expecting numpy array dtype to be float32, actual type: float64[0m
|
| 430 |
+
logger.warn(
|
| 431 |
+
/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/gymnasium/utils/passive_env_checker.py:131: UserWarning: [33mWARN: The obs returned by the `reset()` method was expecting a numpy array, actual type: <class 'list'>[0m
|
| 432 |
+
logger.warn(
|
| 433 |
+
/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/gymnasium/spaces/box.py:240: UserWarning: [33mWARN: Casting input x to numpy array.[0m
|
| 434 |
+
gym.logger.warn("Casting input x to numpy array.")
|
| 435 |
+
[info] using task orders [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
| 436 |
+
[info] using task orders [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
| 437 |
+
[info] using task orders [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
| 438 |
+
[info] using task orders [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
| 439 |
+
Env OK: <class 'gymnasium.wrappers.order_enforcing.OrderEnforcing'>
|
outputs/_setup_logs/_server.log
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/albumentations/__init__.py:13: UserWarning: A new version of Albumentations is available: 2.0.8 (you have 1.4.18). Upgrade using: pip install -U albumentations. To disable automatic update checks, set the environment variable NO_ALBUMENTATIONS_UPDATE to 1.
|
| 2 |
+
check_for_updates()
|
| 3 |
+
flash_attn is not installed. Falling back to sdpa attention. Install flash-attn for better performance: pip install flash-attn
|
| 4 |
+
Starting GR00T inference server...
|
| 5 |
+
Embodiment tag: EmbodimentTag.LIBERO_PANDA
|
| 6 |
+
Model path: checkpoints/GR00T-N1.7-LIBERO/libero_10
|
| 7 |
+
Device: cuda
|
| 8 |
+
Host: 127.0.0.1
|
| 9 |
+
Port: 5555
|
| 10 |
+
Traceback (most recent call last):
|
| 11 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/huggingface_hub/utils/_http.py", line 403, in hf_raise_for_status
|
| 12 |
+
response.raise_for_status()
|
| 13 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/requests/models.py", line 1167, in raise_for_status
|
| 14 |
+
raise HTTPError(http_error_msg, response=self)
|
| 15 |
+
requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: https://huggingface.co/nvidia/Cosmos-Reason2-2B/resolve/main/config.json
|
| 16 |
+
|
| 17 |
+
The above exception was the direct cause of the following exception:
|
| 18 |
+
|
| 19 |
+
Traceback (most recent call last):
|
| 20 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/transformers/utils/hub.py", line 479, in cached_files
|
| 21 |
+
hf_hub_download(
|
| 22 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/huggingface_hub/utils/_validators.py", line 114, in _inner_fn
|
| 23 |
+
return fn(*args, **kwargs)
|
| 24 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/huggingface_hub/file_download.py", line 1014, in hf_hub_download
|
| 25 |
+
return _hf_hub_download_to_cache_dir(
|
| 26 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/huggingface_hub/file_download.py", line 1121, in _hf_hub_download_to_cache_dir
|
| 27 |
+
_raise_on_head_call_error(head_call_error, force_download, local_files_only)
|
| 28 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/huggingface_hub/file_download.py", line 1662, in _raise_on_head_call_error
|
| 29 |
+
raise head_call_error
|
| 30 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/huggingface_hub/file_download.py", line 1550, in _get_metadata_or_catch_error
|
| 31 |
+
metadata = get_hf_file_metadata(
|
| 32 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/huggingface_hub/utils/_validators.py", line 114, in _inner_fn
|
| 33 |
+
return fn(*args, **kwargs)
|
| 34 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/huggingface_hub/file_download.py", line 1467, in get_hf_file_metadata
|
| 35 |
+
r = _request_wrapper(
|
| 36 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/huggingface_hub/file_download.py", line 283, in _request_wrapper
|
| 37 |
+
response = _request_wrapper(
|
| 38 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/huggingface_hub/file_download.py", line 307, in _request_wrapper
|
| 39 |
+
hf_raise_for_status(response)
|
| 40 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/huggingface_hub/utils/_http.py", line 420, in hf_raise_for_status
|
| 41 |
+
raise _format(GatedRepoError, message, response) from e
|
| 42 |
+
huggingface_hub.errors.GatedRepoError: 401 Client Error. (Request ID: Root=1-6a0314fd-4cda431e47f56d0801840f67;ad3e8e30-c9ce-490a-ba30-5986386d4d7c)
|
| 43 |
+
|
| 44 |
+
Cannot access gated repo for url https://huggingface.co/nvidia/Cosmos-Reason2-2B/resolve/main/config.json.
|
| 45 |
+
Access to model nvidia/Cosmos-Reason2-2B is restricted. You must have access to it and be authenticated to access it. Please log in.
|
| 46 |
+
|
| 47 |
+
The above exception was the direct cause of the following exception:
|
| 48 |
+
|
| 49 |
+
Traceback (most recent call last):
|
| 50 |
+
File "/home/ubuntu/VLA_SAE/gr00t/eval/run_gr00t_server.py", line 167, in <module>
|
| 51 |
+
main(config)
|
| 52 |
+
File "/home/ubuntu/VLA_SAE/gr00t/eval/run_gr00t_server.py", line 86, in main
|
| 53 |
+
policy = Gr00tPolicy(
|
| 54 |
+
File "/home/ubuntu/VLA_SAE/gr00t/policy/gr00t_policy.py", line 100, in __init__
|
| 55 |
+
model = AutoModel.from_pretrained(model_dir)
|
| 56 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/transformers/models/auto/auto_factory.py", line 604, in from_pretrained
|
| 57 |
+
return model_class.from_pretrained(
|
| 58 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/transformers/modeling_utils.py", line 277, in _wrapper
|
| 59 |
+
return func(*args, **kwargs)
|
| 60 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/transformers/modeling_utils.py", line 4971, in from_pretrained
|
| 61 |
+
model = cls(config, *model_args, **model_kwargs)
|
| 62 |
+
File "/home/ubuntu/VLA_SAE/gr00t/model/gr00t_n1d7/gr00t_n1d7.py", line 516, in __init__
|
| 63 |
+
self.backbone = backbone_cls(
|
| 64 |
+
File "/home/ubuntu/VLA_SAE/gr00t/model/modules/qwen3_backbone.py", line 80, in __init__
|
| 65 |
+
self.model = Qwen3VLForConditionalGeneration.from_pretrained(
|
| 66 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/transformers/modeling_utils.py", line 277, in _wrapper
|
| 67 |
+
return func(*args, **kwargs)
|
| 68 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/transformers/modeling_utils.py", line 4843, in from_pretrained
|
| 69 |
+
config, model_kwargs = cls.config_class.from_pretrained(
|
| 70 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/transformers/configuration_utils.py", line 622, in from_pretrained
|
| 71 |
+
config_dict, kwargs = cls.get_config_dict(pretrained_model_name_or_path, **kwargs)
|
| 72 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/transformers/configuration_utils.py", line 662, in get_config_dict
|
| 73 |
+
config_dict, kwargs = cls._get_config_dict(pretrained_model_name_or_path, **kwargs)
|
| 74 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/transformers/configuration_utils.py", line 721, in _get_config_dict
|
| 75 |
+
resolved_config_file = cached_file(
|
| 76 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/transformers/utils/hub.py", line 322, in cached_file
|
| 77 |
+
file = cached_files(path_or_repo_id=path_or_repo_id, filenames=[filename], **kwargs)
|
| 78 |
+
File "/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/transformers/utils/hub.py", line 543, in cached_files
|
| 79 |
+
raise OSError(
|
| 80 |
+
OSError: You are trying to access a gated repo.
|
| 81 |
+
Make sure to have access to it at https://huggingface.co/nvidia/Cosmos-Reason2-2B.
|
| 82 |
+
401 Client Error. (Request ID: Root=1-6a0314fd-4cda431e47f56d0801840f67;ad3e8e30-c9ce-490a-ba30-5986386d4d7c)
|
| 83 |
+
|
| 84 |
+
Cannot access gated repo for url https://huggingface.co/nvidia/Cosmos-Reason2-2B/resolve/main/config.json.
|
| 85 |
+
Access to model nvidia/Cosmos-Reason2-2B is restricted. You must have access to it and be authenticated to access it. Please log in.
|
outputs/_setup_logs/_uv_sync.log
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Using CPython 3.10.12 interpreter at: /usr/bin/python3
|
| 2 |
+
Creating virtual environment at: .venv
|
| 3 |
+
error: Failed to generate package metadata for `flash-attn==2.7.4.post1 @ path+scripts/deployment/dgpu/wheels/flash_attn-2.7.4.post1-cp310-cp310-linux_aarch64.whl`
|
| 4 |
+
Caused by: Failed to extract archive: flash_attn-2.7.4.post1-cp310-cp310-linux_aarch64.whl
|
| 5 |
+
Caused by: Invalid zip file structure
|
| 6 |
+
Caused by: Encountered an unexpected header (actual: 0x73726576, expected: 0x4034b50).
|
outputs/_setup_logs/_venv_install.log
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
+ uv pip install --python .venv/bin/python -e . --no-deps
|
| 2 |
+
Resolved 1 package in 1ms
|
| 3 |
+
Building gr00t @ file:///home/ubuntu/VLA_SAE
|
| 4 |
+
Built gr00t @ file:///home/ubuntu/VLA_SAE
|
| 5 |
+
Prepared 1 package in 848ms
|
| 6 |
+
Installed 1 package in 0.80ms
|
| 7 |
+
+ gr00t==0.1.0 (from file:///home/ubuntu/VLA_SAE)
|
| 8 |
+
+ uv pip install --python .venv/bin/python torch==2.7.1 torchvision==0.22.1 transformers==4.57.3 numpy==1.26.4 albumentations==1.4.18 av==16.1.0 diffusers==0.35.1 dm-tree lmdb==1.7.5 msgpack==1.1.0 msgpack-numpy==0.4.8 pandas==2.2.3 peft==0.17.1 termcolor==3.2.0 tyro==0.9.17 click==8.1.8 datasets==3.6.0 cryptography einops==0.8.1 gitpython==3.1.46 jsonlines==4.0.0 gymnasium==1.2.2 matplotlib==3.10.1 omegaconf==2.3.0 scipy==1.15.3 torchcodec==0.4.0 wandb==0.23.0 pyzmq==27.0.1 'huggingface-hub[cli]' 'opencv-python-headless>=4.5,<4.13' safetensors accelerate sentencepiece protobuf pyyaml tqdm
|
| 9 |
+
Resolved 127 packages in 1.16s
|
| 10 |
+
Downloading aiohttp (1.6MiB)
|
| 11 |
+
Downloading nvidia-cufile-cu12 (1.1MiB)
|
| 12 |
+
Downloading sympy (6.0MiB)
|
| 13 |
+
Downloading hf-xet (4.3MiB)
|
| 14 |
+
Downloading nvidia-curand-cu12 (53.7MiB)
|
| 15 |
+
Downloading scipy (35.9MiB)
|
| 16 |
+
Downloading pyarrow (46.6MiB)
|
| 17 |
+
Downloading scikit-image (14.1MiB)
|
| 18 |
+
Downloading cryptography (4.5MiB)
|
| 19 |
+
Downloading nvidia-nvjitlink-cu12 (18.8MiB)
|
| 20 |
+
Downloading numpy (17.4MiB)
|
| 21 |
+
Downloading diffusers (3.9MiB)
|
| 22 |
+
Downloading tokenizers (3.1MiB)
|
| 23 |
+
Downloading kiwisolver (1.6MiB)
|
| 24 |
+
Downloading opencv-python-headless (47.7MiB)
|
| 25 |
+
Downloading transformers (11.4MiB)
|
| 26 |
+
Downloading nvidia-cusolver-cu12 (150.9MiB)
|
| 27 |
+
Downloading nvidia-cusparse-cu12 (206.5MiB)
|
| 28 |
+
Downloading pandas (12.5MiB)
|
| 29 |
+
Downloading sentencepiece (1.3MiB)
|
| 30 |
+
Downloading nvidia-cuda-cupti-cu12 (8.5MiB)
|
| 31 |
+
Downloading pillow (6.7MiB)
|
| 32 |
+
Downloading nvidia-cufft-cu12 (190.9MiB)
|
| 33 |
+
Downloading networkx (1.6MiB)
|
| 34 |
+
Downloading nvidia-cuda-nvrtc-cu12 (22.6MiB)
|
| 35 |
+
Downloading pydantic-core (2.0MiB)
|
| 36 |
+
Downloading fonttools (4.7MiB)
|
| 37 |
+
Downloading av (38.4MiB)
|
| 38 |
+
Downloading nvidia-cudnn-cu12 (544.5MiB)
|
| 39 |
+
Downloading nvidia-nccl-cu12 (192.0MiB)
|
| 40 |
+
Downloading nvidia-cublas-cu12 (374.9MiB)
|
| 41 |
+
Downloading nvidia-cusparselt-cu12 (149.5MiB)
|
| 42 |
+
Downloading pygments (1.2MiB)
|
| 43 |
+
Downloading torchcodec (1.3MiB)
|
| 44 |
+
Downloading wandb (19.3MiB)
|
| 45 |
+
Downloading matplotlib (8.2MiB)
|
| 46 |
+
Downloading torchvision (7.1MiB)
|
| 47 |
+
Downloading triton (148.4MiB)
|
| 48 |
+
Downloading torch (783.1MiB)
|
| 49 |
+
Downloaded nvidia-cufile-cu12
|
| 50 |
+
Downloaded sentencepiece
|
| 51 |
+
Downloaded kiwisolver
|
| 52 |
+
Downloaded aiohttp
|
| 53 |
+
Downloaded torchcodec
|
| 54 |
+
Downloaded pygments
|
| 55 |
+
Downloaded pydantic-core
|
| 56 |
+
Building antlr4-python3-runtime==4.9.3
|
| 57 |
+
Built antlr4-python3-runtime==4.9.3
|
| 58 |
+
Downloaded networkx
|
| 59 |
+
Downloaded tokenizers
|
| 60 |
+
Downloaded diffusers
|
| 61 |
+
Downloaded hf-xet
|
| 62 |
+
Downloaded fonttools
|
| 63 |
+
Downloaded cryptography
|
| 64 |
+
Downloaded pillow
|
| 65 |
+
Downloaded torchvision
|
| 66 |
+
Downloaded nvidia-cuda-cupti-cu12
|
| 67 |
+
Downloaded matplotlib
|
| 68 |
+
Downloaded sympy
|
| 69 |
+
Downloaded pandas
|
| 70 |
+
Downloaded scikit-image
|
| 71 |
+
Downloaded numpy
|
| 72 |
+
Downloaded nvidia-nvjitlink-cu12
|
| 73 |
+
Downloaded transformers
|
| 74 |
+
Downloaded wandb
|
| 75 |
+
Downloaded nvidia-cuda-nvrtc-cu12
|
| 76 |
+
Downloaded scipy
|
| 77 |
+
Downloaded av
|
| 78 |
+
Downloaded opencv-python-headless
|
| 79 |
+
Downloaded nvidia-curand-cu12
|
| 80 |
+
Downloaded pyarrow
|
| 81 |
+
Downloaded nvidia-cusolver-cu12
|
| 82 |
+
Downloaded nvidia-cusparselt-cu12
|
| 83 |
+
Downloaded nvidia-cufft-cu12
|
| 84 |
+
Downloaded nvidia-cusparse-cu12
|
| 85 |
+
Downloaded nvidia-nccl-cu12
|
| 86 |
+
Downloaded triton
|
| 87 |
+
Downloaded nvidia-cublas-cu12
|
| 88 |
+
Downloaded nvidia-cudnn-cu12
|
| 89 |
+
Downloaded torch
|
| 90 |
+
Prepared 125 packages in 43.34s
|
| 91 |
+
Installed 127 packages in 197ms
|
| 92 |
+
+ absl-py==2.4.0
|
| 93 |
+
+ accelerate==1.13.0
|
| 94 |
+
+ aiohappyeyeballs==2.6.1
|
| 95 |
+
+ aiohttp==3.13.5
|
| 96 |
+
+ aiosignal==1.4.0
|
| 97 |
+
+ albucore==0.0.17
|
| 98 |
+
+ albumentations==1.4.18
|
| 99 |
+
+ annotated-types==0.7.0
|
| 100 |
+
+ antlr4-python3-runtime==4.9.3
|
| 101 |
+
+ async-timeout==5.0.1
|
| 102 |
+
+ attrs==26.1.0
|
| 103 |
+
+ av==16.1.0
|
| 104 |
+
+ certifi==2026.4.22
|
| 105 |
+
+ cffi==2.0.0
|
| 106 |
+
+ charset-normalizer==3.4.7
|
| 107 |
+
+ click==8.1.8
|
| 108 |
+
+ cloudpickle==3.1.2
|
| 109 |
+
+ contourpy==1.3.2
|
| 110 |
+
+ cryptography==48.0.0
|
| 111 |
+
+ cycler==0.12.1
|
| 112 |
+
+ datasets==3.6.0
|
| 113 |
+
+ diffusers==0.35.1
|
| 114 |
+
+ dill==0.3.8
|
| 115 |
+
+ dm-tree==0.1.10
|
| 116 |
+
+ docstring-parser==0.18.0
|
| 117 |
+
+ einops==0.8.1
|
| 118 |
+
+ eval-type-backport==0.3.1
|
| 119 |
+
+ farama-notifications==0.0.6
|
| 120 |
+
+ filelock==3.29.0
|
| 121 |
+
+ fonttools==4.62.1
|
| 122 |
+
+ frozenlist==1.8.0
|
| 123 |
+
+ fsspec==2025.3.0
|
| 124 |
+
+ gitdb==4.0.12
|
| 125 |
+
+ gitpython==3.1.46
|
| 126 |
+
+ gymnasium==1.2.2
|
| 127 |
+
+ hf-xet==1.5.0
|
| 128 |
+
+ huggingface-hub==0.36.2
|
| 129 |
+
+ idna==3.14
|
| 130 |
+
+ imageio==2.37.3
|
| 131 |
+
+ importlib-metadata==9.0.0
|
| 132 |
+
+ inquirerpy==0.3.4
|
| 133 |
+
+ jinja2==3.1.6
|
| 134 |
+
+ jsonlines==4.0.0
|
| 135 |
+
+ kiwisolver==1.5.0
|
| 136 |
+
+ lazy-loader==0.5
|
| 137 |
+
+ lmdb==1.7.5
|
| 138 |
+
+ markdown-it-py==4.2.0
|
| 139 |
+
+ markupsafe==3.0.3
|
| 140 |
+
+ matplotlib==3.10.1
|
| 141 |
+
+ mdurl==0.1.2
|
| 142 |
+
+ mpmath==1.3.0
|
| 143 |
+
+ msgpack==1.1.0
|
| 144 |
+
+ msgpack-numpy==0.4.8
|
| 145 |
+
+ multidict==6.7.1
|
| 146 |
+
+ multiprocess==0.70.16
|
| 147 |
+
+ networkx==3.4.2
|
| 148 |
+
+ numpy==1.26.4
|
| 149 |
+
+ nvidia-cublas-cu12==12.6.4.1
|
| 150 |
+
+ nvidia-cuda-cupti-cu12==12.6.80
|
| 151 |
+
+ nvidia-cuda-nvrtc-cu12==12.6.77
|
| 152 |
+
+ nvidia-cuda-runtime-cu12==12.6.77
|
| 153 |
+
+ nvidia-cudnn-cu12==9.5.1.17
|
| 154 |
+
+ nvidia-cufft-cu12==11.3.0.4
|
| 155 |
+
+ nvidia-cufile-cu12==1.11.1.6
|
| 156 |
+
+ nvidia-curand-cu12==10.3.7.77
|
| 157 |
+
+ nvidia-cusolver-cu12==11.7.1.2
|
| 158 |
+
+ nvidia-cusparse-cu12==12.5.4.2
|
| 159 |
+
+ nvidia-cusparselt-cu12==0.6.3
|
| 160 |
+
+ nvidia-nccl-cu12==2.26.2
|
| 161 |
+
+ nvidia-nvjitlink-cu12==12.6.85
|
| 162 |
+
+ nvidia-nvtx-cu12==12.6.77
|
| 163 |
+
+ omegaconf==2.3.0
|
| 164 |
+
+ opencv-python-headless==4.11.0.86
|
| 165 |
+
+ packaging==26.2
|
| 166 |
+
+ pandas==2.2.3
|
| 167 |
+
+ peft==0.17.1
|
| 168 |
+
+ pfzy==0.3.4
|
| 169 |
+
+ pillow==12.2.0
|
| 170 |
+
+ platformdirs==4.9.6
|
| 171 |
+
+ prompt-toolkit==3.0.52
|
| 172 |
+
+ propcache==0.5.2
|
| 173 |
+
+ protobuf==6.33.6
|
| 174 |
+
+ psutil==7.2.2
|
| 175 |
+
+ pyarrow==24.0.0
|
| 176 |
+
+ pycparser==3.0
|
| 177 |
+
+ pydantic==2.13.4
|
| 178 |
+
+ pydantic-core==2.46.4
|
| 179 |
+
+ pygments==2.20.0
|
| 180 |
+
+ pyparsing==3.3.2
|
| 181 |
+
+ python-dateutil==2.9.0.post0
|
| 182 |
+
+ pytz==2026.2
|
| 183 |
+
+ pyyaml==6.0.3
|
| 184 |
+
+ pyzmq==27.0.1
|
| 185 |
+
+ regex==2026.5.9
|
| 186 |
+
+ requests==2.34.0
|
| 187 |
+
+ rich==15.0.0
|
| 188 |
+
+ safetensors==0.7.0
|
| 189 |
+
+ scikit-image==0.25.2
|
| 190 |
+
+ scipy==1.15.3
|
| 191 |
+
+ sentencepiece==0.2.1
|
| 192 |
+
+ sentry-sdk==2.59.0
|
| 193 |
+
+ setuptools==82.0.1
|
| 194 |
+
+ shtab==1.8.0
|
| 195 |
+
+ six==1.17.0
|
| 196 |
+
+ smmap==5.0.3
|
| 197 |
+
+ sympy==1.14.0
|
| 198 |
+
+ termcolor==3.2.0
|
| 199 |
+
+ tifffile==2025.5.10
|
| 200 |
+
+ tokenizers==0.22.2
|
| 201 |
+
+ torch==2.7.1
|
| 202 |
+
+ torchcodec==0.4.0
|
| 203 |
+
+ torchvision==0.22.1
|
| 204 |
+
+ tqdm==4.67.3
|
| 205 |
+
+ transformers==4.57.3
|
| 206 |
+
+ triton==3.3.1
|
| 207 |
+
+ typeguard==4.5.1
|
| 208 |
+
+ typing-extensions==4.15.0
|
| 209 |
+
+ typing-inspection==0.4.2
|
| 210 |
+
+ tyro==0.9.17
|
| 211 |
+
+ tzdata==2026.2
|
| 212 |
+
+ urllib3==2.7.0
|
| 213 |
+
+ wandb==0.23.0
|
| 214 |
+
+ wcwidth==0.7.0
|
| 215 |
+
+ wrapt==2.1.2
|
| 216 |
+
+ xxhash==3.7.0
|
| 217 |
+
+ yarl==1.23.0
|
| 218 |
+
+ zipp==3.23.1
|
outputs/_setup_logs/server_live.log
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Starting GR00T inference server...
|
| 2 |
+
Embodiment tag: EmbodimentTag.LIBERO_PANDA
|
| 3 |
+
Model path: checkpoints/GR00T-N1.7-LIBERO/libero_10
|
| 4 |
+
Device: cuda
|
| 5 |
+
Host: 127.0.0.1
|
| 6 |
+
Port: 5555
|
| 7 |
+
/home/ubuntu/VLA_SAE/.venv/lib/python3.10/site-packages/albumentations/__init__.py:13: UserWarning: A new version of Albumentations is available: 2.0.8 (you have 1.4.18). Upgrade using: pip install -U albumentations. To disable automatic update checks, set the environment variable NO_ALBUMENTATIONS_UPDATE to 1.
|
| 8 |
+
check_for_updates()
|
| 9 |
+
flash_attn is not installed. Falling back to sdpa attention. Install flash-attn for better performance: pip install flash-attn
|
| 10 |
+
/home/ubuntu/VLA_SAE/gr00t/model/modules/dit.py:255: FutureWarning: Accessing config attribute `compute_dtype` directly via 'AlternateVLDiT' object attribute is deprecated. Please access 'compute_dtype' over 'AlternateVLDiT's config object instead, e.g. 'unet.config.compute_dtype'.
|
| 11 |
+
embedding_dim=self.inner_dim, compute_dtype=self.compute_dtype
|
| 12 |
+
/home/ubuntu/VLA_SAE/gr00t/model/modules/dit.py:286: FutureWarning: Accessing config attribute `output_dim` directly via 'AlternateVLDiT' object attribute is deprecated. Please access 'output_dim' over 'AlternateVLDiT's config object instead, e.g. 'unet.config.output_dim'.
|
| 13 |
+
self.proj_out_2 = nn.Linear(self.inner_dim, self.output_dim)
|
| 14 |
+
Total number of DiT parameters: 1091722240
|
| 15 |
+
Total number of SelfAttentionTransformer parameters: 201433088
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
✓ Server ready — listening on 127.0.0.1:5555
|
| 19 |
+
|
| 20 |
+
Server is ready and listening on tcp://127.0.0.1:5555
|
outputs/_setup_logs/smoke_run.log
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Loaded 10 scenarios from examples/LIBERO/smoke_tests/scenarios_10.yaml
|
| 2 |
+
labels: 8 normal, 2 abnormal_probe
|
| 3 |
+
|
| 4 |
+
Expected GR00T server command (run this in a separate terminal):
|
| 5 |
+
uv run python gr00t/eval/run_gr00t_server.py --model-path checkpoints/GR00T-N1.7-LIBERO/libero_10 --embodiment-tag LIBERO_PANDA --use-sim-policy-wrapper
|
| 6 |
+
|
| 7 |
+
NOTE: The GR00T-N1.7 backbone 'nvidia/Cosmos-Reason2-2B' is a GATED HuggingFace repo.
|
| 8 |
+
To start the server you must:
|
| 9 |
+
1. Request access at https://huggingface.co/nvidia/Cosmos-Reason2-2B (one click, usually instant).
|
| 10 |
+
2. Authenticate, e.g. export HF_TOKEN=hf_xxx (or: uv run hf auth login)
|
| 11 |
+
3. Re-run the server / smoke tests.
|
| 12 |
+
|
| 13 |
+
Model checkpoint OK: checkpoints/GR00T-N1.7-LIBERO/libero_10
|
| 14 |
+
Run directory: outputs/libero_smoke_tests/20260512_122756
|
| 15 |
+
GR00T server reachable at 127.0.0.1:5555.
|
| 16 |
+
LIBERO rollout interpreter: /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/bin/python
|
| 17 |
+
|
| 18 |
+
[1/10] normal_kitchen3_moka_pot_on_stove (normal) env=libero_sim/KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it seed=1000 max_episode_steps=50
|
| 19 |
+
-> ok rc=0 17.3s actions=yes video=yes success=False
|
| 20 |
+
|
| 21 |
+
[2/10] normal_kitchen4_bowl_in_drawer (normal) env=libero_sim/KITCHEN_SCENE4_put_the_black_bowl_in_the_bottom_drawer_of_the_cabinet_and_close_it seed=1001 max_episode_steps=50
|
| 22 |
+
-> ok rc=0 12.1s actions=yes video=yes success=False
|
| 23 |
+
|
| 24 |
+
[3/10] normal_living1_soup_and_cheese_in_basket (normal) env=libero_sim/LIVING_ROOM_SCENE1_put_both_the_alphabet_soup_and_the_cream_cheese_box_in_the_basket seed=1002 max_episode_steps=50
|
| 25 |
+
-> ok rc=0 13.3s actions=yes video=yes success=False
|
| 26 |
+
|
| 27 |
+
[4/10] normal_living2_soup_and_tomato_in_basket (normal) env=libero_sim/LIVING_ROOM_SCENE2_put_both_the_alphabet_soup_and_the_tomato_sauce_in_the_basket seed=1003 max_episode_steps=50
|
| 28 |
+
-> ok rc=0 14.2s actions=yes video=yes success=False
|
| 29 |
+
|
| 30 |
+
[5/10] normal_study1_book_in_caddy (normal) env=libero_sim/STUDY_SCENE1_pick_up_the_book_and_place_it_in_the_back_compartment_of_the_caddy seed=1004 max_episode_steps=50
|
| 31 |
+
-> ok rc=0 12.0s actions=yes video=yes success=False
|
| 32 |
+
|
| 33 |
+
[6/10] normal_kitchen8_both_moka_pots_on_stove (normal) env=libero_sim/KITCHEN_SCENE8_put_both_moka_pots_on_the_stove seed=1005 max_episode_steps=50
|
| 34 |
+
-> ok rc=0 10.8s actions=yes video=yes success=False
|
| 35 |
+
|
| 36 |
+
[7/10] normal_kitchen6_mug_in_microwave (normal) env=libero_sim/KITCHEN_SCENE6_put_the_yellow_and_white_mug_in_the_microwave_and_close_it seed=1006 max_episode_steps=50
|
| 37 |
+
-> ok rc=0 12.1s actions=yes video=yes success=False
|
| 38 |
+
|
| 39 |
+
[8/10] normal_living5_two_mugs_on_plates (normal) env=libero_sim/LIVING_ROOM_SCENE5_put_the_white_mug_on_the_left_plate_and_put_the_yellow_and_white_mug_on_the_right_plate seed=1007 max_episode_steps=50
|
| 40 |
+
-> ok rc=0 15.1s actions=yes video=yes success=False
|
| 41 |
+
|
| 42 |
+
[9/10] abnormal_probe_obs_noise (abnormal_probe) env=libero_sim/KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it seed=2000 max_episode_steps=50
|
| 43 |
+
-> ok rc=0 10.8s actions=yes video=yes success=False
|
| 44 |
+
|
| 45 |
+
[10/10] abnormal_probe_short_timeout (abnormal_probe) env=libero_sim/LIVING_ROOM_SCENE2_put_both_the_alphabet_soup_and_the_tomato_sauce_in_the_basket seed=2001 max_episode_steps=16
|
| 46 |
+
-> ok rc=0 13.1s actions=yes video=yes success=False
|
| 47 |
+
|
| 48 |
+
======== SMOKE-TEST SUMMARY ========
|
| 49 |
+
scenario_id | label | seed | rollout_started | actions_produced | video_saved | success | output_dir | error_if_any
|
| 50 |
+
-----------------------------------------+----------------+------+-----------------+------------------+-------------+---------+-------------------------------------------------------------------------------------+-------------
|
| 51 |
+
normal_kitchen3_moka_pot_on_stove | normal | 1000 | yes | yes | yes | False | outputs/libero_smoke_tests/20260512_122756/normal_kitchen3_moka_pot_on_stove |
|
| 52 |
+
normal_kitchen4_bowl_in_drawer | normal | 1001 | yes | yes | yes | False | outputs/libero_smoke_tests/20260512_122756/normal_kitchen4_bowl_in_drawer |
|
| 53 |
+
normal_living1_soup_and_cheese_in_basket | normal | 1002 | yes | yes | yes | False | outputs/libero_smoke_tests/20260512_122756/normal_living1_soup_and_cheese_in_basket |
|
| 54 |
+
normal_living2_soup_and_tomato_in_basket | normal | 1003 | yes | yes | yes | False | outputs/libero_smoke_tests/20260512_122756/normal_living2_soup_and_tomato_in_basket |
|
| 55 |
+
normal_study1_book_in_caddy | normal | 1004 | yes | yes | yes | False | outputs/libero_smoke_tests/20260512_122756/normal_study1_book_in_caddy |
|
| 56 |
+
normal_kitchen8_both_moka_pots_on_stove | normal | 1005 | yes | yes | yes | False | outputs/libero_smoke_tests/20260512_122756/normal_kitchen8_both_moka_pots_on_stove |
|
| 57 |
+
normal_kitchen6_mug_in_microwave | normal | 1006 | yes | yes | yes | False | outputs/libero_smoke_tests/20260512_122756/normal_kitchen6_mug_in_microwave |
|
| 58 |
+
normal_living5_two_mugs_on_plates | normal | 1007 | yes | yes | yes | False | outputs/libero_smoke_tests/20260512_122756/normal_living5_two_mugs_on_plates |
|
| 59 |
+
abnormal_probe_obs_noise | abnormal_probe | 2000 | yes | yes | yes | False | outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise |
|
| 60 |
+
abnormal_probe_short_timeout | abnormal_probe | 2001 | yes | yes | yes | False | outputs/libero_smoke_tests/20260512_122756/abnormal_probe_short_timeout |
|
| 61 |
+
|
| 62 |
+
Wrote: outputs/libero_smoke_tests/20260512_122756/summary.json
|
| 63 |
+
Wrote: outputs/libero_smoke_tests/20260512_122756/summary.md
|
| 64 |
+
|
| 65 |
+
Review the videos with:
|
| 66 |
+
python /home/ubuntu/VLA_SAE/examples/LIBERO/smoke_tests/review_smoke_tests.py --run-dir outputs/libero_smoke_tests/20260512_122756
|
| 67 |
+
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/actions.npy
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b7b5e56b63aa6efeeb95fae090d9988add5621839fd3eb16aa95c6b22fad60fe
|
| 3 |
+
size 3264
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00000.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00001.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00002.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00003.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00004.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00005.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00006.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00007.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00008.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00009.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00010.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00011.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00012.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00013.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00014.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00015.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00016.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00017.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00018.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00019.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00020.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00021.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00022.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00023.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames/frame_00024.png
ADDED
|
Git LFS Details
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/metadata.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"scenario_id": "abnormal_probe_obs_noise",
|
| 3 |
+
"label": "abnormal_probe",
|
| 4 |
+
"manifest_entry": {
|
| 5 |
+
"id": "abnormal_probe_obs_noise",
|
| 6 |
+
"label": "abnormal_probe",
|
| 7 |
+
"env_name": "libero_sim/KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it",
|
| 8 |
+
"instruction": "turn on the stove and put the moka pot on it",
|
| 9 |
+
"instruction_override": null,
|
| 10 |
+
"seed": 2000,
|
| 11 |
+
"max_episode_steps": 50,
|
| 12 |
+
"n_action_steps": 8,
|
| 13 |
+
"save_video": true,
|
| 14 |
+
"obs_noise_std": 6.0,
|
| 15 |
+
"action_repeat": 1,
|
| 16 |
+
"notes": "Simulation-only perturbation: mild zero-mean Gaussian noise (sigma=6 in 0-255 pixel units) is added to the image observations *before* they are sent to the policy. Smoke test only - this is a placeholder for future, more principled distribution-shift probes; no complex anomaly logic."
|
| 17 |
+
},
|
| 18 |
+
"resolved": {
|
| 19 |
+
"env_name": "libero_sim/KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it",
|
| 20 |
+
"seed": 2000,
|
| 21 |
+
"max_episode_steps": 50,
|
| 22 |
+
"n_action_steps": 8,
|
| 23 |
+
"save_video": true,
|
| 24 |
+
"save_frames": true,
|
| 25 |
+
"obs_noise_std": 6.0,
|
| 26 |
+
"action_repeat": 1,
|
| 27 |
+
"instruction_override": null
|
| 28 |
+
},
|
| 29 |
+
"model_path": "checkpoints/GR00T-N1.7-LIBERO/libero_10",
|
| 30 |
+
"policy_server": {
|
| 31 |
+
"host": "127.0.0.1",
|
| 32 |
+
"port": 5555
|
| 33 |
+
},
|
| 34 |
+
"libero_python": "/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/bin/python",
|
| 35 |
+
"timestamp": "2026-05-12T12:29:46"
|
| 36 |
+
}
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/rollout_summary.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"env_name": "libero_sim/KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it",
|
| 3 |
+
"seed": 2000,
|
| 4 |
+
"requested_max_episode_steps": 50,
|
| 5 |
+
"n_action_steps": 8,
|
| 6 |
+
"n_get_action_calls": 7,
|
| 7 |
+
"episode_successes": [
|
| 8 |
+
false
|
| 9 |
+
],
|
| 10 |
+
"success": false,
|
| 11 |
+
"episode_length": 7,
|
| 12 |
+
"episode_reward": 0.0,
|
| 13 |
+
"elapsed_sec": 6.133596658706665,
|
| 14 |
+
"obs_noise_std": 6.0,
|
| 15 |
+
"action_repeat": 1,
|
| 16 |
+
"video_path": "outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/video.mp4",
|
| 17 |
+
"actions_path": "outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/actions.npy",
|
| 18 |
+
"policy_server": {
|
| 19 |
+
"host": "127.0.0.1",
|
| 20 |
+
"port": 5555
|
| 21 |
+
}
|
| 22 |
+
}
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/stderr.log
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[robosuite WARNING] No private macro file found! (__init__.py:7)
|
| 2 |
+
[robosuite WARNING] It is recommended to use a private macro file (__init__.py:8)
|
| 3 |
+
[robosuite WARNING] To setup, run: python /home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/robosuite/scripts/setup_macros.py (__init__.py:9)
|
| 4 |
+
Matplotlib created a temporary config/cache directory at /tmp/matplotlib-syt5xusc because the default path (/home/ubuntu/.config/matplotlib) is not a writable directory; it is highly recommended to set the MPLCONFIGDIR environment variable to a writable directory, in particular to speed up the import of Matplotlib and to better support multiprocessing.
|
| 5 |
+
Gym has been unmaintained since 2022 and does not support NumPy 2.0 amongst other critical functionality.
|
| 6 |
+
Please upgrade to Gymnasium, the maintained drop-in replacement of Gym, or contact the authors of your software and request that they upgrade.
|
| 7 |
+
See the migration guide at https://gymnasium.farama.org/introduction/migration_guide/ for additional information.
|
| 8 |
+
/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/gymnasium/utils/passive_env_checker.py:159: UserWarning: [33mWARN: The obs returned by the `reset()` method is not within the observation space.[0m
|
| 9 |
+
logger.warn(f"{pre} is not within the observation space.")
|
| 10 |
+
/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/gymnasium/utils/passive_env_checker.py:135: UserWarning: [33mWARN: The obs returned by the `reset()` method was expecting numpy array dtype to be float32, actual type: float64[0m
|
| 11 |
+
logger.warn(
|
| 12 |
+
/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/gymnasium/utils/passive_env_checker.py:131: UserWarning: [33mWARN: The obs returned by the `reset()` method was expecting a numpy array, actual type: <class 'list'>[0m
|
| 13 |
+
logger.warn(
|
| 14 |
+
/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/gymnasium/spaces/box.py:240: UserWarning: [33mWARN: Casting input x to numpy array.[0m
|
| 15 |
+
gym.logger.warn("Casting input x to numpy array.")
|
| 16 |
+
|
| 17 |
+
logger.warn(f"{pre} is not within the observation space.")
|
| 18 |
+
/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/gymnasium/utils/passive_env_checker.py:135: UserWarning: [33mWARN: The obs returned by the `step()` method was expecting numpy array dtype to be float32, actual type: float64[0m
|
| 19 |
+
logger.warn(
|
| 20 |
+
/home/ubuntu/VLA_SAE/gr00t/eval/sim/LIBERO/libero_uv/.venv/lib/python3.10/site-packages/gymnasium/utils/passive_env_checker.py:131: UserWarning: [33mWARN: The obs returned by the `step()` method was expecting a numpy array, actual type: <class 'list'>[0m
|
| 21 |
+
logger.warn(
|
| 22 |
+
|
outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/stdout.log
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Running collecting 1 episodes for libero_sim/KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it with 1 vec envs
|
| 2 |
+
[info] using task orders [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
| 3 |
+
[info] using task orders [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
| 4 |
+
[info] using task orders [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
| 5 |
+
[info] using task orders [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
| 6 |
+
Collecting 1 episodes took 6.133342027664185 seconds
|
| 7 |
+
SMOKE_RESULT_JSON: {"ok": true, "rollout_started": true, "actions_produced": true, "video_saved": true, "success": false, "error": null, "env_name": "libero_sim/KITCHEN_SCENE3_turn_on_the_stove_and_put_the_moka_pot_on_it", "seed": 2000, "n_action_calls": 7, "episode_length": 7, "episode_reward": 0.0, "video_path": "outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/video.mp4", "actions_path": "outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/actions.npy", "video_source_name": "1c2407cd-5353-4895-957f-f8b3986573d2_s0.mp4", "frames_dir": "outputs/libero_smoke_tests/20260512_122756/abnormal_probe_obs_noise/frames", "n_frames": 25}
|