From eba346a17206b34330b9470debb4c6eb4103a37e Mon Sep 17 00:00:00 2001 From: Aidan Gao Date: Tue, 20 Oct 2026 16:47:08 -0400 Subject: [PATCH] =?UTF-8?q?arc:=20audit=20fixes=20=E2=80=94=20no=20min-spe?= =?UTF-8?q?ed=20floor=20on=20absolute=20clocks,=20mode-0=20replan,=20pool?= =?UTF-8?q?=20pins,=20seeded=20W&B=20ids?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the must/should findings of the 2026-10-20 audit of #272 (each reproduced on real data before fixing): - Absolute-duration decode no longer clips at 0.01 m/s. Under one clock per arm the translation clock also times rotation and gripper; the floor rewrote 720 of 2,113 real slow-pace ground-truth chunks (rotation replayed up to 10x faster, replan shortened by up to 27 ticks) and was discontinuous at zero distance. MAX_SPEED (5 m/s) stays as the motion bound; min_speed is removed. - FirstStreamArcDecoder with first_stream=0 reports no replan count again, so the policy's replan_every applies (it executed the whole 100-tick chunk open-loop). - The sp366e51 stationery pool pins its episode count and name digest (417 / 24), so a missing or deleted episode fails the run instead of training on less. - frozen_yam_arc W&B ids carry the seed (${name}_s${seed}); a second seed no longer reuses the s42 id that resume: never rejects after the norm-stats pass. - Tests: velocity/duration parity at 0.005 m/s, eval/robot parity on a noisy predicted token with garbage beyond the executed prefix, mode-0 replan, the time-recipe retimer speed controls, pool pins and the seeded id. The slow-parity and replan pins fail on the #272 code. Checks: 125 contract/recipe tests pass; 92-file sweep 1,374 passed with the same 11 pre-existing failures; shipped-YAML audit 349/349; ruff clean. Co-Authored-By: Claude Opus 5.5 --- docs/ARC_CLOCK_CONTRACT.md | 3 +- .../abc_arc/stationery_sp366e51_bc_time.yaml | 6 ++ .../experiment/_shared/frozen_yam_arc.yaml | 2 +- .../arcdur_abstime/fold_and_stack_towels.yaml | 1 - .../arcdur_abstime/organize_stationary.yaml | 1 - .../real/arcdur_abstime/wireless_earbuds.yaml | 1 - egomimic/rldb/zarr/arc_length_tokenizer.py | 19 +++--- egomimic/robot/arc_decoder.py | 2 +- tests/test_arc_clock_contract.py | 60 ++++++++++++++++--- tests/test_arc_first_delay.py | 7 +++ tests/test_arcdur_abstime_recipes.py | 3 + tests/test_time_retimer_controls.py | 35 +++++++++++ 12 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 tests/test_time_retimer_controls.py diff --git a/docs/ARC_CLOCK_CONTRACT.md b/docs/ARC_CLOCK_CONTRACT.md index 0d91509d9..ab04958aa 100644 --- a/docs/ARC_CLOCK_CONTRACT.md +++ b/docs/ARC_CLOCK_CONTRACT.md @@ -55,7 +55,8 @@ There is no repeated final padding clock. - duration: every row is the absolute seconds from the chunk anchor to that waypoint (row zero is the start delay). Decoding projects the rows once onto the nearest non-decreasing clock by isotonic regression on the slack above - the fastest clock allowed by the translation speed bounds (0.01-5 m/s), so a backwards pair is averaged and no local + the fastest clock allowed by the 5 m/s translation max speed (no min speed: + rotation and gripper ride slow arms), so a backwards pair is averaged and no local error accumulates into later rows. Provenance `timing_layout` is `first_delay_absolute_v1`; velocity keeps `first_delay_incoming_v1`. diff --git a/egomimic/hydra_configs/data/abc_arc/stationery_sp366e51_bc_time.yaml b/egomimic/hydra_configs/data/abc_arc/stationery_sp366e51_bc_time.yaml index 29fe118d8..9f2ea4e03 100644 --- a/egomimic/hydra_configs/data/abc_arc/stationery_sp366e51_bc_time.yaml +++ b/egomimic/hydra_configs/data/abc_arc/stationery_sp366e51_bc_time.yaml @@ -12,3 +12,9 @@ train_datasets: filters: filter_lambdas: - 'lambda row, S=frozenset({''2026-09-19-01-47-36-589000'',''2026-09-19-01-49-02-203000'',''2026-09-19-01-50-15-148000'',''2026-09-19-01-51-24-290000'',''2026-09-19-01-52-36-363000'',''2026-09-19-01-53-54-074000'',''2026-09-19-01-57-05-569000'',''2026-09-19-01-58-31-881000'',''2026-09-19-19-35-14-834000'',''2026-09-19-19-36-18-357000'',''2026-09-19-19-37-28-567000'',''2026-09-19-19-39-01-076000'',''2026-09-19-19-40-20-947000'',''2026-09-19-19-42-56-857000'',''2026-09-19-19-45-44-201000'',''2026-09-19-19-49-14-481000'',''2026-09-19-19-51-14-112000'',''2026-09-19-19-53-03-476000'',''2026-09-19-19-56-10-208000'',''2026-09-19-19-58-28-680000'',''2026-09-19-19-59-57-199000'',''2026-09-19-20-02-18-897000'',''2026-09-19-20-04-54-894000'',''2026-09-19-20-08-17-219000'',''2026-09-19-20-09-45-044000'',''2026-09-19-20-13-34-058000'',''2026-09-24-16-16-51-044000'',''2026-09-24-16-22-08-710000'',''2026-09-24-16-25-15-450000'',''2026-09-24-16-34-19-936000'',''2026-09-24-16-36-18-264000'',''2026-09-24-16-37-25-665000'',''2026-09-24-16-38-27-736000'',''2026-09-24-16-39-36-320000'',''2026-09-24-16-41-18-274000'',''2026-09-24-16-44-07-331000'',''2026-09-24-16-45-05-036000'',''2026-09-24-16-45-59-484000'',''2026-09-24-16-47-33-064000'',''2026-09-24-16-48-35-144000'',''2026-09-24-16-49-40-785000'',''2026-09-24-16-53-27-672000'',''2026-09-24-16-55-19-499000'',''2026-09-24-16-56-21-136000'',''2026-09-24-16-57-27-653000'',''2026-09-24-16-58-26-745000'',''2026-09-24-16-59-25-700000'',''2026-09-24-17-00-20-308000'',''2026-09-24-17-02-52-967000'',''2026-09-24-17-03-56-727000'',''2026-09-24-17-08-24-075000'',''2026-09-24-17-09-25-569000'',''2026-09-24-17-10-17-502000'',''2026-09-24-17-11-16-797000'',''2026-09-24-17-12-19-227000'',''2026-09-24-17-14-09-111000'',''2026-09-24-17-17-15-034000'',''2026-09-24-17-18-35-123000'',''2026-09-24-17-20-59-494000'',''2026-09-24-17-21-59-605000'',''2026-09-24-17-22-54-629000'',''2026-09-24-17-23-57-324000'',''2026-09-24-17-24-57-750000'',''2026-09-24-17-25-58-219000'',''2026-09-24-17-27-48-121000'',''2026-09-24-17-28-40-884000'',''2026-09-24-17-29-38-666000'',''2026-09-24-17-30-45-314000'',''2026-09-24-17-32-45-467000'',''2026-09-24-17-33-40-085000'',''2026-09-24-17-36-04-286000'',''2026-09-24-17-37-05-582000'',''2026-09-24-17-38-57-378000'',''2026-09-24-17-39-50-290000'',''2026-09-24-17-40-48-696000'',''2026-09-24-17-41-59-261000'',''2026-09-24-17-43-00-495000'',''2026-09-24-17-45-36-132000'',''2026-09-24-17-46-40-827000'',''2026-09-24-17-48-03-886000'',''2026-09-24-17-50-03-465000'',''2026-09-24-17-50-59-865000'',''2026-09-24-17-52-36-200000'',''2026-09-24-17-53-33-165000'',''2026-09-24-17-54-37-327000'',''2026-09-24-17-55-39-134000'',''2026-09-24-17-56-27-710000'',''2026-09-24-17-57-26-606000'',''2026-09-24-17-59-13-829000'',''2026-09-24-18-00-21-046000'',''2026-09-24-18-57-41-603000'',''2026-09-24-18-58-34-949000'',''2026-09-24-18-59-31-514000'',''2026-09-24-19-00-19-168000'',''2026-09-24-19-01-06-804000'',''2026-09-24-19-01-53-682000'',''2026-09-24-19-02-45-508000'',''2026-09-24-19-03-45-175000'',''2026-09-24-19-06-08-426000'',''2026-09-24-19-06-56-997000'',''2026-09-24-19-08-37-016000'',''2026-09-24-19-09-29-231000'',''2026-09-24-19-10-59-504000'',''2026-09-24-19-14-02-883000'',''2026-09-24-19-14-52-549000'',''2026-09-24-19-16-07-833000'',''2026-09-24-19-17-58-826000'',''2026-09-24-19-18-51-173000'',''2026-09-24-19-20-07-979000'',''2026-09-24-19-20-57-726000'',''2026-09-24-19-21-48-679000'',''2026-09-24-19-22-45-278000'',''2026-09-24-19-23-37-038000'',''2026-09-24-19-24-36-232000'',''2026-09-24-19-25-28-517000'',''2026-09-24-19-28-10-393000'',''2026-09-24-19-30-55-197000'',''2026-09-24-19-32-03-808000'',''2026-09-24-19-33-18-908000'',''2026-09-24-19-34-30-284000'',''2026-09-24-19-36-14-643000'',''2026-09-24-19-37-52-092000'',''2026-09-24-19-38-53-599000'',''2026-09-24-19-40-00-912000'',''2026-09-24-19-42-21-667000'',''2026-09-24-19-44-44-886000'',''2026-09-24-19-45-54-340000'',''2026-09-24-19-48-06-583000'',''2026-09-24-19-49-27-120000'',''2026-09-24-19-51-07-736000'',''2026-09-24-19-52-01-230000'',''2026-09-24-19-52-55-074000'',''2026-09-24-19-53-56-868000'',''2026-09-24-19-57-19-690000'',''2026-09-24-19-58-17-174000'',''2026-09-24-19-59-47-939000'',''2026-09-24-20-42-36-629000'',''2026-09-24-20-43-29-804000'',''2026-09-24-20-47-44-769000'',''2026-09-24-20-48-44-943000'',''2026-09-24-20-49-50-127000'',''2026-09-24-20-51-36-528000'',''2026-09-24-20-52-28-100000'',''2026-09-24-20-53-23-617000'',''2026-09-24-20-55-00-732000'',''2026-09-24-20-57-04-321000'',''2026-09-24-20-58-29-910000'',''2026-09-24-21-00-02-332000'',''2026-09-24-21-01-01-616000'',''2026-09-24-21-03-03-528000'',''2026-09-24-21-04-05-038000'',''2026-09-24-21-04-59-455000'',''2026-09-24-21-05-58-338000'',''2026-09-24-21-07-01-162000'',''2026-09-24-21-07-59-683000'',''2026-09-24-21-09-41-533000'',''2026-09-24-21-10-37-375000'',''2026-09-24-21-11-51-470000'',''2026-09-24-21-12-51-875000'',''2026-09-24-21-13-53-425000'',''2026-09-24-21-14-57-878000'',''2026-09-24-21-34-24-204000'',''2026-09-24-21-35-22-108000'',''2026-09-24-21-36-18-946000'',''2026-09-24-21-37-15-311000'',''2026-09-24-21-38-11-324000'',''2026-09-24-21-41-17-407000'',''2026-09-24-21-46-50-861000'',''2026-09-24-21-47-47-667000'',''2026-09-24-21-48-49-498000'',''2026-09-24-21-54-47-673000'',''2026-09-24-21-55-43-256000'',''2026-09-24-21-56-42-401000'',''2026-09-24-21-58-04-104000'',''2026-09-24-21-59-02-117000'',''2026-09-24-21-59-55-644000'',''2026-09-24-22-00-51-694000'',''2026-09-24-22-14-04-304000'',''2026-09-24-22-17-02-699000'',''2026-09-24-22-18-02-378000'',''2026-09-24-22-19-03-992000'',''2026-09-24-22-19-58-285000'',''2026-09-24-22-21-19-774000'',''2026-09-24-22-22-35-965000'',''2026-09-24-22-23-37-661000'',''2026-09-24-22-24-30-827000'',''2026-09-24-22-25-19-802000'',''2026-09-24-22-26-08-276000'',''2026-09-24-22-26-59-473000'',''2026-09-24-22-27-45-770000'',''2026-09-24-22-28-39-501000'',''2026-09-24-22-29-39-650000'',''2026-09-24-22-30-34-937000'',''2026-09-24-22-31-47-616000'',''2026-09-24-22-33-19-863000'',''2026-09-24-22-34-41-973000'',''2026-09-24-22-35-36-943000'',''2026-09-24-22-36-43-965000'',''2026-09-24-22-38-33-243000'',''2026-09-24-22-39-24-021000'',''2026-09-24-22-40-22-750000'',''2026-09-24-22-41-14-932000'',''2026-09-24-22-42-01-640000'',''2026-09-24-22-42-57-873000'',''2026-09-24-22-43-49-908000'',''2026-09-24-22-44-46-619000'',''2026-09-24-22-45-41-221000'',''2026-09-24-22-46-32-582000'',''2026-09-24-22-47-38-095000'',''2026-09-24-22-49-57-802000'',''2026-09-24-22-51-05-877000'',''2026-09-24-22-52-31-595000'',''2026-09-24-22-53-30-542000'',''2026-09-24-22-54-24-512000'',''2026-09-24-22-55-23-120000'',''2026-09-24-22-56-30-796000'',''2026-09-24-22-57-21-956000'',''2026-10-04-23-36-23-913000'',''2026-10-04-23-37-34-436000'',''2026-10-04-23-38-29-376000'',''2026-10-04-23-39-16-472000'',''2026-10-04-23-40-52-328000'',''2026-10-04-23-41-52-712000'',''2026-10-04-23-42-56-053000'',''2026-10-04-23-43-57-035000'',''2026-10-04-23-44-53-607000'',''2026-10-04-23-46-52-434000'',''2026-10-04-23-47-59-085000'',''2026-10-04-23-49-35-837000'',''2026-10-04-23-50-56-912000'',''2026-10-04-23-54-51-710000'',''2026-10-04-23-56-06-595000'',''2026-10-04-23-57-46-222000'',''2026-10-04-23-58-41-448000'',''2026-10-05-00-02-16-110000'',''2026-10-05-00-03-11-498000'',''2026-10-05-00-05-08-791000'',''2026-10-05-00-05-59-212000'',''2026-10-05-00-06-58-502000'',''2026-10-05-00-07-58-872000'',''2026-10-05-00-08-53-264000'',''2026-10-05-00-10-35-762000'',''2026-10-05-00-11-29-729000'',''2026-10-05-00-12-18-661000'',''2026-10-05-00-13-29-652000'',''2026-10-05-00-14-21-429000'',''2026-10-05-00-15-14-610000'',''2026-10-05-00-16-08-909000'',''2026-10-05-00-16-59-355000'',''2026-10-05-00-17-57-116000'',''2026-10-05-00-18-55-994000'',''2026-10-05-00-19-54-763000'',''2026-10-05-00-20-47-008000'',''2026-10-05-00-21-58-436000'',''2026-10-05-00-22-57-604000'',''2026-10-05-00-24-31-007000'',''2026-10-05-00-26-07-584000'',''2026-10-05-00-27-15-801000'',''2026-10-05-00-31-38-783000'',''2026-10-05-00-32-45-106000'',''2026-10-05-00-33-47-559000'',''2026-10-05-00-35-12-990000'',''2026-10-05-00-36-16-089000'',''2026-10-05-00-37-13-822000'',''2026-10-05-00-38-18-232000'',''2026-10-05-00-39-24-367000'',''2026-10-05-00-41-27-701000'',''2026-10-05-00-42-19-090000'',''2026-10-05-00-43-16-341000'',''2026-10-05-00-44-13-105000'',''2026-10-05-00-45-25-951000'',''2026-10-05-00-56-21-863000'',''2026-10-05-00-57-25-932000'',''2026-10-05-00-58-32-168000'',''2026-10-05-00-59-27-926000'',''2026-10-05-01-00-54-974000'',''2026-10-05-01-01-45-296000'',''2026-10-05-01-02-49-533000'',''2026-10-05-01-03-48-927000'',''2026-10-05-01-04-46-143000'',''2026-10-05-01-06-29-837000'',''2026-10-05-01-07-25-048000'',''2026-10-05-01-08-20-372000'',''2026-10-05-01-09-18-244000'',''2026-10-05-01-10-19-770000'',''2026-10-05-01-12-49-910000'',''2026-10-05-01-13-43-568000'',''2026-10-05-01-14-39-929000'',''2026-10-05-01-16-21-684000'',''2026-10-05-01-17-23-893000'',''2026-10-05-01-18-26-426000'',''2026-10-05-01-20-34-541000'',''2026-10-05-01-22-50-764000'',''2026-10-05-01-23-48-736000'',''2026-10-05-01-24-47-072000'',''2026-10-05-01-25-53-327000'',''2026-10-05-01-26-55-652000'',''2026-10-05-01-28-28-172000'',''2026-10-05-01-29-23-146000'',''2026-10-05-01-30-18-762000'',''2026-10-05-01-31-16-645000'',''2026-10-05-01-32-23-008000'',''2026-10-05-01-33-17-813000'',''2026-10-05-01-34-12-399000'',''2026-10-05-01-35-22-794000'',''2026-10-05-01-36-20-383000'',''2026-10-05-01-37-22-146000'',''2026-10-05-01-38-38-647000'',''2026-10-05-01-39-35-149000'',''2026-10-05-01-40-39-473000'',''2026-10-05-01-44-28-355000'',''2026-10-05-01-45-48-226000'',''2026-10-05-01-49-21-546000'',''2026-10-05-01-50-27-816000'',''2026-10-05-01-51-57-507000'',''2026-10-05-01-52-56-426000'',''2026-10-05-01-54-06-709000'',''2026-10-05-01-55-23-640000'',''2026-10-05-01-56-21-722000'',''2026-10-05-01-57-32-892000'',''2026-10-05-01-59-23-690000'',''2026-10-05-02-00-19-041000'',''2026-10-05-02-01-24-768000'',''2026-10-05-02-02-55-119000'',''2026-10-05-02-03-58-215000'',''2026-10-05-02-05-10-388000'',''2026-10-05-02-06-12-440000'',''2026-10-05-02-07-34-346000'',''2026-10-05-02-10-20-131000'',''2026-10-05-02-13-30-991000'',''2026-10-05-02-14-39-232000'',''2026-10-05-02-15-46-274000'',''2026-10-05-02-16-53-310000'',''2026-10-05-03-01-44-578000'',''2026-10-05-03-03-05-872000'',''2026-10-05-03-03-57-829000'',''2026-10-05-03-05-06-326000'',''2026-10-05-03-06-07-901000'',''2026-10-05-03-07-47-499000'',''2026-10-05-03-09-15-122000'',''2026-10-05-03-17-52-345000'',''2026-10-05-03-18-47-826000'',''2026-10-05-03-19-43-195000'',''2026-10-05-03-20-43-208000'',''2026-10-05-03-21-36-796000'',''2026-10-05-03-22-28-967000'',''2026-10-05-03-23-24-917000'',''2026-10-05-03-24-25-981000'',''2026-10-05-03-26-31-444000'',''2026-10-05-03-27-32-853000'',''2026-10-05-03-28-28-302000'',''2026-10-05-03-29-26-224000'',''2026-10-05-03-30-24-839000'',''2026-10-05-03-31-18-148000'',''2026-10-05-03-32-14-426000'',''2026-10-05-03-33-06-745000'',''2026-10-05-03-34-02-127000'',''2026-10-05-03-35-01-817000'',''2026-10-05-03-37-50-641000'',''2026-10-05-03-40-11-631000'',''2026-10-05-03-41-07-723000'',''2026-10-05-03-41-59-748000'',''2026-10-05-03-43-21-107000'',''2026-10-05-03-44-36-538000'',''2026-10-05-03-47-00-789000'',''2026-10-05-03-48-20-592000'',''2026-10-20-01-47-13-426000'',''2026-10-20-01-48-18-264000'',''2026-10-20-01-49-12-681000'',''2026-10-20-01-50-38-577000'',''2026-10-20-01-51-32-479000'',''2026-10-20-01-52-30-293000'',''2026-10-20-02-06-15-769000'',''2026-10-20-02-07-29-700000'',''2026-10-20-02-09-08-674000'',''2026-10-20-02-09-56-929000'',''2026-10-20-02-10-51-322000'',''2026-10-20-02-13-08-838000'',''2026-10-20-02-14-03-456000'',''2026-10-20-02-14-46-879000'',''2026-10-20-02-18-48-381000'',''2026-10-20-02-20-23-532000'',''2026-10-20-02-21-46-513000'',''2026-10-20-02-22-45-822000'',''2026-10-20-02-23-49-036000'',''2026-10-20-02-24-37-976000'',''2026-10-20-02-26-58-177000'',''2026-10-20-02-28-00-539000'',''2026-10-20-02-28-58-844000'',''2026-10-20-02-29-55-296000'',''2026-10-20-02-30-45-458000'',''2026-10-20-02-31-39-339000'',''2026-10-20-02-33-01-715000'',''2026-10-20-02-34-06-147000'',''2026-10-20-02-35-32-775000'',''2026-10-20-02-36-28-650000'',''2026-10-20-02-37-24-813000'',''2026-10-20-02-38-17-275000'',''2026-10-20-02-40-10-731000'',''2026-10-20-02-41-54-441000'',''2026-10-20-02-43-13-966000'',''2026-10-20-02-45-01-804000'',''2026-10-20-02-47-10-773000'',''2026-10-20-02-49-20-440000'',''2026-10-20-02-50-32-499000'',''2026-10-20-02-51-44-522000'',''2026-10-20-02-55-31-691000'',''2026-10-20-02-56-48-751000'',''2026-10-20-03-02-44-469000'',''2026-10-20-03-03-48-622000'',''2026-10-20-03-05-03-276000'',''2026-10-20-03-06-24-686000'',''2026-10-20-03-07-26-695000'',''2026-10-20-03-08-41-072000'',''2026-10-20-03-09-48-587000'',''2026-10-20-03-10-59-519000'',''2026-10-20-03-12-10-879000''}): row[''embodiment''] == ''yam_bimanual'' and row[''lab''] == ''rl2'' and row[''task''] in (''organize_stationary'', ''organize_stationary_updated'') and row[''rig_name''] == ''rl2_abc'' and row[''zarr_processed_path''] != '''' and row[''is_deleted''] == False and row[''episode_hash''] in S' + expected_train_episode_count: 417 + expected_train_episode_names_sha256: 5128bedbaf3ace41897dbd7a5a9c2adf563aa5031605c0c4af30c5417165e39a +valid_datasets: + yam_bimanual: + expected_train_episode_count: 24 + expected_train_episode_names_sha256: 8444289c363ede1f4527515bc668f42f42a586524f760d10c39a07426a4b3243 diff --git a/egomimic/hydra_configs/experiment/_shared/frozen_yam_arc.yaml b/egomimic/hydra_configs/experiment/_shared/frozen_yam_arc.yaml index b43705a3e..0a82b1613 100644 --- a/egomimic/hydra_configs/experiment/_shared/frozen_yam_arc.yaml +++ b/egomimic/hydra_configs/experiment/_shared/frozen_yam_arc.yaml @@ -89,5 +89,5 @@ evaluator: streams: ${abc.arc_streams} logger: wandb: - id: ${name}_s42 + id: ${name}_s${seed} tags: [rl2, hpt300, "${abc.arc_clock_mode}", "${abc.arc_streams}", open_loop, video] diff --git a/egomimic/hydra_configs/experiment/real/arcdur_abstime/fold_and_stack_towels.yaml b/egomimic/hydra_configs/experiment/real/arcdur_abstime/fold_and_stack_towels.yaml index a2813c9ef..c930faf05 100644 --- a/egomimic/hydra_configs/experiment/real/arcdur_abstime/fold_and_stack_towels.yaml +++ b/egomimic/hydra_configs/experiment/real/arcdur_abstime/fold_and_stack_towels.yaml @@ -10,5 +10,4 @@ abc: arc_distance: 0.33 logger: wandb: - id: ${name}_s42 tags: [rl2, fold_towels, since0924plus74, hpt300, arcdur, absolute_waypoint_time, isotonic, translation, m200_d033, open_loop] diff --git a/egomimic/hydra_configs/experiment/real/arcdur_abstime/organize_stationary.yaml b/egomimic/hydra_configs/experiment/real/arcdur_abstime/organize_stationary.yaml index ed7110c19..13c89f3e2 100644 --- a/egomimic/hydra_configs/experiment/real/arcdur_abstime/organize_stationary.yaml +++ b/egomimic/hydra_configs/experiment/real/arcdur_abstime/organize_stationary.yaml @@ -11,5 +11,4 @@ abc: arc_distance: 0.22 logger: wandb: - id: ${name}_s42 tags: [rl2, stationery, stattempo, sp366e51, bc, arcdur, absolute_waypoint_time, isotonic, translation, m200_d022, open_loop] diff --git a/egomimic/hydra_configs/experiment/real/arcdur_abstime/wireless_earbuds.yaml b/egomimic/hydra_configs/experiment/real/arcdur_abstime/wireless_earbuds.yaml index 0ce3e4be0..fc9023880 100644 --- a/egomimic/hydra_configs/experiment/real/arcdur_abstime/wireless_earbuds.yaml +++ b/egomimic/hydra_configs/experiment/real/arcdur_abstime/wireless_earbuds.yaml @@ -11,5 +11,4 @@ abc: arc_distance: 0.22 logger: wandb: - id: ${name}_s42 tags: [rl2, wireless_earbuds_updated, plus49_20261020, hpt300, arcdur, absolute_waypoint_time, isotonic, translation, m200_d022, open_loop] diff --git a/egomimic/rldb/zarr/arc_length_tokenizer.py b/egomimic/rldb/zarr/arc_length_tokenizer.py index 70f3c486d..19f1d366b 100644 --- a/egomimic/rldb/zarr/arc_length_tokenizer.py +++ b/egomimic/rldb/zarr/arc_length_tokenizer.py @@ -1138,8 +1138,9 @@ def resolve_arc_chunking_mode(mode=None, rotation_distance_unit=None): SIX_CLOCK_COLUMNS = (0, 7, 3, 10, 6, 13) CLOCK_COLUMNS = (0, 7, 3, 10) SINGLE_CLOCK_COLUMNS = (0, 7) -# (min, max) speed bounds of an absolute translation clock, m/s. -TRANSLATION_SPEED_BOUNDS = (0.01, 5.0) +# Max speed of an absolute translation clock, m/s. There is no min speed: a slow arm +# also times the rotation and gripper riding its clock, and a floor replays them fast. +MAX_SPEED = 5.0 def validate_clock_mode(mode): @@ -1174,7 +1175,7 @@ def stream_distances(waypoints, off, stream): return np.abs(np.diff(waypoints[:, off + 6])) -def absolute_times_to_seconds(times, distances, min_speed=None, max_speed=None): +def absolute_times_to_seconds(times, distances, max_speed=None): """Absolute waypoint seconds -> start delay and incoming interval seconds. Elmo's speed-bounded isotonic decode (PR #266): project the predicted times @@ -1182,9 +1183,8 @@ def absolute_times_to_seconds(times, distances, min_speed=None, max_speed=None): last at least distance / max_speed, by isotonic regression on the slack above that fastest clock. A backwards pair is averaged instead of drifting later rows late, and no local error sums into later rows. Row zero is the start - delay and stays >= 0. min_speed caps the moving duration at total distance / - min_speed by clipping only the rows past it. Without motion, or without - bounds, this is plain isotonic regression. + delay and stays >= 0. Without motion, or without a bound, this is plain + isotonic regression. """ t = np.asarray(times, dtype=np.float64) cum = np.r_[0.0, np.cumsum(np.asarray(distances, dtype=np.float64))] @@ -1193,8 +1193,6 @@ def absolute_times_to_seconds(times, distances, min_speed=None, max_speed=None): moving = cum[-1] > 0.0 fastest = cum / max_speed if moving and max_speed else np.zeros_like(cum) slack = np.maximum(isotonic_regression(t - fastest).x, 0.0) - if moving and min_speed: - slack = np.minimum(slack, slack[0] + cum[-1] / min_speed - fastest[-1]) clock = fastest + slack return np.r_[clock[0], np.diff(clock)] @@ -1204,7 +1202,7 @@ def clock_token_seconds(token, clock_mode, streams=None): Velocity row zero is already seconds. Duration rows are absolute seconds from the chunk anchor, decoded once by :func:`absolute_times_to_seconds` with the - translation speed bounds; rotation and gripper clocks are only made monotone. + translation max speed; rotation and gripper clocks are only made monotone. """ validate_clock_mode(clock_mode) value = np.array(token, dtype=np.float64) @@ -1218,7 +1216,6 @@ def clock_token_seconds(token, clock_mode, streams=None): ) if STREAM_DIMS[validate_streams(streams)] != value.shape[-1]: raise ValueError(f"{streams} tokens have {STREAM_DIMS[streams]} columns") - bounds = TRANSLATION_SPEED_BOUNDS rows = value.reshape(-1, *value.shape[-2:]) for row in rows: for index, name in enumerate(STREAM_NAMES[streams]): @@ -1227,7 +1224,7 @@ def clock_token_seconds(token, clock_mode, streams=None): row[:, col] = absolute_times_to_seconds( row[:, col], stream_distances(row, off, name), - *(bounds if name == "translation" else (None, None)), + MAX_SPEED if name == "translation" else None, ) value = rows.reshape(value.shape) return value diff --git a/egomimic/robot/arc_decoder.py b/egomimic/robot/arc_decoder.py index a3123257f..74be28c3f 100644 --- a/egomimic/robot/arc_decoder.py +++ b/egomimic/robot/arc_decoder.py @@ -300,7 +300,7 @@ def _decode(self, row): (decoded, np.repeat(decoded[-1:], self.action_horizon - steps, axis=0)) ) self.last_stats = { - "replan_steps": steps, + "replan_steps": steps if self.first_stream else None, # 0: replan_every "horizon": self.action_horizon, "execute_waypoints": len(token), } diff --git a/tests/test_arc_clock_contract.py b/tests/test_arc_clock_contract.py index 672f05e2d..b94c429d7 100644 --- a/tests/test_arc_clock_contract.py +++ b/tests/test_arc_clock_contract.py @@ -4,6 +4,7 @@ import pytest from egomimic.rldb.zarr.arc_length_tokenizer import ( + MAX_SPEED, TokenizeBimanualArcLengthCartesian, absolute_times_to_seconds, bimanual_arc_token_shape, @@ -30,12 +31,12 @@ def codec(clock="velocity", streams="full_separation", **kwargs): return TokenizeBimanualArcLengthCartesian(**options) -def trajectory(): +def trajectory(xyz_step=0.01): frames = np.arange(30) raw = np.zeros((30, 14)) for col, delay, step in ( - (0, 3, 0.01), - (7, 5, 0.01), + (0, 3, xyz_step), + (7, 5, xyz_step), (3, 4, 0.1), (10, 2, 0.1), (6, 6, 0.1), @@ -80,19 +81,17 @@ def test_one_clock_per_arm_carries_rotation_and_gripper_on_translation(clock): def test_absolute_duration_is_elmos_speed_bounded_isotonic_projection(): # A backwards pair keeps enough elapsed time to traverse 10 cm at 5 m/s. - seconds = absolute_times_to_seconds([0, 0.1, 0.35, 0.3, 0.4], [0.1] * 4, 0.01, 5.0) + seconds = absolute_times_to_seconds([0, 0.1, 0.35, 0.3, 0.4], [0.1] * 4, 5.0) np.testing.assert_allclose(np.cumsum(seconds), [0, 0.1, 0.315, 0.335, 0.4]) # Without bounds it is plain isotonic regression; the start delay stays >= 0. seconds = absolute_times_to_seconds([-0.2, 0.3, 0.1, 0.6], [0.1] * 3) np.testing.assert_allclose(np.cumsum(seconds), [0, 0.2, 0.2, 0.6]) - # The min_speed bound clips only the rows past total distance / min_speed. - seconds = absolute_times_to_seconds([0, 0.02, 100, 999], [0.1, 0.1, 0], 0.01, 5.0) - np.testing.assert_allclose(np.cumsum(seconds), [0, 0.02, 20, 20]) @pytest.mark.parametrize("streams", ALL_STREAMS) -def test_velocity_and_duration_reconstruct_same_moving_intervals(streams): - raw = trajectory() +@pytest.mark.parametrize("xyz_step", [0.01, 0.0005]) # 0.1 and 0.005 m/s (slow pace) +def test_velocity_and_duration_reconstruct_same_moving_intervals(streams, xyz_step): + raw = trajectory(xyz_step) a, b = codec("velocity", streams), codec("duration", streams) va = a.transform({"actions": raw})["actions"] vb = b.transform({"actions": raw})["actions"] @@ -324,3 +323,46 @@ def normalizer(): normalizer().infer_norm_from_dataset([], **options) else: normalizer().infer_norm_from_dataset([], **options) + + +def test_rows_past_the_executed_prefix_never_change_robot_motion(): + """Eval and robot decode only the cropped prefix's clocks, agree on a noisy + predicted token, and never move an arm faster than MAX_SPEED per tick.""" + from egomimic.eval.open_loop_sim import OpenLoopSimEval + from egomimic.robot.arc_decoder import FirstStreamArcDecoder + + dt = 1 / 30 + shared = dict( + clock_mode="duration", + streams="translation", + min_distance_unit=0.22, + rotation_distance_unit=0.3, + resampled_vector_length=200, + gripper_stream_mode="events", + ) + raw = np.zeros((100, 14)) + raw[:, 0] = np.linspace(0, 0.12, 100) + raw[:, 8] = np.linspace(0, 0.08, 100) ** 1.5 + raw[:, 3] = np.linspace(0, 0.4, 100) + c = TokenizeBimanualArcLengthCartesian( + action_key="actions", output_action_key="actions", dt=dt, **shared + ) + token = c.transform({"actions": raw})["actions"] + token[:, 14:16] += np.random.default_rng(0).normal(scale=0.05, size=(200, 2)) + assert (np.diff(token[:, 14:16], axis=0) < 0).any() and (token[:, 14:16] < 0).any() + dec = FirstStreamArcDecoder(**shared, dt=dt, execute_percent=25, action_horizon=100) + ev = OpenLoopSimEval( + **shared, control_dt=dt, action_mode="arc", execute_fraction=0.25 + ) + base = dec(token)[0] + steps = dec.execution_steps() + e_out, e_steps = ev._decode_prediction_with_steps(token, max_steps=100) + assert e_steps == steps + np.testing.assert_allclose(base[:steps], e_out, atol=1e-9) + later = token.copy() + later[50:, 14:16] = -5.0 # every unexecuted waypoint "earlier" than the prefix + np.testing.assert_array_equal(dec(later)[0], base) + assert dec.execution_steps() == steps + for off in (0, 7): + step = np.linalg.norm(np.diff(base[:steps, off : off + 3], axis=0), axis=1) + assert step.max(initial=0) <= MAX_SPEED * dt + 1e-9 diff --git a/tests/test_arc_first_delay.py b/tests/test_arc_first_delay.py index 0feba6425..9e22bcbf1 100644 --- a/tests/test_arc_first_delay.py +++ b/tests/test_arc_first_delay.py @@ -219,6 +219,13 @@ def test_validation_and_robot_use_identical_prefix_clocks(layout, fraction): ) decoder.first_stream = 0 np.testing.assert_allclose(decoder(token)[0], codec.detokenize(token, 20)) + # Mode 0 defers the replan to the policy's fixed replan_every. + from egomimic.pipeline.action_adapter import execution_prefix + + assert decoder.execution_steps() is None + assert ( + len(execution_prefix(decoder(token)[0], decoder=decoder, replan_every=5)) == 5 + ) @pytest.mark.parametrize("layout", FIRST_DELAY_LAYOUTS) diff --git a/tests/test_arcdur_abstime_recipes.py b/tests/test_arcdur_abstime_recipes.py index e8a2ab353..6ca6e1c71 100644 --- a/tests/test_arcdur_abstime_recipes.py +++ b/tests/test_arcdur_abstime_recipes.py @@ -35,6 +35,7 @@ def test_protocol_values_reach_data_evaluator_and_rollout(task, distance): assert (abc.arc_token_dim, abc.arc_waypoints) == (16, 200) assert abc.arc_distance == distance assert abc.arc_source_buffer_frames == 100 + assert cfg.logger.wandb.id == f"{cfg.name}_s{cfg.seed}" assert ( cfg.run_provenance.action_contract.timing_layout == "first_delay_absolute_v1" @@ -95,6 +96,8 @@ def test_stationery_pool_is_slowpace366_plus_elmo51_with_frozen_validation(): with compose_for_audit(CONFIGS / f"{FOLDER}/organize_stationary.yaml") as cfg: filters = cfg.data.train_datasets.yam_bimanual.filters assert len(_hashes(filters.filter_lambdas[0])) == 417 + assert cfg.data.train_datasets.yam_bimanual.expected_train_episode_count == 417 + assert cfg.data.valid_datasets.yam_bimanual.expected_train_episode_count == 24 val = _hashes(cfg.data.valid_datasets.yam_bimanual.filters.filter_lambdas[0]) assert len(val) == 24 assert val == _hashes(old.valid_datasets.yam_bimanual.filters.filter_lambdas[0]) diff --git a/tests/test_time_retimer_controls.py b/tests/test_time_retimer_controls.py new file mode 100644 index 000000000..12d847ec4 --- /dev/null +++ b/tests/test_time_retimer_controls.py @@ -0,0 +1,35 @@ +"""The baseline (time) recipe's deployed decoder and its dashboard speed controls.""" + +import numpy as np +from hydra.utils import instantiate + +from egomimic.pipeline.inference_config import build_inference_config +from scripts.audit_hydra_configs import CONFIGS, compose_for_audit + + +def test_time_recipe_speed_controls_bind_to_the_deployed_retimer_and_act(): + with compose_for_audit( + CONFIGS / "experiment/real/wireless_earbuds/baseline.yaml" + ) as cfg: + profile = build_inference_config(cfg)["inference_graph"]["profiles"]["default"] + decoder = instantiate(profile["adapter"]["decoder"]) + assert type(decoder).__name__ == "TimeChunkRetimer" + targets = { + name: spec["target"]["attribute_path"] + for name, spec in profile["overrides"].items() + if spec["target"]["kind"] == "decoder_attribute" + } + assert set(targets) == {"arc_speed_percent", "arc_hold_speed_percent"} + for name, path in targets.items(): + assert hasattr(decoder, path), (name, path) + chunk = np.zeros((1, 100, 14)) + chunk[0, :, 0] = np.linspace(0, 0.3, 100) # 9 cm/s, well above hold_threshold + np.testing.assert_array_equal(decoder(chunk), chunk) # 100 % is byte-identical + setattr(decoder, targets["arc_speed_percent"], 200) + setattr(decoder, targets["arc_hold_speed_percent"], 200) + out = decoder(chunk)[0] + np.testing.assert_allclose(out[:50, 0], chunk[0, ::2, 0], atol=1e-9) + np.testing.assert_allclose( + out[50:, 0], 0.3 + ) # endpoint held, never extrapolated + assert decoder.last_stats["valid_steps"] == 50