Potestates commited on
Commit
804794f
Β·
verified Β·
1 Parent(s): 7dfad3c

README: TERRA-vs-control inventory, and the 4 LIBERO cells

Browse files
Files changed (1) hide show
  1. README.md +200 -38
README.md CHANGED
@@ -1,48 +1,210 @@
1
- # TERRA β€” model checkpoints
 
 
 
 
 
 
 
2
 
3
- OpenVLA-7B + TERRA latent-action phase 2 (`latp2_inv` labels), post-trained with a flow-matching action head and
4
- 4 query tokens. **One directory = one model**, each with its own README (what it is, how to deploy it, what has been
5
- measured). Every deployable directory holds merged weights (`model-*.safetensors`), the action head
6
- (`action_decoder.pt`, required), `dataset_statistics.json` (un-normalisation), `ctrl_meta.json` and the processor.
7
 
8
- **Code:** https://github.com/DingTianxingjian/Latent_Free β€” branch `newton` (deployment wrapper: `acdc_la/realrobot/piper_policy.py`).
 
 
 
 
 
 
9
 
10
  ```bash
11
  git clone -b newton https://github.com/DingTianxingjian/Latent_Free.git latent_free && cd latent_free
12
  huggingface-cli download Potestates/TERRA --include "<dir>/*" --local-dir .
13
  ```
14
 
15
- ## Which model? (εΏ«ι€Ÿι€‰ζ‹©)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
- | dir | role | task | action (dims 0-5) | how to run it on the Piper | status |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  |---|---|---|---|---|---|
19
- | `press_button_terra_delta` | **policy** | press the {blue, orange, pink, red} button | per-step **delta** `target βˆ’ q` | `to_joint_targets(a, q)`, re-read `q` EVERY tick, **chunk = 1** | **pressed the button on the robot** (chunk=1; slightly off-centre). chunk β‰₯ 2 bends the direction β€” don't |
20
- | `press_button_anchored_fwd_step1500` | **policy, newest fix** | press the {blue, orange, pink, red} button | **chunk-anchored delta**, fixed training window | read `q` ONCE per chunk, `chunk_targets(chunk, q)`, N = 1 / 2 / 4 | early snapshot (1500/3000) of the window-fixed run; best offline at N = 4 (2.28 deg); not yet on the robot |
21
- | `piper_multi_anchored_step5500` | **policy, one model for both tasks** | press button (4 colours) + pick up the can and place it on the plate | **chunk-anchored delta** `target[s+k] βˆ’ q[s]` | read `q` ONCE per chunk, `chunk_targets(chunk, q)`, execute N = 1 / 2 / 4 | experimental, latest snapshot (step 5500); not yet run on the robot |
22
- | `piper_multi_anchored_step2500` | same run, earlier snapshot | same | same | same | superseded by step 5500; kept for comparison |
23
- | `piper_pick_can_10k` | policy | `pick up the can and place it on the plate` | **absolute** joint target | send `chunk[k]` directly | offline only (3.24 deg) |
24
- | `press_button_openvla_delta` | **control (baseline)** | press_button | per-step delta | as `press_button_terra_delta` | official OpenVLA-7B, same recipe: 1.92 vs 0.92 deg, **ignores the colour word** β€” for the comparison, not for use |
25
- | `libero_10_full` | policy (sim) | LIBERO-10 | LIBERO EEF delta | `run_libero_eval_cont` | 89.0 % (500 episodes) |
26
- | `libero_goal_full` | policy (sim) | LIBERO-Goal | LIBERO EEF delta | `run_libero_eval_cont` | 94.4 % (500 episodes) |
27
- | `p2fft_20k` | **pretrained base**, not a policy | β€” | β€” | `INIT=./p2fft_20k` for post-training | phase 2, 20k steps on OXE |
28
-
29
- ### The three action spaces on the Piper β€” getting this wrong drives the arm somewhere else
30
-
31
- `ctrl_meta.json β†’ action_space` says which one a directory uses, and `PiperPolicy` reads it.
32
-
33
- * **absolute** (no `action_space` key): `chunk[k]` IS the joint target.
34
- * **delta** (`"delta"`): `chunk[k]` = target βˆ’ measured joints *at step k*. Send `q_now + chunk[k]`, `q` re-read every
35
- tick. Use **chunk = 1**: the demos were teleoperated at 30 Hz with a continuous command, so the demonstrated arm
36
- runs ~0.86–2 deg/step *ahead* of where an arm that settles at each 10 Hz command would be; with chunk = N that lead
37
- adds up linearly and with OPPOSITE signs on j1/j2 (βˆ’0.74 / +0.75 deg per step on pick-can) β€” the direction bends.
38
- * **delta_anchored** (`"delta_anchored"`): `chunk[k]` = target[s+k] βˆ’ joints read ONCE at the chunk's start. Send
39
- `q_s + chunk[k]`; the targets do not depend on how the arm tracks inside the chunk, so the tracking drift above
40
- does not exist β€” what remains is the model's own error, which grows with k (see the directory's README).
41
-
42
- The method ledger is `LEDGER.md` in the code repo. `HANDOFF_press_button.md` at the root of this repo is the brief
43
- for training a new real-robot task.
44
-
45
- ## `p2fft_20k` β€” the phase-2 pretrained base
46
-
47
- See `p2fft_20k/README.md`. Post-training from it: `INIT=./p2fft_20k ... bash acdc_la/scripts/run_vla_post2_portable.sh`;
48
- the matched control is `INIT=none` (official `openvla/openvla-7b`, everything else identical).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ pipeline_tag: robotics
3
+ tags:
4
+ - openvla
5
+ - vision-language-action
6
+ - robotics
7
+ - libero
8
+ ---
9
 
10
+ # TERRA β€” post-trained policies (real robot + LIBERO)
 
 
 
11
 
12
+ OpenVLA-7B with TERRA latent-action phase 2 (`latp2_inv` labels), post-trained with a flow-matching action head and
13
+ 4 query tokens. **One directory per policy**, each self-contained: merged weights (`model-*.safetensors`), the
14
+ action/latent head (`action_decoder.pt` β€” **required**, it is not inside the merged VLA weights),
15
+ `dataset_statistics.json` (action un-normalisation), `ctrl_meta.json`, processor/tokenizer, and its own `README.md`
16
+ with the measured numbers and the exact deployment loop.
17
+
18
+ **Code:** https://github.com/DingTianxingjian/Latent_Free β€” branch `newton`, public, no token needed.
19
 
20
  ```bash
21
  git clone -b newton https://github.com/DingTianxingjian/Latent_Free.git latent_free && cd latent_free
22
  huggingface-cli download Potestates/TERRA --include "<dir>/*" --local-dir .
23
  ```
24
 
25
+ ---
26
+
27
+ ## Which one do I test?
28
+
29
+ | I want to… | use | why |
30
+ |---|---|---|
31
+ | **press a coloured button** | **`press_button_terra_delta`** | the only policy **confirmed working on hardware** β€” it presses the requested button |
32
+ | **pick the can, place on plate** | **`piper_pick_can_openvla_delta`** | **3x better open loop** than the arm currently on the robot (1.05 vs 3.24 deg); simplest of the equals |
33
+ | the same, with our full method | `piper_pick_can_terra_delta` | statistically **indistinguishable** from the row above (1.13 vs 1.05, CI contains 0) β€” either is fine |
34
+ | **one model for both tasks** | `piper_multi_anchored_step5500` | **experimental**, and a retrain will supersede it β€” see its README |
35
+ | **compare the latent mechanism** in sim | the `libero_goal_frac10_*` **triplet** | ours / UniVLA's / **no latent at all**, identical otherwise |
36
+ | post-train a new task | `p2fft_20k` | the **base**, not a policy |
37
+
38
+ > ### ⚠ Two things that will break a deployment if you skip them
39
+ > 1. **Action space differs per directory.** `piper_pick_can_10k` emits **absolute** joint targets; everything with
40
+ > `delta` in the name emits **target minus measured joint angle** and must be sent as `q + a[:6]` with `q` re-read
41
+ > **every tick**; the `anchored` ones use `q` read **once per chunk** and a different call. Every `ctrl_meta.json`
42
+ > now records `"action_space"` explicitly and `PiperPolicy` prints it on load β€” **check the printed line.**
43
+ > 2. **Execute 1 chunk entry, then re-predict** (all per-step `delta` policies). The demos were teleoperated as a
44
+ > continuous 30 Hz command, so later chunk entries are mis-anchored for an arm that settles: drift is
45
+ > **0.855 deg/step on pick-can** and **~2 deg/step on press_button**, and it is opposite-signed on the two joints
46
+ > that carry the motion, so it **bends the direction** instead of just scaling it. This was seen on the robot:
47
+ > chunk 1 presses the button, chunk 2 "the direction is a bit off". Use an `anchored` checkpoint if you need
48
+ > multi-step chunks.
49
+
50
+ ---
51
+
52
+ ## Inventory β€” which directories are **ours (TERRA)** and which are **controls / baselines**
53
+
54
+ Every policy here shares the same interface (LoRA r32 + flow-matching head + 4 query tokens, chunk 12). What differs
55
+ is the **base** and the **latent channel**, which is exactly what each comparison is about.
56
+
57
+ ### TERRA β€” ours (latent channel `tc_cont`, `latp2_inv` labels)
58
+
59
+ | dir | base | task / suite | number |
60
+ |---|---|---|---|
61
+ | `p2fft_20k` | openvla-7b, full fine-tune | **phase-2 base, not a policy** | β€” |
62
+ | `press_button_terra_delta` | `p2fft_20k` | press_button, per-step delta | 0.918Β° βœ… on hardware |
63
+ | `press_button_anchored_fwd_step1500` | `p2fft_20k` | press_button, anchored, fixed window | 0.96Β° at k=0, flat in k |
64
+ | `piper_pick_can_terra_delta` | `p2fft_20k` | pick-can, per-step delta | 1.126Β° |
65
+ | `piper_pick_can_10k` | `p2fft_20k` | pick-can, **absolute** β€” superseded | 3.24Β° |
66
+ | `piper_multi_anchored_step2500` / `step5500` | `p2fft_20k` | both tasks, anchored β€” experimental | see card |
67
+ | `libero_goal_full` | LoRA-scale phase 2 | LIBERO-Goal, full split | 94.4 % |
68
+ | `libero_10_full` | LoRA-scale phase 2 | LIBERO-Long, full split | 89.0 % |
69
+ | `libero_10_frac10_terra_p2fft20k` | `p2fft_20k` | LIBERO-Long, 10 % (43 demos) | 60.6 % |
70
+ | `libero_goal_frac10_terra` | **openvla-7b, no phase 2** | LIBERO-Goal, 10 % | 86.8 % |
71
+
72
+ ### Baselines / controls β€” no latent, or somebody else's latent
73
+
74
+ | dir | what it controls for | number |
75
+ |---|---|---|
76
+ | `press_button_openvla_delta` | stock openvla-7b, **`--latent none`** β€” the whole package vs nothing | 1.920Β° (worse than not moving) |
77
+ | `piper_pick_can_openvla_delta` | the same, on pick-can | **1.054Β°** β€” here it is a good policy |
78
+ | `libero_goal_frac10_none` | **no latent at all**, identical otherwise (`latent_r2` ≑ 0) | 88.6 % |
79
+ | `libero_goal_frac10_univla_cont` | **UniVLA's** deployed latent through our interface | 89.4 % |
80
+
81
+ > **One honest caveat about the two `*_openvla_delta` controls.** They change the base **and** the latent branch at
82
+ > once (`INIT=none LATENT=none`), so where they separate, the effect belongs to *the whole package*, not specifically
83
+ > to the latent branch. The missing arm that would split those two β€” phase-2 base with `--latent none` β€” has not been
84
+ > run. The `libero_goal_frac10_*` triplet *is* clean in that sense (one factor, no phase 2 anywhere), and there the
85
+ > answer is that the latent channel changes nothing.
86
+ >
87
+ > **Which base a policy came from is not in most of these files.** `ctrl_meta.json` records the latent, the head
88
+ > and the dataset, but the training script never wrote the phase-2 init. The four directories published on
89
+ > 2026-11-12 (`libero_goal_frac10_*`, `libero_10_frac10_terra_p2fft20k`) now carry an explicit `"init"` field;
90
+ > for the others the base is stated in the table above and in each card, and nowhere in the weights.
91
 
92
+ ---
93
+
94
+ ## Real-robot policies β€” AgileX Piper
95
+
96
+ Open-loop error = mean |error| per joint in **degrees** on held-out episodes never trained on, chunk sampled from
97
+ noise through the deployment image path. Reproduce any row with
98
+ `envs/univla_vla/bin/python -m acdc_la.realrobot.piper_policy --checkpoint <dir> --replay <rlds root>` β€” it is
99
+ deterministic, and reproducing it is the check that your preprocessing matches ours.
100
+
101
+ ### `press_button` β€” "press the {blue, orange, pink, red} button", 100 episodes, layout reshuffled per episode
102
+
103
+ | dir | base | action | `chunk[0]` | reads the instruction? | on the robot |
104
+ |---|---|---|---|---|---|
105
+ | **`press_button_terra_delta`** | our phase 2 | delta | **0.918** | **yes** β€” output moves 0.42 deg on a colour swap; true word wins 4/5 episodes | βœ… **presses the button** (chunk 1; slightly off centre) |
106
+ | `press_button_openvla_delta` | stock openvla-7b | delta | 1.920 | **no** β€” output moves **2.4e-7 deg**; true word wins 1/5 = chance | ❌ not for deployment |
107
+ | *null: "stay where you are"* | | | *1.691* | | |
108
+
109
+ The control is *worse than not moving*, and its invariance to language is **exact, not approximate**: its instruction
110
+ signal is 0.28% of the query-token state, which is below bfloat16 machine epsilon (7.8e-3), so it cannot survive its
111
+ own forward pass. With 4 colours and per-episode layouts a policy that cannot read the colour can only learn an
112
+ average press β€” which is what its j1/j2 error looks like. Caveats: **5 held-out episodes, 3 of 4 colours, one seed
113
+ per arm, offline**; and the pair changes *both* the base and the latent branch, so the effect cannot be attributed to
114
+ the latent branch alone.
115
+
116
+ ### `piper_pick_can` β€” "pick up the can and place it on the plate", 150 episodes, **one** instruction throughout
117
+
118
+ | dir | base | action | `chunk[0]` | ensembled | note |
119
  |---|---|---|---|---|---|
120
+ | **`piper_pick_can_openvla_delta`** | stock openvla-7b | delta | **1.054** | 0.914 | the matched control β€” and here a perfectly good policy |
121
+ | **`piper_pick_can_terra_delta`** | our phase 2 | delta | **1.126** | 0.950 | ours; **not separated** from the row above |
122
+ | `piper_pick_can_10k` | our phase 2 | **absolute** | 3.24 | 3.69 | the arm tested on the robot first; **superseded** by the two above |
123
+ | *null: "stay where you are"* | | | *1.20* | | |
124
+ | *null: repeat the last command* | | | *2.06* | | |
125
+ | *between-demonstration floor* | | | *0.61* | | **nothing can beat this** |
126
+
127
+ **Delta vs absolute is the real win here: 3.24 β†’ ~1.05 deg, 3x**, and it moves the policy from 2.7x *worse* than
128
+ doing nothing to better than it. **TERRA vs control is not a win for either side**: paired cluster bootstrap over the
129
+ 8 held-out episodes (the independent unit β€” 353 steps is not the sample size) gives a difference of +0.072 deg,
130
+ 95% CI **[-0.038, +0.189]**, sign test p = 0.727.
131
+
132
+ A null here is what the mechanism's own story predicts: **all 150 episodes carry the identical instruction**, so
133
+ there is nothing for a language-grounding advantage to do. The same pair separates 2.1x on `press_button`.
134
+
135
+ ### `piper_multi_anchored_step2500`, `piper_multi_anchored_step5500` β€” ⚠ experimental
136
+
137
+ One model for **both** tasks (press_button x2 + pick-can mixture), using **chunk-ANCHORED** deltas
138
+ (`target[s+k] - q[s]`, `q` read once per chunk) so that executing several entries open loop is valid. Better than the
139
+ per-step parameterisation from k>=2, slightly worse at k=0. Trained with the old backward sample window (magnitudes
140
+ under-predicted ~0.8x). **`press_button_anchored_fwd_step1500` is the first checkpoint with the window fixed**
141
+ (press-button only, snapshot of a run still training): its error stops growing with chunk position β€” it levels
142
+ off near 3.5Β° instead of climbing to 13Β° β€” and it will supersede these for that task. Different deployment call
143
+ (`chunk_targets`, no ensembling) β€” read their READMEs.
144
+
145
+ ---
146
+
147
+ ## LIBERO policies
148
+
149
+ Standard eval, 50 trials x 10 tasks = **500 episodes**, continuous flow head:
150
+
151
+ ```bash
152
+ python -m acdc_la.downstream.vla.run_libero_eval_cont --pretrained_checkpoint <dir>
153
+ ```
154
+
155
+ | dir | suite | demos | base | latent | success |
156
+ |---|---|---|---|---|---|
157
+ | `libero_goal_full` | Goal | full | LoRA-scale phase 2 | ours | **94.4 %** |
158
+ | `libero_10_full` | Long | full | LoRA-scale phase 2 | ours | **89.0 %** |
159
+ | `libero_10_frac10_terra_p2fft20k` | Long | 10 % (43) | **`p2fft_20k`** | ours | **60.6 %** |
160
+ | `libero_goal_frac10_univla_cont` | Goal | 10 % (43) | openvla-7b | **UniVLA's** | 89.4 % |
161
+ | `libero_goal_frac10_none` | Goal | 10 % (43) | openvla-7b | **none** | 88.6 % |
162
+ | `libero_goal_frac10_terra` | Goal | 10 % (43) | openvla-7b | ours | 86.8 % |
163
+
164
+ ### The one result in this repo that is a clean, single-factor comparison β€” and it is negative
165
+
166
+ The three `libero_goal_frac10_*` rows differ **only** in the latent channel. The **no-latent control lands between
167
+ the two latent arms** (88.6 % against 89.4 % and 86.8 %), so at 10 % Goal demos through this interface the latent
168
+ channel is **decorative**: the action head reads what it needs from the visual features alone. 13 episodes in 500 is
169
+ ~1.3Οƒ, one seed each, and a single 500-episode cell carries **Β±8 points at 95 %** once training randomness is
170
+ counted β€” so these three are one band, not a ranking.
171
+
172
+ What the triplet *does* settle: an earlier 9.2 % collapse on Goal with the same latent and no query tokens was an
173
+ **interface** failure, not the latent. What it does not cover: regimes where the visual features are insufficient.
174
+ LIBERO-Long at 10 % is that regime (nothing saturates there: 60.6 % for the best arm) but with one replicate per arm
175
+ it still cannot separate 60.6 / 58.7 / 56.4. The place where a matched control *does* separate is the real robot:
176
+ `press_button_openvla_delta` is 2.1x worse than ours and cannot read the instruction at all.
177
+
178
+ Not published here: the full-split 20k-phase-2 Long arm (89.8 %, our best Long number) and the remaining 10 % Long
179
+ replicates live on a cluster filesystem, not on this host.
180
+
181
+ ---
182
+
183
+ ## `p2fft_20k` β€” the phase-2 base (not a deployable policy)
184
+
185
+ The **base** every TERRA policy above is post-trained from: OpenVLA-7B fully fine-tuned for 20,000 steps on OXE to
186
+ predict our continuous latent action (`latp2_inv`). `vla_full.pt` (15,081,838,026 B) is the VLA state dict, and
187
+ **`action_decoder.pt` (105,268,830 B) is required too** β€” `finetune_libero_ctrl.py:273` initialises the post-training
188
+ heads from it, so a run without it starts from a random head and will not reproduce these policies.
189
+
190
+ ```bash
191
+ huggingface-cli download Potestates/TERRA --include "p2fft_20k/*" --local-dir .
192
+ INIT=./p2fft_20k ... bash acdc_la/scripts/run_vla_post2_portable.sh
193
+ ```
194
+
195
+ The matched control is `INIT=none LATENT=none`, which post-trains the official `openvla/openvla-7b` with everything
196
+ else identical β€” that is how "what did phase 2 buy" is measured, and it is what the two `*_openvla_delta`
197
+ directories are.
198
+
199
+ ---
200
+
201
+ ## One metric trap, worth repeating
202
+
203
+ `action_l1_1step` in every `train_log.jsonl` is **oracle-conditioned**: `FlowHeads.loss` scores a one-step
204
+ correction from a point that already contains the ground truth. On the pick-can pair it ranks the two arms
205
+ **backwards** (TERRA 0.0563 vs control 0.0648, i.e. TERRA "13% better", while both free-running read-outs favour the
206
+ control). Use it to track a single run or pick a checkpoint within one run β€” **never to compare two arms.** Rank with
207
+ `--replay`.
208
+
209
+ `HANDOFF_press_button.md` at the root of this repo is the brief for training the next real-robot task on a fresh
210
+ host. The full experiment ledger, including everything retracted or superseded, is `LEDGER.md` in the code repo.