AlexWortega commited on
Commit
5a2e445
·
verified ·
1 Parent(s): 4ed2ea0

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +12 -0
  2. tinyvla2/CLAUDE.md +153 -0
  3. tinyvla2/REPORT.md +171 -0
  4. tinyvla2/assets/so_arm/assets/base_motor_holder_so101_v1.stl +3 -0
  5. tinyvla2/assets/so_arm/assets/base_so101_v2.stl +3 -0
  6. tinyvla2/assets/so_arm/assets/motor_holder_so101_base_v1.stl +3 -0
  7. tinyvla2/assets/so_arm/assets/motor_holder_so101_wrist_v1.stl +3 -0
  8. tinyvla2/assets/so_arm/assets/moving_jaw_so101_v1.stl +3 -0
  9. tinyvla2/assets/so_arm/assets/rotation_pitch_so101_v1.stl +3 -0
  10. tinyvla2/assets/so_arm/assets/sts3215_03a_no_horn_v1.stl +3 -0
  11. tinyvla2/assets/so_arm/assets/sts3215_03a_v1.stl +3 -0
  12. tinyvla2/assets/so_arm/assets/under_arm_so101_v1.stl +3 -0
  13. tinyvla2/assets/so_arm/assets/upper_arm_so101_v1.stl +3 -0
  14. tinyvla2/assets/so_arm/assets/waveshare_mounting_plate_so101_v2.stl +0 -0
  15. tinyvla2/assets/so_arm/assets/wrist_roll_follower_so101_v1.stl +3 -0
  16. tinyvla2/assets/so_arm/assets/wrist_roll_pitch_so101_v2.stl +3 -0
  17. tinyvla2/assets/so_arm/so101_new_calib.urdf +453 -0
  18. tinyvla2/assets/so_arm/so101_nomesh.urdf +453 -0
  19. tinyvla2/configs/ablation_nolatent.yaml +25 -0
  20. tinyvla2/configs/ablation_nospatial.yaml +25 -0
  21. tinyvla2/configs/libero_ft.yaml +31 -0
  22. tinyvla2/configs/libero_ft2.yaml +33 -0
  23. tinyvla2/configs/libero_ft3.yaml +33 -0
  24. tinyvla2/configs/libero_ft4.yaml +33 -0
  25. tinyvla2/configs/morphology/descriptors.yaml +277 -0
  26. tinyvla2/configs/morphology/robot_prompts.yaml +13 -0
  27. tinyvla2/configs/stage2_mixture.yaml +37 -0
  28. tinyvla2/configs/stage3_distill.yaml +40 -0
  29. tinyvla2/configs/tv2_A.yaml +42 -0
  30. tinyvla2/configs/tv2_B.yaml +42 -0
  31. tinyvla2/configs/tv2_C.yaml +42 -0
  32. tinyvla2/configs/tv2_C_diverse.yaml +67 -0
  33. tinyvla2/configs/tv2_C_diverse_slow.yaml +68 -0
  34. tinyvla2/configs/tv2_C_incontext.yaml +72 -0
  35. tinyvla2/configs/tv2_C_incontext_v2.yaml +73 -0
  36. tinyvla2/configs/tv2_C_incontext_v3.yaml +77 -0
  37. tinyvla2/configs/tv2_C_mega.yaml +71 -0
  38. tinyvla2/configs/tv2_C_qwen_morph.yaml +73 -0
  39. tinyvla2/configs/tv2_C_scaled.yaml +76 -0
  40. tinyvla2/configs/tv2_demoonly.yaml +89 -0
  41. tinyvla2/configs/tv2_richslow.yaml +83 -0
  42. tinyvla2/configs/tv2_robocasa.yaml +77 -0
  43. tinyvla2/configs/tv2_vlm_native.yaml +83 -0
  44. tinyvla2/scripts/__pycache__/fewshot_grid.cpython-312.pyc +0 -0
  45. tinyvla2/scripts/_eval_richslow_10k.py +141 -0
  46. tinyvla2/scripts/audit_so101_fk.py +103 -0
  47. tinyvla2/scripts/build_ee_sidecar.py +169 -0
  48. tinyvla2/scripts/diag_obs_gap.py +131 -0
  49. tinyvla2/scripts/download_subsets.py +77 -0
  50. tinyvla2/scripts/eval_canonical.py +186 -0
.gitattributes CHANGED
@@ -39,3 +39,15 @@ videos/ep02_task2_succ.mp4 filter=lfs diff=lfs merge=lfs -text
39
  videos/ep03_task3_fail.mp4 filter=lfs diff=lfs merge=lfs -text
40
  videos/ep04_task4_succ.mp4 filter=lfs diff=lfs merge=lfs -text
41
  videos/ep05_task5_fail.mp4 filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  videos/ep03_task3_fail.mp4 filter=lfs diff=lfs merge=lfs -text
40
  videos/ep04_task4_succ.mp4 filter=lfs diff=lfs merge=lfs -text
41
  videos/ep05_task5_fail.mp4 filter=lfs diff=lfs merge=lfs -text
42
+ tinyvla2/assets/so_arm/assets/base_motor_holder_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
43
+ tinyvla2/assets/so_arm/assets/base_so101_v2.stl filter=lfs diff=lfs merge=lfs -text
44
+ tinyvla2/assets/so_arm/assets/motor_holder_so101_base_v1.stl filter=lfs diff=lfs merge=lfs -text
45
+ tinyvla2/assets/so_arm/assets/motor_holder_so101_wrist_v1.stl filter=lfs diff=lfs merge=lfs -text
46
+ tinyvla2/assets/so_arm/assets/moving_jaw_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
47
+ tinyvla2/assets/so_arm/assets/rotation_pitch_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
48
+ tinyvla2/assets/so_arm/assets/sts3215_03a_no_horn_v1.stl filter=lfs diff=lfs merge=lfs -text
49
+ tinyvla2/assets/so_arm/assets/sts3215_03a_v1.stl filter=lfs diff=lfs merge=lfs -text
50
+ tinyvla2/assets/so_arm/assets/under_arm_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
51
+ tinyvla2/assets/so_arm/assets/upper_arm_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
52
+ tinyvla2/assets/so_arm/assets/wrist_roll_follower_so101_v1.stl filter=lfs diff=lfs merge=lfs -text
53
+ tinyvla2/assets/so_arm/assets/wrist_roll_pitch_so101_v2.stl filter=lfs diff=lfs merge=lfs -text
tinyvla2/CLAUDE.md ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # TinyVLA — project context
2
+
3
+ Small vision-language-action models. Two lines of work:
4
+
5
+ - **v1 (shipped)** — dual-rate VLA for LIBERO: slow Qwen3.5 semantic path (cacheable,
6
+ 0.5–2 Hz) + fast flow-matching expert (10–20 Hz). **86% LIBERO-spatial, 84% 4-suite
7
+ average** — beats OpenVLA-7B on every suite at 0.64B params / 140M online.
8
+ - **v2 (research)** — cross-embodiment: canonical action space + morphology
9
+ conditioning across 10 robots. Best: `tv2_C_scaled` (ratio-to-baseline **0.81**
10
+ in-training / **0.88** held-out; was 0.99/1.17 before scaling).
11
+
12
+ Everything (checkpoints, results, wiring) lives in the **private** HF repo
13
+ `AlexWortega/tinyvla` (v1 at the root, v2 under `tinyvla2/`).
14
+
15
+ ## Environment
16
+
17
+ ```bash
18
+ source ~/tinyvla_venv/bin/activate # NOT physics_venv
19
+ ```
20
+
21
+ Pinned, non-obvious, and hard-won:
22
+ - lerobot **0.6.1**, torch **2.10.0+cu128**, transformers **5.5.4** (lerobot's
23
+ `transformers-dep` extra; older breaks `eo1`).
24
+ - **torchcodec 0.10 CPU build from PyPI** — the cu128 wheel clashes with the system
25
+ NPP libs. Video decode is the training bottleneck, use 12+ dataloader workers.
26
+ - `placo` for SO-101 forward kinematics; needs `assets/so_arm/so101_new_calib.urdf`
27
+ **plus the 13 STL meshes** (placo loads collision geometry, it won't run without them).
28
+ - `fla-core` + `causal-conv1d` give Qwen3.5 linear-attention its fast path (~5×);
29
+ they require CUDA — any CPU-only smoke test of the model will crash inside
30
+ `causal_conv1d`.
31
+ - Data lives in `~/tinyvla_data` (gitignored), HF cache in `~/.cache/huggingface`.
32
+
33
+ ## Architecture
34
+
35
+ ```
36
+ SLOW (cacheable @1Hz) FAST (10–20 Hz, ~140M online)
37
+ image → Qwen3.5-0.8B vision tower ─┬────► 64 spatial tokens/cam ──┐
38
+ instruction ──────────────────────┤ │
39
+ ▼ proprio ┤
40
+ LM (first 12 of 24 layers) morphology/ID ┤
41
+ ▼ ▼
42
+ 8–16 readout latents ────────────────► flow-matching expert
43
+ (38M, chunk 50, 10 Euler)
44
+ ```
45
+
46
+ Variant switches in `TinyVLAConfig`:
47
+ - `conditioning`: `id` (learned embedding) | `morph` (16-number descriptor → MLP) |
48
+ `morph_qwen` (robot text through the shared LM)
49
+ - `action_space`: `native` | `canonical` (base-frame EE deltas)
50
+ - `vlm_native`: demos + robot text go INTO the LM sequence (dual-rate preserved)
51
+ - `use_demo_conditioning`, `demo_only`: in-context exemplar channels (all dead, see below)
52
+
53
+ ## Data pipeline
54
+
55
+ ```
56
+ LeRobot dataset → scripts/build_ee_sidecar.py (offline, once, NO video decode)
57
+ SO-101: FK(joints) via placo; others: EE pose from state
58
+ → ~/tinyvla_data/ee_sidecar/<name>.parquet
59
+ → src/tinyvla/data/canonical.py (10 Hz resample, base-frame
60
+ left-delta rotvec, gripper q1–q99, quantile norm to [-1,1])
61
+ → CanonicalSource → WeightedMixtureDataset → policy
62
+ ```
63
+
64
+ `docs/WIRING.md` has the full embodiment registry (id ↔ morph_key ↔ dataset ↔
65
+ canonicalization source ↔ weight ↔ train/held-out).
66
+
67
+ Robot pool: SO-101 (40 community sets, FK), Bridge/WidowX 53K eps, RT-1/Google 87K eps,
68
+ UR5, xArm, DLR-EDAN, TidyBot, Stretch, Jaco, LeKiwi (held out), RoboCasa365/PandaOmron.
69
+
70
+ ## Running things
71
+
72
+ ```bash
73
+ # training (any variant — the config picks the mode)
74
+ PYTHONUNBUFFERED=1 python scripts/train.py --config configs/tv2_C_scaled.yaml
75
+
76
+ # physical-space eval across robots (the metric that matters)
77
+ python scripts/eval_similar_robots.py # ratio = endpoint err ÷ zero-floor
78
+
79
+ # LIBERO closed-loop (v1)
80
+ MUJOCO_GL=egl python scripts/eval_libero.py --checkpoint <ckpt> \
81
+ --suite libero_spatial --episodes 100 --n-action-steps 10 --action-repeat 1
82
+ ```
83
+
84
+ Long runs: launch with `nohup`, watch via checkpoints (stdout is block-buffered
85
+ under nohup — use `PYTHONUNBUFFERED=1` or you see nothing for hours).
86
+
87
+ ## Evaluation — read this before trusting any number
88
+
89
+ 1. **Always run the zero-prediction control.** Per-step action error is degenerate
90
+ (jaco moves ~3.6 mm/step; predicting zeros scores ~4 mm). Use the
91
+ **integrated-trajectory endpoint error over the 50-step chunk**, and divide by the
92
+ per-robot **zero-floor** (what "predict mean motion" achieves). Only the ratio is
93
+ comparable across robots; ratio < 1.0 means the model beats the trivial baseline.
94
+ 2. **Check both held-out robots, not one.** "canonical ≫ native" looked decisive on
95
+ jaco and reversed on stretch.
96
+ 3. Absolute mm are meaningless across robots — different motion scales.
97
+
98
+ ## Results
99
+
100
+ ### v1 — LIBERO (closed-loop success)
101
+ | suite | TinyVLA | OpenVLA-7B |
102
+ |---|---|---|
103
+ | spatial | **86%** (dual-rate @1 Hz; 79% full-rate) | 84.7% |
104
+ | object | **94%** | 88.4% |
105
+ | goal | **86%** | 79.2% |
106
+ | long | **70%** | 53.7% |
107
+ | **avg** | **84.0%** | 76.5% |
108
+
109
+ Dual-rate (semantic latent refreshed at 1 Hz) matched or beat full-rate — the core
110
+ architectural claim, verified closed-loop.
111
+
112
+ ### v2 — cross-embodiment (ratio to zero-floor, lower is better)
113
+ | | in-training | held-out |
114
+ |---|---|---|
115
+ | C-diverse (8 robots, 20k steps, frozen LM) | 0.99 | 1.17 |
116
+ | **C-scaled (9.1M frames, 60k steps, LM unfrozen)** | **0.81** | **0.88** |
117
+
118
+ Per-robot final: jaco 0.69, xarm 0.64, dlr_edan 0.69, stretch 0.69, LeKiwi (held-out)
119
+ 1.07, ur5 1.22 (hardest — the fastest robot, 9.9 mm/step; its canonicalization is fine).
120
+
121
+ **Negative results (all documented in `tinyvla2/RESULTS.md`):**
122
+ - Robot description as text in the prompt (frozen LM) — hurts.
123
+ - Morphology via the shared Qwen + unfrozen LM — 3× more content-sensitive, no
124
+ absolute gain.
125
+ - In-context demo conditioning — **five architectures**, all dead (real vs garbage
126
+ exemplars indistinguishable): MLP same-task → MLP cross-task → 24× capacity →
127
+ demos inside the LM attention stream → demos as the only identity channel.
128
+
129
+ ## Lessons
130
+
131
+ 1. **Check against the trivial baseline before running ablations.** Five conditioning
132
+ mechanisms moved nothing while the model sat at ratio 0.99 — we were comparing
133
+ variants of an underfit model and measuring noise. Data + steps + unfreezing the LM
134
+ moved everything.
135
+ 2. **Silent data-convention bugs cost the most time.** LIBERO went 0% → 60% purely by
136
+ fixing three of them: robosuite renders are **180°-rotated** (a vertical flip alone
137
+ leaves a mirror), eef rotvec is antipodal vs the dataset, and the dataset's
138
+ `fps: 10` metadata is wrong (frames are native 20 Hz). A **GT-action replay
139
+ harness** (`scripts/replay_libero.py`) diagnoses all of these independently of the
140
+ policy — build it first.
141
+ 3. **Unfreezing the LM is the single biggest lever** (LIBERO 60% → 86%).
142
+ 4. Cross-morphology transfer converges slowest — LeKiwi was still improving at 60k
143
+ (1.24 → 1.13 → 1.07) when in-training had plateaued at 40k.
144
+ 5. Some community datasets are broken at source (meta frame count ≠ parquet rows);
145
+ validate `len(reader.hf_dataset) == meta.total_frames` and quarantine mismatches.
146
+
147
+ ## State
148
+
149
+ Best checkpoints: **v1** `outputs/libero_ft4/final` (86% LIBERO) · **v2**
150
+ `outputs/tv2_C_scaled/final` (0.81/0.88). Currently training: `tv2_robocasa` —
151
+ continue-train from C-scaled on RoboCasa365 (sim kitchens, PandaOmron mobile
152
+ manipulator, 1.5M frames) at weight 0.5 with the old robots replayed at 0.5, to
153
+ separate new-domain integration from catastrophic forgetting.
tinyvla2/REPORT.md ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ base_model: Qwen/Qwen3.5-0.8B
4
+ tags:
5
+ - robotics
6
+ - vla
7
+ - lerobot
8
+ - libero
9
+ - dual-rate
10
+ pipeline_tag: robotics
11
+ ---
12
+
13
+ # TinyVLA: a dual-rate VLA that beats OpenVLA-7B on LIBERO-Spatial at 0.64B params
14
+
15
+ **Headline result: 86% success on LIBERO-Spatial (n=100) in dual-rate mode (semantic latent
16
+ refreshed at 1 Hz), 79% full-rate — vs OpenVLA-7B's 84.7% — with 140M parameters on the
17
+ online control path.** Built and trained end-to-end in ~5 days on a single RTX A6000.
18
+
19
+ ## 1. Architecture
20
+
21
+ ```
22
+ SLOW / SMART path (0.5–2 Hz, cached)
23
+ instruction ─────────────────────┐
24
+ camera → Qwen3.5-0.8B vision tower → Qwen3.5 language stack (first 12 of 24 layers)
25
+ │ │
26
+ │ 8 readout tokens → linear → semantic latent (8×512)
27
+ │ │ cached between refreshes
28
+ ──────────────────┼───────────────────────┼──────────────────────────
29
+ │ FAST / MOTOR path (10–20 Hz, ~140M online)
30
+ └── 64 spatial tokens per camera ─┐
31
+ proprio (padded 16d) ───────────────────────────────┤
32
+ embodiment-id embedding ────────────────────────────┤
33
+
34
+ flow-matching action expert (38M, 12 blocks,
35
+ interleaved cross-/self-attention, AdaLN-free)
36
+
37
+ 50-step action chunk (padded 8d + validity mask)
38
+ ```
39
+
40
+ - **Single shared encoder**: the Qwen3.5-0.8B native ViT (100.6M) feeds both paths.
41
+ - **Slow path**: image + instruction + 8 trailing pad-token "readout" slots through the
42
+ truncated language stack (L/2 layer-skip a la SmolVLA); their hidden states → latent 8×512.
43
+ - **Fast path**: raw vision-tower spatial tokens (bypassing the LM) + proprio + embodiment
44
+ token, cross-attended by the flow expert. 10 Euler steps at inference.
45
+ - **Flow matching**: linear-OT path, t ~ Beta(1.5, 1), velocity target, masked MSE.
46
+ - Totals: 643M stored / 39.5M–575M trainable depending on stage / ~140M online per tick.
47
+ - Implemented as an **out-of-tree LeRobot policy plugin**
48
+ (`--policy.type=tinyvla --policy.discover_packages_path=tinyvla`, lerobot 0.6.1).
49
+
50
+ ## 2. Training recipe
51
+
52
+ | Stage | Data | Steps | Trainable | Result |
53
+ |---|---|---|---|---|
54
+ | 1. Overfit smoke | svla_so100_stacking (56 eps) | 5k | expert+proj (40M) | loss 1.885→0.099 |
55
+ | 2. Mixture pretrain | 40 SO100/101 community datasets (~10.7K eps, weight 0.65, per-dataset proportional) + Bridge V2 10K eps (0.35) | 40k, bs 64×2, lr 1e-4 | 39.5M (backbone frozen) | held-out RMSE@t0 0.30σ; **stale-latent 2s == fresh** |
56
+ | 2b. Staleness aug | same | from step 20k | — | latent from frame t−k, k~U(0,2s), p=0.5 |
57
+ | 3. Spatial distillation | + 132K teacher labels (Qwen3.5-4B, center-point → 32×32 grid), CE ×0.15 on cam0 tokens | 20k, lr 5e-5 | same + head | head top-1 20.8%, top-5 44.8% (random 0.1%) |
58
+ | 4. LIBERO FT (ft4) | HuggingFaceVLA/libero (1693 eps, all suites) | 40k, bs 48, lr 1e-4 / backbone 0.1× | **575M** (LM layers + vision unfrozen; vocab embeddings frozen) | loss 0.066 |
59
+
60
+ Ablation FTs: ft1 (all frozen) → 0% closed-loop; ft2 (vision unfrozen) → 60%;
61
+ ft3 (= ft2 from distilled ckpt) → 61%; **ft4 (LM+vision unfrozen) → 79/86%**.
62
+ Unfreezing the LM was the single largest gain; distillation was neutral on LIBERO
63
+ (likely overwritten by vision unfreezing during FT — bake it into pretraining next time).
64
+
65
+ Cross-embodiment handling: canonical schema (cam0=scene/cam1=wrist via regex mapping,
66
+ per-dataset mean/std normalization, action pad-to-8 + validity mask, state pad-to-16,
67
+ learned embodiment-id token). Weighted mixture sampler with per-source retry on corrupt
68
+ frames (IndexError/KeyError/decode RuntimeError).
69
+
70
+ ## 3. Results
71
+
72
+ ### LIBERO — all four suites (dual-rate, latent @ 1 Hz)
73
+
74
+ | Suite | TinyVLA | OpenVLA-7B (published) |
75
+ |---|---|---|
76
+ | Spatial | **86%** (n=100; full-rate 79%) | 84.7% |
77
+ | Object | **94%** (n=50) | 88.4% |
78
+ | Goal | **86%** (n=50) | 79.2% |
79
+ | Long (libero_10) | **70%** (n=50) | 53.7% |
80
+ | **Average** | **84.0%** | 76.5% |
81
+
82
+ Beats OpenVLA-7B on every suite at 1/11 the parameters and ~1/50 the online compute.
83
+ XS-VLA (90.3 avg) remains ahead — their latent flow matching is future work for us.
84
+
85
+ ### LIBERO-Spatial (100 episodes, 10 per task, init states 0-9)
86
+
87
+ | Mode | Success |
88
+ |---|---|
89
+ | Full-rate (latent every chunk) | 79/100 = **79%** |
90
+ | **Dual-rate (latent @ 1 Hz)** | 86/100 = **86%** |
91
+
92
+ Dual-rate ≥ full-rate consistently across all evals (also 65% vs 58% at ft3, n=50) —
93
+ a stable cached latent appears to act as temporal regularization.
94
+
95
+ ### Context (published numbers, their own eval protocols)
96
+
97
+ | Model | Params | LIBERO-Spatial | 4-suite avg |
98
+ |---|---|---|---|
99
+ | MolmoAct2-LIBERO | 5B | 98.4% (**97.2% re-measured on our rig**, n=500) | **98.25%** |
100
+ | π0 | 3.3B | 98% | — |
101
+ | SmolVLA-2.25B | 2.25B | 93% | — |
102
+ | XS-VLA | 0.26B | ~93% | 90.3% |
103
+ | MolmoAct-7B (v1) | 7B | 87.0% | 86.6% |
104
+ | **TinyVLA (this)** | **0.64B (140M online)** | **86% dual-rate** | **84.0%** |
105
+ | OpenVLA | 7B | 84.7% | 76.5% |
106
+
107
+ **MolmoAct2 context** (from their cards/repo, no numeric tables published outside the
108
+ LIBERO-LeRobot card): 5B params on Molmo2-ER backbone + flow expert; claims to beat π0.5;
109
+ Molmo2-ER reportedly surpasses GPT-5 / Gemini-Robotics-ER-1.5 on 13 embodied-reasoning
110
+ benchmarks; #1 VLA on the MolmoSpace leaderboard. Inference needs ~26GB fp32 (16GB bf16),
111
+ cuda-graphs for ~2× speedup — H100-class serving vs our Raspberry-Pi-class 140M fast path.
112
+ Notably they ship **MolmoAct2-SO100_101** (absolute joint control) — a 5B competitor on our
113
+ target embodiment; a head-to-head offline eval on SO101 held-out data is an obvious next test.
114
+
115
+ Honesty notes: our n=100 (±8pp) vs their 500; single suite; eval covers init states 0–9
116
+ per task (demos of same tasks/init states are in training data — this is the standard
117
+ LIBERO protocol used by all listed models). Multi-suite evals (object/goal/long) and an
118
+ identically-protocoled SmolVLA baseline run are in progress.
119
+
120
+ ### Dual-rate validation (the architectural claim)
121
+
122
+ - Offline: held-out chunk MSE with latent from a frame 2s earlier == fresh (0.772 vs 0.772).
123
+ - Closed-loop: 86% @ 1 Hz refresh vs 79% full-rate.
124
+ - Deployment implication (Raspberry Pi 5): the 0.5B slow path can run at 0.5–1 Hz
125
+ (llama.cpp GGUF Q4 territory), only the 140M fast path needs real-time rates.
126
+
127
+ ## 4. Pitfalls we hit (read before reproducing)
128
+
129
+ 1. **robosuite/LIBERO renders are 180°-rotated** vs recorded datasets. A vertical flip
130
+ alone leaves a horizontal MIRROR — the policy reaches to the wrong side and scores 0%.
131
+ Use `img[::-1, ::-1]`. (lerobot's `LiberoEnv.render()` flips for visualization but
132
+ `_format_raw_obs` feeds policies raw rotated frames — check your eval path.)
133
+ 2. **eef orientation antipodal ambiguity**: env quat → rotvec gives −π-ish x-component,
134
+ datasets store +π-ish. Canonicalize (flip antipodal representation when rotvec_x < 0)
135
+ or your normalized state input is off by ~17σ.
136
+ 3. **HuggingFaceVLA/libero meta says fps=10 but frames are native 20 Hz** (~161
137
+ frames/episode ≈ 8s demos). Do NOT action-repeat at eval.
138
+ 4. **GT-replay harness is the decisive convention test**: replay dataset actions in the env
139
+ (first episode per task ↔ init_state 0). 4/5 success = conventions right; policy-independent.
140
+ 5. HuggingFaceVLA community_dataset v1/v3 are aggregates of v2.1 sub-datasets → convert with
141
+ lerobot's `convert_dataset_v21_to_v30.py`; 4/44 sources have meta≠parquet row counts
142
+ (broken at source) — validate `len(reader.hf_dataset) == meta.total_frames`.
143
+ 6. Qwen3.5 custom forwards need `mm_token_type_ids` for M-RoPE; patchify must match the
144
+ official processor bit-exact (verify!). Linear-attention needs fla-core + causal-conv1d
145
+ or you get a 5× slower torch fallback.
146
+ 7. Flow-matching loss can drop while conditioning is ignored — A/B with shuffled
147
+ observations to verify the model actually uses them.
148
+
149
+ ## 5. Repository layout
150
+
151
+ ```
152
+ src/tinyvla/ # LeRobot plugin: configuration/modeling/processor + modules
153
+ scripts/train.py # accelerate loop: weighted mixture, staleness aug, distill loss, resume
154
+ scripts/eval_libero.py # closed-loop LIBERO eval (all convention fixes, dual-rate, video)
155
+ scripts/replay_libero.py, diag_obs_gap.py # convention diagnostics
156
+ scripts/prepare_community_v3.py, label_spatial.py, eval_offline.py
157
+ configs/ # stage2/stage3/libero_ft* yaml
158
+ checkpoints/ # stage2_final, stage3_final, libero_ft4_final (main)
159
+ videos/ # sample eval episodes
160
+ ```
161
+
162
+ ## 6. Quick start
163
+
164
+ ```python
165
+ from tinyvla.modeling_tinyvla import TinyVLAPolicy
166
+ policy = TinyVLAPolicy.from_pretrained("AlexWortega/tinyvla") # ft4 checkpoint at root
167
+ # closed-loop LIBERO: python scripts/eval_libero.py --checkpoint <ckpt> \
168
+ # --suite libero_spatial --episodes 100 --n-action-steps 10 --refresh-s 1.0
169
+ ```
170
+
171
+ Trained 2026-08-14 → 2026-08-19 on 1× RTX A6000 (48GB), lerobot 0.6.1, torch 2.10, transformers 5.5.4.
tinyvla2/assets/so_arm/assets/base_motor_holder_so101_v1.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8cd2f241037ea377af1191fffe0dd9d9006beea6dcc48543660ed41647072424
3
+ size 1877084
tinyvla2/assets/so_arm/assets/base_so101_v2.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bb12b7026575e1f70ccc7240051f9d943553bf34e5128537de6cd86fae33924d
3
+ size 471584
tinyvla2/assets/so_arm/assets/motor_holder_so101_base_v1.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:31242ae6fb59d8b15c66617b88ad8e9bded62d57c35d11c0c43a70d2f4caa95b
3
+ size 1129384
tinyvla2/assets/so_arm/assets/motor_holder_so101_wrist_v1.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:887f92e6013cb64ea3a1ab8675e92da1e0beacfd5e001f972523540545e08011
3
+ size 1052184
tinyvla2/assets/so_arm/assets/moving_jaw_so101_v1.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:785a9dded2f474bc1d869e0d3dae398a3dcd9c0c345640040472210d2861fa9d
3
+ size 1413584
tinyvla2/assets/so_arm/assets/rotation_pitch_so101_v1.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9be900cc2a2bf718102841ef82ef8d2873842427648092c8ed2ca1e2ef4ffa34
3
+ size 883684
tinyvla2/assets/so_arm/assets/sts3215_03a_no_horn_v1.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:75ef3781b752e4065891aea855e34dc161a38a549549cd0970cedd07eae6f887
3
+ size 865884
tinyvla2/assets/so_arm/assets/sts3215_03a_v1.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a37c871fb502483ab96c256baf457d36f2e97afc9205313d9c5ab275ef941cd0
3
+ size 954084
tinyvla2/assets/so_arm/assets/under_arm_so101_v1.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d01d1f2de365651dcad9d6669e94ff87ff7652b5bb2d10752a66a456a86dbc71
3
+ size 1975884
tinyvla2/assets/so_arm/assets/upper_arm_so101_v1.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:475056e03a17e71919b82fd88ab9a0b898ab50164f2a7943652a6b2941bb2d4f
3
+ size 1303484
tinyvla2/assets/so_arm/assets/waveshare_mounting_plate_so101_v2.stl ADDED
Binary file (62.8 kB). View file
 
tinyvla2/assets/so_arm/assets/wrist_roll_follower_so101_v1.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4b17b410a12d64ec39554abc3e8054d8a97384b2dc4a8d95a5ecb2a93670f5f4
3
+ size 1439884
tinyvla2/assets/so_arm/assets/wrist_roll_pitch_so101_v2.stl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6c7ec5525b4d8b9e397a30ab4bb0037156a5d5f38a4adf2c7d943d6c56eda5ae
3
+ size 2699784
tinyvla2/assets/so_arm/so101_new_calib.urdf ADDED
@@ -0,0 +1,453 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generated using onshape-to-robot -->
3
+ <!-- Onshape https://cad.onshape.com/documents/7715cc284bb430fe6dab4ffd/w/4fd0791b683777b02f8d975a/e/826c553ede3b7592eb9ca800 -->
4
+ <robot name="so101_new_calib">
5
+
6
+ <!-- Materials -->
7
+ <material name="3d_printed">
8
+ <color rgba="1.0 0.82 0.12 1.0"/>
9
+ </material>
10
+ <material name="sts3215">
11
+ <color rgba="0.1 0.1 0.1 1.0"/>
12
+ </material>
13
+
14
+ <!-- Link base -->
15
+ <link name="base_link">
16
+ <inertial>
17
+ <origin xyz="0.0137179 -5.19711e-05 0.0334843" rpy="0 0 0"/>
18
+ <mass value="0.147"/>
19
+ <inertia ixx="0.000114686" ixy="-4.59787e-07" ixz="4.97151e-06" iyy="0.000136117" iyz="9.75275e-08" izz="0.000130364"/>
20
+ </inertial>
21
+ <!-- Part base_motor_holder_so101_v1 -->
22
+ <visual>
23
+ <origin xyz="-0.00636471 -9.94414e-05 -0.0024" rpy="1.5708 -1.67685e-15 1.5708"/>
24
+ <geometry>
25
+ <mesh filename="assets/base_motor_holder_so101_v1.stl"/>
26
+ </geometry>
27
+ <material name="3d_printed"/>
28
+ </visual>
29
+ <collision>
30
+ <origin xyz="-0.00636471 -9.94414e-05 -0.0024" rpy="1.5708 -1.67685e-15 1.5708"/>
31
+ <geometry>
32
+ <mesh filename="assets/base_motor_holder_so101_v1.stl"/>
33
+ </geometry>
34
+ </collision>
35
+ <!-- Part base_so101_v2 -->
36
+ <visual>
37
+ <origin xyz="-0.00636471 -8.97657e-09 -0.0024" rpy="1.5708 -2.78073e-29 1.5708"/>
38
+ <geometry>
39
+ <mesh filename="assets/base_so101_v2.stl"/>
40
+ </geometry>
41
+ <material name="3d_printed"/>
42
+ </visual>
43
+ <collision>
44
+ <origin xyz="-0.00636471 -8.97657e-09 -0.0024" rpy="1.5708 -2.78073e-29 1.5708"/>
45
+ <geometry>
46
+ <mesh filename="assets/base_so101_v2.stl"/>
47
+ </geometry>
48
+ </collision>
49
+ <!-- Part sts3215_03a_v1 -->
50
+ <visual>
51
+ <origin xyz="0.0263353 -8.97657e-09 0.0437" rpy="-8.21148e-16 7.84513e-18 1.249e-15"/>
52
+ <geometry>
53
+ <mesh filename="assets/sts3215_03a_v1.stl"/>
54
+ </geometry>
55
+ <material name="sts3215"/>
56
+ </visual>
57
+ <collision>
58
+ <origin xyz="0.0263353 -8.97657e-09 0.0437" rpy="-8.21148e-16 7.84513e-18 1.249e-15"/>
59
+ <geometry>
60
+ <mesh filename="assets/sts3215_03a_v1.stl"/>
61
+ </geometry>
62
+ </collision>
63
+ <!-- Part waveshare_mounting_plate_so101_v2 -->
64
+ <visual>
65
+ <origin xyz="-0.0309827 -0.000199441 0.0474" rpy="1.5708 -1.35493e-14 1.5708"/>
66
+ <geometry>
67
+ <mesh filename="assets/waveshare_mounting_plate_so101_v2.stl"/>
68
+ </geometry>
69
+ <material name="3d_printed"/>
70
+ </visual>
71
+ <collision>
72
+ <origin xyz="-0.0309827 -0.000199441 0.0474" rpy="1.5708 -1.35493e-14 1.5708"/>
73
+ <geometry>
74
+ <mesh filename="assets/waveshare_mounting_plate_so101_v2.stl"/>
75
+ </geometry>
76
+ </collision>
77
+ </link>
78
+
79
+ <!-- Link shoulder -->
80
+ <link name="shoulder_link">
81
+ <inertial>
82
+ <origin xyz="-0.0307604 -1.66727e-05 -0.0252713" rpy="0 0 0"/>
83
+ <mass value="0.100006"/>
84
+ <inertia ixx="8.3759e-05" ixy="7.55525e-08" ixz="-1.16342e-06" iyy="8.10403e-05" iyz="1.54663e-07" izz="2.39783e-05"/>
85
+ </inertial>
86
+ <!-- Part sts3215_03a_v1_2 -->
87
+ <visual>
88
+ <origin xyz="-0.0303992 0.000422241 -0.0417" rpy="1.5708 1.5708 0"/>
89
+ <geometry>
90
+ <mesh filename="assets/sts3215_03a_v1.stl"/>
91
+ </geometry>
92
+ <material name="sts3215"/>
93
+ </visual>
94
+ <collision>
95
+ <origin xyz="-0.0303992 0.000422241 -0.0417" rpy="1.5708 1.5708 0"/>
96
+ <geometry>
97
+ <mesh filename="assets/sts3215_03a_v1.stl"/>
98
+ </geometry>
99
+ </collision>
100
+ <!-- Part motor_holder_so101_base_v1 -->
101
+ <visual>
102
+ <origin xyz="-0.0675992 -0.000177759 0.0158499" rpy="1.5708 -1.5708 0"/>
103
+ <geometry>
104
+ <mesh filename="assets/motor_holder_so101_base_v1.stl"/>
105
+ </geometry>
106
+ <material name="3d_printed"/>
107
+ </visual>
108
+ <collision>
109
+ <origin xyz="-0.0675992 -0.000177759 0.0158499" rpy="1.5708 -1.5708 0"/>
110
+ <geometry>
111
+ <mesh filename="assets/motor_holder_so101_base_v1.stl"/>
112
+ </geometry>
113
+ </collision>
114
+ <!-- Part rotation_pitch_so101_v1 -->
115
+ <visual>
116
+ <origin xyz="0.0122008 2.22413e-05 0.0464" rpy="-1.5708 2.35221e-33 0"/>
117
+ <geometry>
118
+ <mesh filename="assets/rotation_pitch_so101_v1.stl"/>
119
+ </geometry>
120
+ <material name="3d_printed"/>
121
+ </visual>
122
+ <collision>
123
+ <origin xyz="0.0122008 2.22413e-05 0.0464" rpy="-1.5708 2.35221e-33 0"/>
124
+ <geometry>
125
+ <mesh filename="assets/rotation_pitch_so101_v1.stl"/>
126
+ </geometry>
127
+ </collision>
128
+ </link>
129
+
130
+ <!-- Link upper_arm -->
131
+ <link name="upper_arm_link">
132
+ <inertial>
133
+ <origin xyz="-0.0898471 -0.00838224 0.0184089" rpy="0 0 0"/>
134
+ <mass value="0.103"/>
135
+ <inertia ixx="4.08002e-05" ixy="-1.97819e-05" ixz="-4.03016e-08" iyy="0.000147318" iyz="8.97326e-09" izz="0.000142487"/>
136
+ </inertial>
137
+ <!-- Part sts3215_03a_v1_3 -->
138
+ <visual>
139
+ <origin xyz="-0.11257 -0.0155 0.0187" rpy="-3.14159 -5.27356e-16 -1.5708"/>
140
+ <geometry>
141
+ <mesh filename="assets/sts3215_03a_v1.stl"/>
142
+ </geometry>
143
+ <material name="sts3215"/>
144
+ </visual>
145
+ <collision>
146
+ <origin xyz="-0.11257 -0.0155 0.0187" rpy="-3.14159 -5.27356e-16 -1.5708"/>
147
+ <geometry>
148
+ <mesh filename="assets/sts3215_03a_v1.stl"/>
149
+ </geometry>
150
+ </collision>
151
+ <!-- Part upper_arm_so101_v1 -->
152
+ <visual>
153
+ <origin xyz="-0.065085 0.012 0.0182" rpy="3.14159 -0 -1.30911e-30"/>
154
+ <geometry>
155
+ <mesh filename="assets/upper_arm_so101_v1.stl"/>
156
+ </geometry>
157
+ <material name="3d_printed"/>
158
+ </visual>
159
+ <collision>
160
+ <origin xyz="-0.065085 0.012 0.0182" rpy="3.14159 -0 -1.30911e-30"/>
161
+ <geometry>
162
+ <mesh filename="assets/upper_arm_so101_v1.stl"/>
163
+ </geometry>
164
+ </collision>
165
+ </link>
166
+
167
+ <!-- Link lower_arm -->
168
+ <link name="lower_arm_link">
169
+ <inertial>
170
+ <origin xyz="-0.0980701 0.00324376 0.0182831" rpy="0 0 0"/>
171
+ <mass value="0.104"/>
172
+ <inertia ixx="2.87438e-05" ixy="7.41152e-06" ixz="1.26409e-06" iyy="0.000159844" iyz="-4.90188e-08" izz="0.00014529"/>
173
+ </inertial>
174
+ <!-- Part under_arm_so101_v1 -->
175
+ <visual>
176
+ <origin xyz="-0.0648499 -0.032 0.0182" rpy="3.14159 -0 6.67202e-31"/>
177
+ <geometry>
178
+ <mesh filename="assets/under_arm_so101_v1.stl"/>
179
+ </geometry>
180
+ <material name="3d_printed"/>
181
+ </visual>
182
+ <collision>
183
+ <origin xyz="-0.0648499 -0.032 0.0182" rpy="3.14159 -0 6.67202e-31"/>
184
+ <geometry>
185
+ <mesh filename="assets/under_arm_so101_v1.stl"/>
186
+ </geometry>
187
+ </collision>
188
+ <!-- Part motor_holder_so101_wrist_v1 -->
189
+ <visual>
190
+ <origin xyz="-0.0648499 -0.032 0.018" rpy="-3.14159 -2.55351e-15 -1.83387e-30"/>
191
+ <geometry>
192
+ <mesh filename="assets/motor_holder_so101_wrist_v1.stl"/>
193
+ </geometry>
194
+ <material name="3d_printed"/>
195
+ </visual>
196
+ <collision>
197
+ <origin xyz="-0.0648499 -0.032 0.018" rpy="-3.14159 -2.55351e-15 -1.83387e-30"/>
198
+ <geometry>
199
+ <mesh filename="assets/motor_holder_so101_wrist_v1.stl"/>
200
+ </geometry>
201
+ </collision>
202
+ <!-- Part sts3215_03a_v1_4 -->
203
+ <visual>
204
+ <origin xyz="-0.1224 0.0052 0.0187" rpy="-3.14159 -7.88861e-31 -3.14159"/>
205
+ <geometry>
206
+ <mesh filename="assets/sts3215_03a_v1.stl"/>
207
+ </geometry>
208
+ <material name="sts3215"/>
209
+ </visual>
210
+ <collision>
211
+ <origin xyz="-0.1224 0.0052 0.0187" rpy="-3.14159 -7.88861e-31 -3.14159"/>
212
+ <geometry>
213
+ <mesh filename="assets/sts3215_03a_v1.stl"/>
214
+ </geometry>
215
+ </collision>
216
+ </link>
217
+
218
+ <!-- Link wrist -->
219
+ <link name="wrist_link">
220
+ <inertial>
221
+ <origin xyz="-0.000103312 -0.0386143 0.0281156" rpy="0 0 0"/>
222
+ <mass value="0.079"/>
223
+ <inertia ixx="3.68263e-05" ixy="1.7893e-08" ixz="-5.28128e-08" iyy="2.5391e-05" iyz="3.6412e-06" izz="2.1e-05"/>
224
+ </inertial>
225
+ <!-- Part sts3215_03a_no_horn_v1 -->
226
+ <visual>
227
+ <origin xyz="8.32667e-17 -0.0424 0.0306" rpy="1.5708 1.5708 0"/>
228
+ <geometry>
229
+ <mesh filename="assets/sts3215_03a_no_horn_v1.stl"/>
230
+ </geometry>
231
+ <material name="sts3215"/>
232
+ </visual>
233
+ <collision>
234
+ <origin xyz="8.32667e-17 -0.0424 0.0306" rpy="1.5708 1.5708 0"/>
235
+ <geometry>
236
+ <mesh filename="assets/sts3215_03a_no_horn_v1.stl"/>
237
+ </geometry>
238
+ </collision>
239
+ <!-- Part wrist_roll_pitch_so101_v2 -->
240
+ <visual>
241
+ <origin xyz="0 -0.028 0.0181" rpy="-1.5708 -1.5708 0"/>
242
+ <geometry>
243
+ <mesh filename="assets/wrist_roll_pitch_so101_v2.stl"/>
244
+ </geometry>
245
+ <material name="3d_printed"/>
246
+ </visual>
247
+ <collision>
248
+ <origin xyz="0 -0.028 0.0181" rpy="-1.5708 -1.5708 0"/>
249
+ <geometry>
250
+ <mesh filename="assets/wrist_roll_pitch_so101_v2.stl"/>
251
+ </geometry>
252
+ </collision>
253
+ </link>
254
+
255
+ <!-- Link gripper -->
256
+ <link name="gripper_link">
257
+ <inertial>
258
+ <origin xyz="0.000213627 0.000245138 -0.025187" rpy="0 0 0"/>
259
+ <mass value="0.087"/>
260
+ <inertia ixx="2.75087e-05" ixy="-3.35241e-07" ixz="-5.7352e-06" iyy="4.33657e-05" iyz="-5.17847e-08" izz="3.45059e-05"/>
261
+ </inertial>
262
+ <!-- Part sts3215_03a_v1_5 -->
263
+ <visual>
264
+ <origin xyz="0.0077 0.0001 -0.0234" rpy="-1.5708 -5.19179e-17 -1.66533e-16"/>
265
+ <geometry>
266
+ <mesh filename="assets/sts3215_03a_v1.stl"/>
267
+ </geometry>
268
+ <material name="sts3215"/>
269
+ </visual>
270
+ <collision>
271
+ <origin xyz="0.0077 0.0001 -0.0234" rpy="-1.5708 -5.19179e-17 -1.66533e-16"/>
272
+ <geometry>
273
+ <mesh filename="assets/sts3215_03a_v1.stl"/>
274
+ </geometry>
275
+ </collision>
276
+ <!-- Part wrist_roll_follower_so101_v1 -->
277
+ <visual>
278
+ <origin xyz="8.32667e-17 -0.000218214 0.000949706" rpy="-3.14159 -5.55112e-17 0"/>
279
+ <geometry>
280
+ <mesh filename="assets/wrist_roll_follower_so101_v1.stl"/>
281
+ </geometry>
282
+ <material name="3d_printed"/>
283
+ </visual>
284
+ <collision>
285
+ <origin xyz="8.32667e-17 -0.000218214 0.000949706" rpy="-3.14159 -5.55112e-17 0"/>
286
+ <geometry>
287
+ <mesh filename="assets/wrist_roll_follower_so101_v1.stl"/>
288
+ </geometry>
289
+ </collision>
290
+ </link>
291
+
292
+ <!-- Gripper frame (dummy link + fixed joint) -->
293
+ <link name="gripper_frame_link">
294
+ <origin xyz="0 0 0" rpy="0 -0 0"/>
295
+ <inertial>
296
+ <origin xyz="0 0 0" rpy="0 0 0"/>
297
+ <mass value="1e-9"/>
298
+ <inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0"/>
299
+ </inertial>
300
+ </link>
301
+
302
+ <joint name="gripper_frame_joint" type="fixed">
303
+ <origin xyz="-0.0079 -0.000218121 -0.0981274" rpy="0 3.14159 0"/>
304
+ <parent link="gripper_link"/>
305
+ <child link="gripper_frame_link"/>
306
+ <axis xyz="0 0 0"/>
307
+ </joint>
308
+
309
+ <!-- Link moving_jaw_so101_v1 -->
310
+ <link name="moving_jaw_so101_v1_link">
311
+ <inertial>
312
+ <origin xyz="-0.00157495 -0.0300244 0.0192755" rpy="0 0 0"/>
313
+ <mass value="0.012"/>
314
+ <inertia ixx="6.61427e-06" ixy="-3.19807e-07" ixz="-5.90717e-09" iyy="1.89032e-06" iyz="-1.09945e-07" izz="5.28738e-06"/>
315
+ </inertial>
316
+ <!-- Part moving_jaw_so101_v1 -->
317
+ <visual>
318
+ <origin xyz="-5.55112e-17 -5.55112e-17 0.0189" rpy="9.53145e-17 6.93889e-18 1.24077e-24"/>
319
+ <geometry>
320
+ <mesh filename="assets/moving_jaw_so101_v1.stl"/>
321
+ </geometry>
322
+ <material name="3d_printed"/>
323
+ </visual>
324
+ <collision>
325
+ <origin xyz="-5.55112e-17 -5.55112e-17 0.0189" rpy="9.53145e-17 6.93889e-18 1.24077e-24"/>
326
+ <geometry>
327
+ <mesh filename="assets/moving_jaw_so101_v1.stl"/>
328
+ </geometry>
329
+ </collision>
330
+ </link>
331
+
332
+ <!-- Joint from gripper to moving_jaw_so101_v1 -->
333
+ <joint name="gripper" type="revolute">
334
+ <origin xyz="0.0202 0.0188 -0.0234" rpy="1.5708 -5.24284e-08 -1.41553e-15"/>
335
+ <parent link="gripper_link"/>
336
+ <child link="moving_jaw_so101_v1_link"/>
337
+ <axis xyz="0 0 1"/>
338
+ <limit effort="10" velocity="10" lower="-0.174533" upper="1.74533"/>
339
+ </joint>
340
+
341
+ <transmission name="gripper_trans">
342
+ <type>transmission_interface/SimpleTransmission</type>
343
+ <joint name="gripper">
344
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
345
+ </joint>
346
+ <actuator name="motor6">
347
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
348
+ <mechanicalReduction>1</mechanicalReduction>
349
+ </actuator>
350
+ </transmission>
351
+
352
+ <!-- Joint from wrist to gripper -->
353
+ <joint name="wrist_roll" type="revolute">
354
+ <origin xyz="5.55112e-17 -0.0611 0.0181" rpy="1.5708 0.0486795 3.14159"/>
355
+ <parent link="wrist_link"/>
356
+ <child link="gripper_link"/>
357
+ <axis xyz="0 0 1"/>
358
+ <limit effort="10" velocity="10" lower="-2.74385" upper="2.84121"/>
359
+ </joint>
360
+
361
+ <transmission name="wrist_roll_trans">
362
+ <type>transmission_interface/SimpleTransmission</type>
363
+ <joint name="wrist_roll">
364
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
365
+ </joint>
366
+ <actuator name="motor5">
367
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
368
+ <mechanicalReduction>1</mechanicalReduction>
369
+ </actuator>
370
+ </transmission>
371
+
372
+ <!-- Joint from lower_arm to wrist -->
373
+ <joint name="wrist_flex" type="revolute">
374
+ <origin xyz="-0.1349 0.0052 3.62355e-17" rpy="4.02456e-15 8.67362e-16 -1.5708"/>
375
+ <parent link="lower_arm_link"/>
376
+ <child link="wrist_link"/>
377
+ <axis xyz="0 0 1"/>
378
+ <limit effort="10" velocity="10" lower="-1.65806" upper="1.65806"/>
379
+ </joint>
380
+
381
+ <transmission name="wrist_flex_trans">
382
+ <type>transmission_interface/SimpleTransmission</type>
383
+ <joint name="wrist_flex">
384
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
385
+ </joint>
386
+ <actuator name="motor4">
387
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
388
+ <mechanicalReduction>1</mechanicalReduction>
389
+ </actuator>
390
+ </transmission>
391
+
392
+ <!-- Joint from upper_arm to lower_arm -->
393
+ <!-- Note: 5-degree calibration offset applied to joint limits -->
394
+ <joint name="elbow_flex" type="revolute">
395
+ <origin xyz="-0.11257 -0.028 1.73763e-16" rpy="-3.63608e-16 8.74301e-16 1.5708"/>
396
+ <parent link="upper_arm_link"/>
397
+ <child link="lower_arm_link"/>
398
+ <axis xyz="0 0 1"/>
399
+ <limit effort="10" velocity="10" lower="-1.69" upper="1.69"/>
400
+ </joint>
401
+
402
+ <transmission name="elbow_flex_trans">
403
+ <type>transmission_interface/SimpleTransmission</type>
404
+ <joint name="elbow_flex">
405
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
406
+ </joint>
407
+ <actuator name="motor3">
408
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
409
+ <mechanicalReduction>1</mechanicalReduction>
410
+ </actuator>
411
+ </transmission>
412
+
413
+ <!-- Joint from shoulder to upper_arm -->
414
+ <joint name="shoulder_lift" type="revolute">
415
+ <origin xyz="-0.0303992 -0.0182778 -0.0542" rpy="-1.5708 -1.5708 0"/>
416
+ <parent link="shoulder_link"/>
417
+ <child link="upper_arm_link"/>
418
+ <axis xyz="0 0 1"/>
419
+ <limit effort="10" velocity="10" lower="-1.74533" upper="1.74533"/>
420
+ </joint>
421
+
422
+ <transmission name="shoulder_lift_trans">
423
+ <type>transmission_interface/SimpleTransmission</type>
424
+ <joint name="shoulder_lift">
425
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
426
+ </joint>
427
+ <actuator name="motor2">
428
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
429
+ <mechanicalReduction>1</mechanicalReduction>
430
+ </actuator>
431
+ </transmission>
432
+
433
+ <!-- Joint from base to shoulder -->
434
+ <joint name="shoulder_pan" type="revolute">
435
+ <origin xyz="0.0388353 -8.97657e-09 0.0624" rpy="3.14159 4.18253e-17 -3.14159"/>
436
+ <parent link="base_link"/>
437
+ <child link="shoulder_link"/>
438
+ <axis xyz="0 0 1"/>
439
+ <limit effort="10" velocity="10" lower="-1.91986" upper="1.91986"/>
440
+ </joint>
441
+
442
+ <transmission name="shoulder_pan_trans">
443
+ <type>transmission_interface/SimpleTransmission</type>
444
+ <joint name="shoulder_pan">
445
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
446
+ </joint>
447
+ <actuator name="motor1">
448
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
449
+ <mechanicalReduction>1</mechanicalReduction>
450
+ </actuator>
451
+ </transmission>
452
+
453
+ </robot>
tinyvla2/assets/so_arm/so101_nomesh.urdf ADDED
@@ -0,0 +1,453 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generated using onshape-to-robot -->
3
+ <!-- Onshape https://cad.onshape.com/documents/7715cc284bb430fe6dab4ffd/w/4fd0791b683777b02f8d975a/e/826c553ede3b7592eb9ca800 -->
4
+ <robot name="so101_new_calib">
5
+
6
+ <!-- Materials -->
7
+ <material name="3d_printed">
8
+ <color rgba="1.0 0.82 0.12 1.0"/>
9
+ </material>
10
+ <material name="sts3215">
11
+ <color rgba="0.1 0.1 0.1 1.0"/>
12
+ </material>
13
+
14
+ <!-- Link base -->
15
+ <link name="base_link">
16
+ <inertial>
17
+ <origin xyz="0.0137179 -5.19711e-05 0.0334843" rpy="0 0 0"/>
18
+ <mass value="0.147"/>
19
+ <inertia ixx="0.000114686" ixy="-4.59787e-07" ixz="4.97151e-06" iyy="0.000136117" iyz="9.75275e-08" izz="0.000130364"/>
20
+ </inertial>
21
+ <!-- Part base_motor_holder_so101_v1 -->
22
+ <visual>
23
+ <origin xyz="-0.00636471 -9.94414e-05 -0.0024" rpy="1.5708 -1.67685e-15 1.5708"/>
24
+ <geometry>
25
+ <mesh filename=""/>
26
+ </geometry>
27
+ <material name="3d_printed"/>
28
+ </visual>
29
+ <collision>
30
+ <origin xyz="-0.00636471 -9.94414e-05 -0.0024" rpy="1.5708 -1.67685e-15 1.5708"/>
31
+ <geometry>
32
+ <mesh filename=""/>
33
+ </geometry>
34
+ </collision>
35
+ <!-- Part base_so101_v2 -->
36
+ <visual>
37
+ <origin xyz="-0.00636471 -8.97657e-09 -0.0024" rpy="1.5708 -2.78073e-29 1.5708"/>
38
+ <geometry>
39
+ <mesh filename=""/>
40
+ </geometry>
41
+ <material name="3d_printed"/>
42
+ </visual>
43
+ <collision>
44
+ <origin xyz="-0.00636471 -8.97657e-09 -0.0024" rpy="1.5708 -2.78073e-29 1.5708"/>
45
+ <geometry>
46
+ <mesh filename=""/>
47
+ </geometry>
48
+ </collision>
49
+ <!-- Part sts3215_03a_v1 -->
50
+ <visual>
51
+ <origin xyz="0.0263353 -8.97657e-09 0.0437" rpy="-8.21148e-16 7.84513e-18 1.249e-15"/>
52
+ <geometry>
53
+ <mesh filename=""/>
54
+ </geometry>
55
+ <material name="sts3215"/>
56
+ </visual>
57
+ <collision>
58
+ <origin xyz="0.0263353 -8.97657e-09 0.0437" rpy="-8.21148e-16 7.84513e-18 1.249e-15"/>
59
+ <geometry>
60
+ <mesh filename=""/>
61
+ </geometry>
62
+ </collision>
63
+ <!-- Part waveshare_mounting_plate_so101_v2 -->
64
+ <visual>
65
+ <origin xyz="-0.0309827 -0.000199441 0.0474" rpy="1.5708 -1.35493e-14 1.5708"/>
66
+ <geometry>
67
+ <mesh filename=""/>
68
+ </geometry>
69
+ <material name="3d_printed"/>
70
+ </visual>
71
+ <collision>
72
+ <origin xyz="-0.0309827 -0.000199441 0.0474" rpy="1.5708 -1.35493e-14 1.5708"/>
73
+ <geometry>
74
+ <mesh filename=""/>
75
+ </geometry>
76
+ </collision>
77
+ </link>
78
+
79
+ <!-- Link shoulder -->
80
+ <link name="shoulder_link">
81
+ <inertial>
82
+ <origin xyz="-0.0307604 -1.66727e-05 -0.0252713" rpy="0 0 0"/>
83
+ <mass value="0.100006"/>
84
+ <inertia ixx="8.3759e-05" ixy="7.55525e-08" ixz="-1.16342e-06" iyy="8.10403e-05" iyz="1.54663e-07" izz="2.39783e-05"/>
85
+ </inertial>
86
+ <!-- Part sts3215_03a_v1_2 -->
87
+ <visual>
88
+ <origin xyz="-0.0303992 0.000422241 -0.0417" rpy="1.5708 1.5708 0"/>
89
+ <geometry>
90
+ <mesh filename=""/>
91
+ </geometry>
92
+ <material name="sts3215"/>
93
+ </visual>
94
+ <collision>
95
+ <origin xyz="-0.0303992 0.000422241 -0.0417" rpy="1.5708 1.5708 0"/>
96
+ <geometry>
97
+ <mesh filename=""/>
98
+ </geometry>
99
+ </collision>
100
+ <!-- Part motor_holder_so101_base_v1 -->
101
+ <visual>
102
+ <origin xyz="-0.0675992 -0.000177759 0.0158499" rpy="1.5708 -1.5708 0"/>
103
+ <geometry>
104
+ <mesh filename=""/>
105
+ </geometry>
106
+ <material name="3d_printed"/>
107
+ </visual>
108
+ <collision>
109
+ <origin xyz="-0.0675992 -0.000177759 0.0158499" rpy="1.5708 -1.5708 0"/>
110
+ <geometry>
111
+ <mesh filename=""/>
112
+ </geometry>
113
+ </collision>
114
+ <!-- Part rotation_pitch_so101_v1 -->
115
+ <visual>
116
+ <origin xyz="0.0122008 2.22413e-05 0.0464" rpy="-1.5708 2.35221e-33 0"/>
117
+ <geometry>
118
+ <mesh filename=""/>
119
+ </geometry>
120
+ <material name="3d_printed"/>
121
+ </visual>
122
+ <collision>
123
+ <origin xyz="0.0122008 2.22413e-05 0.0464" rpy="-1.5708 2.35221e-33 0"/>
124
+ <geometry>
125
+ <mesh filename=""/>
126
+ </geometry>
127
+ </collision>
128
+ </link>
129
+
130
+ <!-- Link upper_arm -->
131
+ <link name="upper_arm_link">
132
+ <inertial>
133
+ <origin xyz="-0.0898471 -0.00838224 0.0184089" rpy="0 0 0"/>
134
+ <mass value="0.103"/>
135
+ <inertia ixx="4.08002e-05" ixy="-1.97819e-05" ixz="-4.03016e-08" iyy="0.000147318" iyz="8.97326e-09" izz="0.000142487"/>
136
+ </inertial>
137
+ <!-- Part sts3215_03a_v1_3 -->
138
+ <visual>
139
+ <origin xyz="-0.11257 -0.0155 0.0187" rpy="-3.14159 -5.27356e-16 -1.5708"/>
140
+ <geometry>
141
+ <mesh filename=""/>
142
+ </geometry>
143
+ <material name="sts3215"/>
144
+ </visual>
145
+ <collision>
146
+ <origin xyz="-0.11257 -0.0155 0.0187" rpy="-3.14159 -5.27356e-16 -1.5708"/>
147
+ <geometry>
148
+ <mesh filename=""/>
149
+ </geometry>
150
+ </collision>
151
+ <!-- Part upper_arm_so101_v1 -->
152
+ <visual>
153
+ <origin xyz="-0.065085 0.012 0.0182" rpy="3.14159 -0 -1.30911e-30"/>
154
+ <geometry>
155
+ <mesh filename=""/>
156
+ </geometry>
157
+ <material name="3d_printed"/>
158
+ </visual>
159
+ <collision>
160
+ <origin xyz="-0.065085 0.012 0.0182" rpy="3.14159 -0 -1.30911e-30"/>
161
+ <geometry>
162
+ <mesh filename=""/>
163
+ </geometry>
164
+ </collision>
165
+ </link>
166
+
167
+ <!-- Link lower_arm -->
168
+ <link name="lower_arm_link">
169
+ <inertial>
170
+ <origin xyz="-0.0980701 0.00324376 0.0182831" rpy="0 0 0"/>
171
+ <mass value="0.104"/>
172
+ <inertia ixx="2.87438e-05" ixy="7.41152e-06" ixz="1.26409e-06" iyy="0.000159844" iyz="-4.90188e-08" izz="0.00014529"/>
173
+ </inertial>
174
+ <!-- Part under_arm_so101_v1 -->
175
+ <visual>
176
+ <origin xyz="-0.0648499 -0.032 0.0182" rpy="3.14159 -0 6.67202e-31"/>
177
+ <geometry>
178
+ <mesh filename=""/>
179
+ </geometry>
180
+ <material name="3d_printed"/>
181
+ </visual>
182
+ <collision>
183
+ <origin xyz="-0.0648499 -0.032 0.0182" rpy="3.14159 -0 6.67202e-31"/>
184
+ <geometry>
185
+ <mesh filename=""/>
186
+ </geometry>
187
+ </collision>
188
+ <!-- Part motor_holder_so101_wrist_v1 -->
189
+ <visual>
190
+ <origin xyz="-0.0648499 -0.032 0.018" rpy="-3.14159 -2.55351e-15 -1.83387e-30"/>
191
+ <geometry>
192
+ <mesh filename=""/>
193
+ </geometry>
194
+ <material name="3d_printed"/>
195
+ </visual>
196
+ <collision>
197
+ <origin xyz="-0.0648499 -0.032 0.018" rpy="-3.14159 -2.55351e-15 -1.83387e-30"/>
198
+ <geometry>
199
+ <mesh filename=""/>
200
+ </geometry>
201
+ </collision>
202
+ <!-- Part sts3215_03a_v1_4 -->
203
+ <visual>
204
+ <origin xyz="-0.1224 0.0052 0.0187" rpy="-3.14159 -7.88861e-31 -3.14159"/>
205
+ <geometry>
206
+ <mesh filename=""/>
207
+ </geometry>
208
+ <material name="sts3215"/>
209
+ </visual>
210
+ <collision>
211
+ <origin xyz="-0.1224 0.0052 0.0187" rpy="-3.14159 -7.88861e-31 -3.14159"/>
212
+ <geometry>
213
+ <mesh filename=""/>
214
+ </geometry>
215
+ </collision>
216
+ </link>
217
+
218
+ <!-- Link wrist -->
219
+ <link name="wrist_link">
220
+ <inertial>
221
+ <origin xyz="-0.000103312 -0.0386143 0.0281156" rpy="0 0 0"/>
222
+ <mass value="0.079"/>
223
+ <inertia ixx="3.68263e-05" ixy="1.7893e-08" ixz="-5.28128e-08" iyy="2.5391e-05" iyz="3.6412e-06" izz="2.1e-05"/>
224
+ </inertial>
225
+ <!-- Part sts3215_03a_no_horn_v1 -->
226
+ <visual>
227
+ <origin xyz="8.32667e-17 -0.0424 0.0306" rpy="1.5708 1.5708 0"/>
228
+ <geometry>
229
+ <mesh filename=""/>
230
+ </geometry>
231
+ <material name="sts3215"/>
232
+ </visual>
233
+ <collision>
234
+ <origin xyz="8.32667e-17 -0.0424 0.0306" rpy="1.5708 1.5708 0"/>
235
+ <geometry>
236
+ <mesh filename=""/>
237
+ </geometry>
238
+ </collision>
239
+ <!-- Part wrist_roll_pitch_so101_v2 -->
240
+ <visual>
241
+ <origin xyz="0 -0.028 0.0181" rpy="-1.5708 -1.5708 0"/>
242
+ <geometry>
243
+ <mesh filename=""/>
244
+ </geometry>
245
+ <material name="3d_printed"/>
246
+ </visual>
247
+ <collision>
248
+ <origin xyz="0 -0.028 0.0181" rpy="-1.5708 -1.5708 0"/>
249
+ <geometry>
250
+ <mesh filename=""/>
251
+ </geometry>
252
+ </collision>
253
+ </link>
254
+
255
+ <!-- Link gripper -->
256
+ <link name="gripper_link">
257
+ <inertial>
258
+ <origin xyz="0.000213627 0.000245138 -0.025187" rpy="0 0 0"/>
259
+ <mass value="0.087"/>
260
+ <inertia ixx="2.75087e-05" ixy="-3.35241e-07" ixz="-5.7352e-06" iyy="4.33657e-05" iyz="-5.17847e-08" izz="3.45059e-05"/>
261
+ </inertial>
262
+ <!-- Part sts3215_03a_v1_5 -->
263
+ <visual>
264
+ <origin xyz="0.0077 0.0001 -0.0234" rpy="-1.5708 -5.19179e-17 -1.66533e-16"/>
265
+ <geometry>
266
+ <mesh filename=""/>
267
+ </geometry>
268
+ <material name="sts3215"/>
269
+ </visual>
270
+ <collision>
271
+ <origin xyz="0.0077 0.0001 -0.0234" rpy="-1.5708 -5.19179e-17 -1.66533e-16"/>
272
+ <geometry>
273
+ <mesh filename=""/>
274
+ </geometry>
275
+ </collision>
276
+ <!-- Part wrist_roll_follower_so101_v1 -->
277
+ <visual>
278
+ <origin xyz="8.32667e-17 -0.000218214 0.000949706" rpy="-3.14159 -5.55112e-17 0"/>
279
+ <geometry>
280
+ <mesh filename=""/>
281
+ </geometry>
282
+ <material name="3d_printed"/>
283
+ </visual>
284
+ <collision>
285
+ <origin xyz="8.32667e-17 -0.000218214 0.000949706" rpy="-3.14159 -5.55112e-17 0"/>
286
+ <geometry>
287
+ <mesh filename=""/>
288
+ </geometry>
289
+ </collision>
290
+ </link>
291
+
292
+ <!-- Gripper frame (dummy link + fixed joint) -->
293
+ <link name="gripper_frame_link">
294
+ <origin xyz="0 0 0" rpy="0 -0 0"/>
295
+ <inertial>
296
+ <origin xyz="0 0 0" rpy="0 0 0"/>
297
+ <mass value="1e-9"/>
298
+ <inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0"/>
299
+ </inertial>
300
+ </link>
301
+
302
+ <joint name="gripper_frame_joint" type="fixed">
303
+ <origin xyz="-0.0079 -0.000218121 -0.0981274" rpy="0 3.14159 0"/>
304
+ <parent link="gripper_link"/>
305
+ <child link="gripper_frame_link"/>
306
+ <axis xyz="0 0 0"/>
307
+ </joint>
308
+
309
+ <!-- Link moving_jaw_so101_v1 -->
310
+ <link name="moving_jaw_so101_v1_link">
311
+ <inertial>
312
+ <origin xyz="-0.00157495 -0.0300244 0.0192755" rpy="0 0 0"/>
313
+ <mass value="0.012"/>
314
+ <inertia ixx="6.61427e-06" ixy="-3.19807e-07" ixz="-5.90717e-09" iyy="1.89032e-06" iyz="-1.09945e-07" izz="5.28738e-06"/>
315
+ </inertial>
316
+ <!-- Part moving_jaw_so101_v1 -->
317
+ <visual>
318
+ <origin xyz="-5.55112e-17 -5.55112e-17 0.0189" rpy="9.53145e-17 6.93889e-18 1.24077e-24"/>
319
+ <geometry>
320
+ <mesh filename=""/>
321
+ </geometry>
322
+ <material name="3d_printed"/>
323
+ </visual>
324
+ <collision>
325
+ <origin xyz="-5.55112e-17 -5.55112e-17 0.0189" rpy="9.53145e-17 6.93889e-18 1.24077e-24"/>
326
+ <geometry>
327
+ <mesh filename=""/>
328
+ </geometry>
329
+ </collision>
330
+ </link>
331
+
332
+ <!-- Joint from gripper to moving_jaw_so101_v1 -->
333
+ <joint name="gripper" type="revolute">
334
+ <origin xyz="0.0202 0.0188 -0.0234" rpy="1.5708 -5.24284e-08 -1.41553e-15"/>
335
+ <parent link="gripper_link"/>
336
+ <child link="moving_jaw_so101_v1_link"/>
337
+ <axis xyz="0 0 1"/>
338
+ <limit effort="10" velocity="10" lower="-0.174533" upper="1.74533"/>
339
+ </joint>
340
+
341
+ <transmission name="gripper_trans">
342
+ <type>transmission_interface/SimpleTransmission</type>
343
+ <joint name="gripper">
344
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
345
+ </joint>
346
+ <actuator name="motor6">
347
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
348
+ <mechanicalReduction>1</mechanicalReduction>
349
+ </actuator>
350
+ </transmission>
351
+
352
+ <!-- Joint from wrist to gripper -->
353
+ <joint name="wrist_roll" type="revolute">
354
+ <origin xyz="5.55112e-17 -0.0611 0.0181" rpy="1.5708 0.0486795 3.14159"/>
355
+ <parent link="wrist_link"/>
356
+ <child link="gripper_link"/>
357
+ <axis xyz="0 0 1"/>
358
+ <limit effort="10" velocity="10" lower="-2.74385" upper="2.84121"/>
359
+ </joint>
360
+
361
+ <transmission name="wrist_roll_trans">
362
+ <type>transmission_interface/SimpleTransmission</type>
363
+ <joint name="wrist_roll">
364
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
365
+ </joint>
366
+ <actuator name="motor5">
367
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
368
+ <mechanicalReduction>1</mechanicalReduction>
369
+ </actuator>
370
+ </transmission>
371
+
372
+ <!-- Joint from lower_arm to wrist -->
373
+ <joint name="wrist_flex" type="revolute">
374
+ <origin xyz="-0.1349 0.0052 3.62355e-17" rpy="4.02456e-15 8.67362e-16 -1.5708"/>
375
+ <parent link="lower_arm_link"/>
376
+ <child link="wrist_link"/>
377
+ <axis xyz="0 0 1"/>
378
+ <limit effort="10" velocity="10" lower="-1.65806" upper="1.65806"/>
379
+ </joint>
380
+
381
+ <transmission name="wrist_flex_trans">
382
+ <type>transmission_interface/SimpleTransmission</type>
383
+ <joint name="wrist_flex">
384
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
385
+ </joint>
386
+ <actuator name="motor4">
387
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
388
+ <mechanicalReduction>1</mechanicalReduction>
389
+ </actuator>
390
+ </transmission>
391
+
392
+ <!-- Joint from upper_arm to lower_arm -->
393
+ <!-- Note: 5-degree calibration offset applied to joint limits -->
394
+ <joint name="elbow_flex" type="revolute">
395
+ <origin xyz="-0.11257 -0.028 1.73763e-16" rpy="-3.63608e-16 8.74301e-16 1.5708"/>
396
+ <parent link="upper_arm_link"/>
397
+ <child link="lower_arm_link"/>
398
+ <axis xyz="0 0 1"/>
399
+ <limit effort="10" velocity="10" lower="-1.69" upper="1.69"/>
400
+ </joint>
401
+
402
+ <transmission name="elbow_flex_trans">
403
+ <type>transmission_interface/SimpleTransmission</type>
404
+ <joint name="elbow_flex">
405
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
406
+ </joint>
407
+ <actuator name="motor3">
408
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
409
+ <mechanicalReduction>1</mechanicalReduction>
410
+ </actuator>
411
+ </transmission>
412
+
413
+ <!-- Joint from shoulder to upper_arm -->
414
+ <joint name="shoulder_lift" type="revolute">
415
+ <origin xyz="-0.0303992 -0.0182778 -0.0542" rpy="-1.5708 -1.5708 0"/>
416
+ <parent link="shoulder_link"/>
417
+ <child link="upper_arm_link"/>
418
+ <axis xyz="0 0 1"/>
419
+ <limit effort="10" velocity="10" lower="-1.74533" upper="1.74533"/>
420
+ </joint>
421
+
422
+ <transmission name="shoulder_lift_trans">
423
+ <type>transmission_interface/SimpleTransmission</type>
424
+ <joint name="shoulder_lift">
425
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
426
+ </joint>
427
+ <actuator name="motor2">
428
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
429
+ <mechanicalReduction>1</mechanicalReduction>
430
+ </actuator>
431
+ </transmission>
432
+
433
+ <!-- Joint from base to shoulder -->
434
+ <joint name="shoulder_pan" type="revolute">
435
+ <origin xyz="0.0388353 -8.97657e-09 0.0624" rpy="3.14159 4.18253e-17 -3.14159"/>
436
+ <parent link="base_link"/>
437
+ <child link="shoulder_link"/>
438
+ <axis xyz="0 0 1"/>
439
+ <limit effort="10" velocity="10" lower="-1.91986" upper="1.91986"/>
440
+ </joint>
441
+
442
+ <transmission name="shoulder_pan_trans">
443
+ <type>transmission_interface/SimpleTransmission</type>
444
+ <joint name="shoulder_pan">
445
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
446
+ </joint>
447
+ <actuator name="motor1">
448
+ <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
449
+ <mechanicalReduction>1</mechanicalReduction>
450
+ </actuator>
451
+ </transmission>
452
+
453
+ </robot>
tinyvla2/configs/ablation_nolatent.yaml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Stage-2 gate baseline: same mixture, 5k steps, nolatent ablation
2
+ output_dir: /home/alexw/tinyvla/outputs/ablation_nolatent
3
+ wandb: null
4
+ datasets:
5
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
6
+ weight: 0.65
7
+ - repo_id: nvidia/BridgeData2_LeRobot_v3
8
+ root: /home/alexw/tinyvla_data/bridge_v3_10k
9
+ revision: main
10
+ episodes: 10000
11
+ weight: 0.35
12
+ policy:
13
+ chunk_size: 50
14
+ n_action_steps: 50
15
+ image_size: 256
16
+ use_semantic_latent: false
17
+ batch_size: 64
18
+ grad_accum: 2
19
+ num_workers: 12
20
+ lr: 1.0e-4
21
+ warmup_steps: 500
22
+ steps: 5000
23
+ log_freq: 50
24
+ save_freq: 2500
25
+ mixed_precision: bf16
tinyvla2/configs/ablation_nospatial.yaml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Stage-2 gate baseline: same mixture, 5k steps, nospatial ablation
2
+ output_dir: /home/alexw/tinyvla/outputs/ablation_nospatial
3
+ wandb: null
4
+ datasets:
5
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
6
+ weight: 0.65
7
+ - repo_id: nvidia/BridgeData2_LeRobot_v3
8
+ root: /home/alexw/tinyvla_data/bridge_v3_10k
9
+ revision: main
10
+ episodes: 10000
11
+ weight: 0.35
12
+ policy:
13
+ chunk_size: 50
14
+ n_action_steps: 50
15
+ image_size: 256
16
+ use_spatial_tokens: false
17
+ batch_size: 64
18
+ grad_accum: 2
19
+ num_workers: 12
20
+ lr: 1.0e-4
21
+ warmup_steps: 500
22
+ steps: 5000
23
+ log_freq: 50
24
+ save_freq: 2500
25
+ mixed_precision: bf16
tinyvla2/configs/libero_ft.yaml ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # LIBERO fine-tune from the Stage-2 pretrained checkpoint (XS-VLA/SmolVLA protocol).
2
+ # LIBERO = new embodiment (id 2): Franka 7-DoF EE @10fps, two 256^2 cameras.
3
+
4
+ output_dir: /home/alexw/tinyvla/outputs/libero_ft
5
+ wandb: null
6
+
7
+ datasets:
8
+ - repo_id: HuggingFaceVLA/libero
9
+ weight: 1.0
10
+ embodiment_id: 2
11
+
12
+ policy:
13
+ chunk_size: 50
14
+ n_action_steps: 50
15
+ image_size: 256
16
+ freeze_lm: true
17
+ freeze_vision_encoder: true
18
+
19
+ resume_from: /home/alexw/tinyvla/outputs/stage2_mixture/final
20
+ resume_step: 0
21
+
22
+ batch_size: 64
23
+ grad_accum: 1
24
+ num_workers: 12
25
+ lr: 5.0e-5
26
+ warmup_steps: 500
27
+ steps: 15000
28
+ grad_clip: 10.0
29
+ log_freq: 50
30
+ save_freq: 2500
31
+ mixed_precision: bf16
tinyvla2/configs/libero_ft2.yaml ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # LIBERO fine-tune v2: unfreeze the vision tower (sim renders are OOD for the
2
+ # real-world-pretrained encoder), longer schedule.
3
+
4
+ output_dir: /home/alexw/tinyvla/outputs/libero_ft2
5
+ wandb: null
6
+
7
+ datasets:
8
+ - repo_id: HuggingFaceVLA/libero
9
+ weight: 1.0
10
+ embodiment_id: 2
11
+
12
+ policy:
13
+ chunk_size: 50
14
+ n_action_steps: 50
15
+ image_size: 256
16
+ freeze_lm: true
17
+ freeze_vision_encoder: false
18
+
19
+ backbone_lr_mult: 0.1
20
+
21
+ resume_from: /home/alexw/tinyvla/outputs/stage2_mixture/final
22
+ resume_step: 0
23
+
24
+ batch_size: 48
25
+ grad_accum: 1
26
+ num_workers: 12
27
+ lr: 1.0e-4
28
+ warmup_steps: 1000
29
+ steps: 30000
30
+ grad_clip: 10.0
31
+ log_freq: 50
32
+ save_freq: 5000
33
+ mixed_precision: bf16
tinyvla2/configs/libero_ft3.yaml ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # LIBERO fine-tune v2: unfreeze the vision tower (sim renders are OOD for the
2
+ # real-world-pretrained encoder), longer schedule.
3
+
4
+ output_dir: /home/alexw/tinyvla/outputs/libero_ft3
5
+ wandb: null
6
+
7
+ datasets:
8
+ - repo_id: HuggingFaceVLA/libero
9
+ weight: 1.0
10
+ embodiment_id: 2
11
+
12
+ policy:
13
+ chunk_size: 50
14
+ n_action_steps: 50
15
+ image_size: 256
16
+ freeze_lm: true
17
+ freeze_vision_encoder: false
18
+
19
+ backbone_lr_mult: 0.1
20
+
21
+ resume_from: /home/alexw/tinyvla/outputs/stage3_distill/final
22
+ resume_step: 0
23
+
24
+ batch_size: 48
25
+ grad_accum: 1
26
+ num_workers: 12
27
+ lr: 1.0e-4
28
+ warmup_steps: 1000
29
+ steps: 30000
30
+ grad_clip: 10.0
31
+ log_freq: 50
32
+ save_freq: 5000
33
+ mixed_precision: bf16
tinyvla2/configs/libero_ft4.yaml ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # LIBERO fine-tune v4: unfreeze BOTH vision tower and language stack
2
+ # (12 truncated layers; vocab embeddings stay frozen) at 0.1x lr, longer schedule.
3
+
4
+ output_dir: /home/alexw/tinyvla/outputs/libero_ft4
5
+ wandb: null
6
+
7
+ datasets:
8
+ - repo_id: HuggingFaceVLA/libero
9
+ weight: 1.0
10
+ embodiment_id: 2
11
+
12
+ policy:
13
+ chunk_size: 50
14
+ n_action_steps: 50
15
+ image_size: 256
16
+ freeze_lm: false
17
+ freeze_vision_encoder: false
18
+
19
+ backbone_lr_mult: 0.1
20
+
21
+ resume_from: /home/alexw/tinyvla/outputs/stage3_distill/final
22
+ resume_step: 0
23
+
24
+ batch_size: 48
25
+ grad_accum: 1
26
+ num_workers: 12
27
+ lr: 1.0e-4
28
+ warmup_steps: 1000
29
+ steps: 40000
30
+ grad_clip: 10.0
31
+ log_freq: 50
32
+ save_freq: 5000
33
+ mixed_precision: bf16
tinyvla2/configs/morphology/descriptors.yaml ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Per-embodiment morphology descriptors (variant C).
2
+ # Fields hand-normalized to O(1); order MUST match MORPH_FIELDS in
3
+ # src/tinyvla/modules/embodiment.py. Values: raw → normalized applied at load.
4
+ # arm_dof/10, reach_m*2, gripper_width_m*10, num_cameras/3, is_mobile ±1,
5
+ # control_hz/30, joint_lo_mean/pi, joint_hi_mean/pi, workspace_xyz*2,
6
+ # payload_kg/5, ee_type_parallel ±1, ee_type_multi ±1, base_holonomic ±1, reserved.
7
+
8
+ so101: # SO-100/101 5-DOF + gripper
9
+ arm_dof: 5
10
+ reach_m: 0.35
11
+ gripper_width_m: 0.03
12
+ num_cameras: 2
13
+ is_mobile: -1
14
+ control_hz: 30
15
+ joint_lo_mean: -1.9
16
+ joint_hi_mean: 1.9
17
+ workspace_x: 0.30
18
+ workspace_y: 0.30
19
+ workspace_z: 0.25
20
+ payload_kg: 0.25
21
+ ee_type_parallel: 1
22
+ ee_type_multi: -1
23
+ base_holonomic: -1
24
+ reserved: 0
25
+
26
+ bridge: # WidowX 250 6-DOF
27
+ arm_dof: 6
28
+ reach_m: 0.60
29
+ gripper_width_m: 0.06
30
+ num_cameras: 1
31
+ is_mobile: -1
32
+ control_hz: 5
33
+ joint_lo_mean: -2.6
34
+ joint_hi_mean: 2.6
35
+ workspace_x: 0.40
36
+ workspace_y: 0.40
37
+ workspace_z: 0.35
38
+ payload_kg: 0.25
39
+ ee_type_parallel: 1
40
+ ee_type_multi: -1
41
+ base_holonomic: -1
42
+ reserved: 0
43
+
44
+ rt1: # Google robot (mobile manipulator, 7-DOF arm)
45
+ arm_dof: 7
46
+ reach_m: 0.90
47
+ gripper_width_m: 0.08
48
+ num_cameras: 1
49
+ is_mobile: 1
50
+ control_hz: 3
51
+ joint_lo_mean: -2.9
52
+ joint_hi_mean: 2.9
53
+ workspace_x: 0.70
54
+ workspace_y: 0.70
55
+ workspace_z: 0.60
56
+ payload_kg: 1.0
57
+ ee_type_parallel: 1
58
+ ee_type_multi: -1
59
+ base_holonomic: 1
60
+ reserved: 0
61
+
62
+ droid: # Franka Panda 7-DOF
63
+ arm_dof: 7
64
+ reach_m: 0.85
65
+ gripper_width_m: 0.08
66
+ num_cameras: 2
67
+ is_mobile: -1
68
+ control_hz: 15
69
+ joint_lo_mean: -2.8
70
+ joint_hi_mean: 2.8
71
+ workspace_x: 0.60
72
+ workspace_y: 0.60
73
+ workspace_z: 0.55
74
+ payload_kg: 3.0
75
+ ee_type_parallel: 1
76
+ ee_type_multi: -1
77
+ base_holonomic: -1
78
+ reserved: 0
79
+
80
+ libero: # Franka Panda (sim) — same morphology as droid
81
+ arm_dof: 7
82
+ reach_m: 0.85
83
+ gripper_width_m: 0.08
84
+ num_cameras: 2
85
+ is_mobile: -1
86
+ control_hz: 20
87
+ joint_lo_mean: -2.8
88
+ joint_hi_mean: 2.8
89
+ workspace_x: 0.60
90
+ workspace_y: 0.60
91
+ workspace_z: 0.55
92
+ payload_kg: 3.0
93
+ ee_type_parallel: 1
94
+ ee_type_multi: -1
95
+ base_holonomic: -1
96
+ reserved: 0
97
+
98
+ # --- held-out (never in pretraining; descriptors ready for zero-shot variant C) ---
99
+ jaco: # Kinova Jaco 6-DOF, 3-finger gripper
100
+ arm_dof: 6
101
+ reach_m: 0.90
102
+ gripper_width_m: 0.10
103
+ num_cameras: 1
104
+ is_mobile: -1
105
+ control_hz: 10
106
+ joint_lo_mean: -3.1
107
+ joint_hi_mean: 3.1
108
+ workspace_x: 0.60
109
+ workspace_y: 0.60
110
+ workspace_z: 0.55
111
+ payload_kg: 1.6
112
+ ee_type_parallel: -1
113
+ ee_type_multi: 1
114
+ base_holonomic: -1
115
+ reserved: 0
116
+
117
+ fanuc: # Fanuc Mate 6-DOF industrial
118
+ arm_dof: 6
119
+ reach_m: 0.70
120
+ gripper_width_m: 0.05
121
+ num_cameras: 1
122
+ is_mobile: -1
123
+ control_hz: 10
124
+ joint_lo_mean: -3.1
125
+ joint_hi_mean: 3.1
126
+ workspace_x: 0.50
127
+ workspace_y: 0.50
128
+ workspace_z: 0.45
129
+ payload_kg: 4.0
130
+ ee_type_parallel: 1
131
+ ee_type_multi: -1
132
+ base_holonomic: -1
133
+ reserved: 0
134
+
135
+ lekiwi: # LeKiwi — SO-100 (5-DOF) arm on a 3-wheel holonomic kiwi base (mobile manipulator)
136
+ arm_dof: 5
137
+ reach_m: 0.35
138
+ gripper_width_m: 0.03
139
+ num_cameras: 2
140
+ is_mobile: 1
141
+ control_hz: 30
142
+ joint_lo_mean: -1.9
143
+ joint_hi_mean: 1.9
144
+ workspace_x: 1.5
145
+ workspace_y: 1.5
146
+ workspace_z: 0.30
147
+ payload_kg: 0.25
148
+ ee_type_parallel: 1
149
+ ee_type_multi: -1
150
+ base_holonomic: 1
151
+ reserved: 0
152
+
153
+ ur5: # Universal Robots UR5e — 6-DOF industrial arm, fixed base
154
+ arm_dof: 6
155
+ reach_m: 0.85
156
+ gripper_width_m: 0.085
157
+ num_cameras: 1
158
+ is_mobile: -1
159
+ control_hz: 5
160
+ joint_lo_mean: -3.1
161
+ joint_hi_mean: 3.1
162
+ workspace_x: 0.70
163
+ workspace_y: 0.70
164
+ workspace_z: 0.60
165
+ payload_kg: 5.0
166
+ ee_type_parallel: 1
167
+ ee_type_multi: -1
168
+ base_holonomic: -1
169
+ reserved: 0
170
+
171
+ xarm: # UFactory xArm — 6-DOF arm, fixed base
172
+ arm_dof: 6
173
+ reach_m: 0.70
174
+ gripper_width_m: 0.08
175
+ num_cameras: 1
176
+ is_mobile: -1
177
+ control_hz: 5
178
+ joint_lo_mean: -3.1
179
+ joint_hi_mean: 3.1
180
+ workspace_x: 0.55
181
+ workspace_y: 0.55
182
+ workspace_z: 0.50
183
+ payload_kg: 3.5
184
+ ee_type_parallel: 1
185
+ ee_type_multi: -1
186
+ base_holonomic: -1
187
+ reserved: 0
188
+
189
+ dlr_edan: # DLR EDAN — 7-DOF assistive arm on a wheelchair (mobile assistive)
190
+ arm_dof: 7
191
+ reach_m: 0.80
192
+ gripper_width_m: 0.07
193
+ num_cameras: 1
194
+ is_mobile: 1
195
+ control_hz: 5
196
+ joint_lo_mean: -2.9
197
+ joint_hi_mean: 2.9
198
+ workspace_x: 0.65
199
+ workspace_y: 0.65
200
+ workspace_z: 0.55
201
+ payload_kg: 2.0
202
+ ee_type_parallel: 1
203
+ ee_type_multi: -1
204
+ base_holonomic: -1
205
+ reserved: 0
206
+
207
+ tidybot: # holonomic wheeled mobile manipulator (Kinova arm on omni base), whole-body EE control
208
+ arm_dof: 7
209
+ reach_m: 0.90
210
+ gripper_width_m: 0.08
211
+ num_cameras: 2
212
+ is_mobile: 1
213
+ control_hz: 10
214
+ joint_lo_mean: -2.9
215
+ joint_hi_mean: 2.9
216
+ workspace_x: 2.0
217
+ workspace_y: 2.0
218
+ workspace_z: 1.0
219
+ payload_kg: 1.0
220
+ ee_type_parallel: 1
221
+ ee_type_multi: -1
222
+ base_holonomic: 1
223
+ reserved: 0
224
+
225
+ uav: # quadrotor drone — free-flying 6-DOF body, no arm, no gripper (max morphology gap)
226
+ arm_dof: 0
227
+ reach_m: 0.0
228
+ gripper_width_m: 0.0
229
+ num_cameras: 6
230
+ is_mobile: 1
231
+ control_hz: 30
232
+ joint_lo_mean: 0.0
233
+ joint_hi_mean: 0.0
234
+ workspace_x: 5.0
235
+ workspace_y: 5.0
236
+ workspace_z: 5.0
237
+ payload_kg: 0.5
238
+ ee_type_parallel: -1
239
+ ee_type_multi: -1
240
+ base_holonomic: 1
241
+ reserved: 1
242
+
243
+ hello_stretch: # Hello Robot Stretch — mobile base + prismatic lift
244
+ arm_dof: 4
245
+ reach_m: 0.52
246
+ gripper_width_m: 0.08
247
+ num_cameras: 1
248
+ is_mobile: 1
249
+ control_hz: 10
250
+ joint_lo_mean: -1.5
251
+ joint_hi_mean: 1.5
252
+ workspace_x: 0.50
253
+ workspace_y: 0.30
254
+ workspace_z: 1.10
255
+ payload_kg: 1.5
256
+ ee_type_parallel: 1
257
+ ee_type_multi: -1
258
+ base_holonomic: -1
259
+ reserved: 0
260
+
261
+ panda_omron: # RoboCasa PandaOmron — Franka 7-DOF arm on a holonomic omni mobile base (kitchen scenes)
262
+ arm_dof: 7
263
+ reach_m: 0.85
264
+ gripper_width_m: 0.08
265
+ num_cameras: 3
266
+ is_mobile: 1
267
+ control_hz: 20
268
+ joint_lo_mean: -2.8
269
+ joint_hi_mean: 2.8
270
+ workspace_x: 2.0
271
+ workspace_y: 2.0
272
+ workspace_z: 1.2
273
+ payload_kg: 3.0
274
+ ee_type_parallel: 1
275
+ ee_type_multi: -1
276
+ base_holonomic: 1
277
+ reserved: 0
tinyvla2/configs/morphology/robot_prompts.yaml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Natural-language robot descriptions prepended to the task instruction (Qwen slow path).
2
+ # Complements the numeric morphology descriptor (C scheme) with a text channel.
3
+ so101: "Robot: SO-101, a 5-DOF tabletop arm with a parallel-jaw gripper on a fixed base."
4
+ bridge: "Robot: WidowX 250, a 6-DOF tabletop arm with a parallel gripper on a fixed base."
5
+ rt1: "Robot: Google Robot, a 7-DOF arm mounted on a wheeled mobile base."
6
+ ur5: "Robot: UR5e, a 6-DOF industrial arm with a parallel gripper on a fixed base."
7
+ xarm: "Robot: xArm, a 6-DOF arm with a parallel gripper on a fixed base."
8
+ dlr_edan: "Robot: DLR EDAN, a 7-DOF assistive arm mounted on a wheelchair mobile base."
9
+ tidybot: "Robot: TidyBot, a 7-DOF arm on a holonomic omnidirectional wheeled base; whole-body end-effector control."
10
+ hello_stretch: "Robot: Hello Stretch, an arm on a telescoping prismatic lift atop a mobile base."
11
+ jaco: "Robot: Kinova Jaco, a 6-DOF arm with a three-finger gripper on a fixed base."
12
+ lekiwi: "Robot: LeKiwi, a 5-DOF SO-100 arm mounted on a three-wheel holonomic mobile base."
13
+ panda_omron: "Robot: RoboCasa PandaOmron, a 7-DOF Franka arm on a holonomic omnidirectional mobile base, operating in kitchen scenes."
tinyvla2/configs/stage2_mixture.yaml ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Stage 2: small-scale mixture pretraining on one A6000.
2
+ # SO-100/101 community slice (converted v3.0) 0.65 + Bridge V2 0.35.
3
+ # Fractal dropped for MVP (v2.0, unconvertible in lerobot 0.6.1).
4
+
5
+ output_dir: /home/alexw/tinyvla/outputs/stage2_mixture
6
+ wandb: null # set a project name to enable
7
+
8
+ datasets:
9
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
10
+ weight: 0.65
11
+ - repo_id: nvidia/BridgeData2_LeRobot_v3
12
+ root: /home/alexw/tinyvla_data/bridge_v3_10k
13
+ revision: main
14
+ episodes: 10000
15
+ weight: 0.35
16
+
17
+ policy:
18
+ chunk_size: 50
19
+ n_action_steps: 50
20
+ image_size: 256
21
+ freeze_lm: true
22
+ freeze_vision_encoder: true
23
+
24
+ batch_size: 64
25
+ grad_accum: 2
26
+ num_workers: 14
27
+ resume_from: /home/alexw/tinyvla/outputs/stage2_mixture/step_2000
28
+ resume_step: 2000
29
+ lr: 1.0e-4
30
+ warmup_steps: 1000
31
+ steps: 40000
32
+ grad_clip: 10.0
33
+ log_freq: 50
34
+ save_freq: 2000
35
+ mixed_precision: bf16
36
+ staleness_start_step: 20000
37
+ staleness_prob: 0.5
tinyvla2/configs/stage3_distill.yaml ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Stage 3: continue SO101+Bridge pretraining with the spatial-distillation
2
+ # aux CE loss (teacher labels from Qwen3.5-4B) and staleness aug from the start.
3
+
4
+ output_dir: /home/alexw/tinyvla/outputs/stage3_distill
5
+ wandb: null
6
+
7
+ spatial_labels_dir: /home/alexw/tinyvla_data/spatial_labels
8
+
9
+ datasets:
10
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
11
+ weight: 0.65
12
+ - repo_id: nvidia/BridgeData2_LeRobot_v3
13
+ root: /home/alexw/tinyvla_data/bridge_v3_10k
14
+ revision: main
15
+ episodes: 10000
16
+ weight: 0.35
17
+
18
+ policy:
19
+ chunk_size: 50
20
+ n_action_steps: 50
21
+ image_size: 256
22
+ freeze_lm: true
23
+ freeze_vision_encoder: true
24
+ spatial_loss_weight: 0.15
25
+
26
+ resume_from: /home/alexw/tinyvla/outputs/stage2_mixture/final
27
+ resume_step: 0
28
+
29
+ batch_size: 64
30
+ grad_accum: 2
31
+ num_workers: 14
32
+ lr: 5.0e-5
33
+ warmup_steps: 500
34
+ steps: 20000
35
+ grad_clip: 10.0
36
+ log_freq: 50
37
+ save_freq: 2500
38
+ mixed_precision: bf16
39
+ staleness_start_step: 0
40
+ staleness_prob: 0.5
tinyvla2/configs/tv2_A.yaml ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # TinyVLA-2 variant A: conditioning=id, action_space=native
2
+ # Identical data/weights/seed across A/B/C (matched-pairs design).
3
+ output_dir: /home/alexw/tinyvla/outputs/tv2_A_id_native
4
+ wandb: null
5
+ seed: 42
6
+ morphology_descriptors: /home/alexw/tinyvla/configs/morphology/descriptors.yaml
7
+
8
+ datasets:
9
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
10
+ weight: 0.40
11
+ morph_key: so101
12
+ - repo_id: MolmoAct2-Bridge-Dataset
13
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
14
+ episodes: 20000
15
+ weight: 0.30
16
+ morph_key: bridge
17
+ - repo_id: MolmoAct2-RT-1-Dataset
18
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
19
+ episodes: 20000
20
+ weight: 0.30
21
+ morph_key: rt1
22
+
23
+ policy:
24
+ chunk_size: 50
25
+ n_action_steps: 50
26
+ image_size: 256
27
+ freeze_lm: true
28
+ freeze_vision_encoder: true
29
+ num_embodiments: 16
30
+ conditioning: id
31
+ action_space: native
32
+
33
+ batch_size: 64
34
+ grad_accum: 2
35
+ num_workers: 12
36
+ lr: 1.0e-4
37
+ warmup_steps: 1000
38
+ steps: 40000
39
+ grad_clip: 10.0
40
+ log_freq: 50
41
+ save_freq: 5000
42
+ mixed_precision: bf16
tinyvla2/configs/tv2_B.yaml ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # TinyVLA-2 variant B: conditioning=id, action_space=canonical
2
+ # Identical data/weights/seed across A/B/C (matched-pairs design).
3
+ output_dir: /home/alexw/tinyvla/outputs/tv2_B_id_canon
4
+ wandb: null
5
+ seed: 42
6
+ morphology_descriptors: /home/alexw/tinyvla/configs/morphology/descriptors.yaml
7
+
8
+ datasets:
9
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
10
+ weight: 0.40
11
+ morph_key: so101
12
+ - repo_id: MolmoAct2-Bridge-Dataset
13
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
14
+ episodes: 20000
15
+ weight: 0.30
16
+ morph_key: bridge
17
+ - repo_id: MolmoAct2-RT-1-Dataset
18
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
19
+ episodes: 20000
20
+ weight: 0.30
21
+ morph_key: rt1
22
+
23
+ policy:
24
+ chunk_size: 50
25
+ n_action_steps: 50
26
+ image_size: 256
27
+ freeze_lm: true
28
+ freeze_vision_encoder: true
29
+ num_embodiments: 16
30
+ conditioning: id
31
+ action_space: canonical
32
+
33
+ batch_size: 64
34
+ grad_accum: 2
35
+ num_workers: 12
36
+ lr: 1.0e-4
37
+ warmup_steps: 1000
38
+ steps: 40000
39
+ grad_clip: 10.0
40
+ log_freq: 50
41
+ save_freq: 5000
42
+ mixed_precision: bf16
tinyvla2/configs/tv2_C.yaml ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # TinyVLA-2 variant C: conditioning=morph, action_space=canonical
2
+ # Identical data/weights/seed across A/B/C (matched-pairs design).
3
+ output_dir: /home/alexw/tinyvla/outputs/tv2_C_morph_canon
4
+ wandb: null
5
+ seed: 42
6
+ morphology_descriptors: /home/alexw/tinyvla/configs/morphology/descriptors.yaml
7
+
8
+ datasets:
9
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
10
+ weight: 0.40
11
+ morph_key: so101
12
+ - repo_id: MolmoAct2-Bridge-Dataset
13
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
14
+ episodes: 20000
15
+ weight: 0.30
16
+ morph_key: bridge
17
+ - repo_id: MolmoAct2-RT-1-Dataset
18
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
19
+ episodes: 20000
20
+ weight: 0.30
21
+ morph_key: rt1
22
+
23
+ policy:
24
+ chunk_size: 50
25
+ n_action_steps: 50
26
+ image_size: 256
27
+ freeze_lm: true
28
+ freeze_vision_encoder: true
29
+ num_embodiments: 16
30
+ conditioning: morph
31
+ action_space: canonical
32
+
33
+ batch_size: 64
34
+ grad_accum: 2
35
+ num_workers: 12
36
+ lr: 1.0e-4
37
+ warmup_steps: 1000
38
+ steps: 40000
39
+ grad_clip: 10.0
40
+ log_freq: 50
41
+ save_freq: 5000
42
+ mixed_precision: bf16
tinyvla2/configs/tv2_C_diverse.yaml ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Continue-train variant C on an EXPANDED, morphology-diverse mixture to fix the
2
+ # OOD descriptor extrapolation (original C's morph-encoder saw only 3 arm-ish
3
+ # embodiments → failed zero-shot on mobile TidyBot). Now 8 embodiments spanning
4
+ # fixed arms (SO101, WidowX, UR5, xArm) + mobile (Google-robot, dlr_edan wheelchair,
5
+ # TidyBot holonomic, Stretch). jaco held out entirely for the transfer test.
6
+ output_dir: /home/alexw/tinyvla/outputs/tv2_C_diverse
7
+ wandb: null
8
+ seed: 42
9
+ morphology_descriptors: /home/alexw/tinyvla/configs/morphology/descriptors.yaml
10
+ resume_from: /home/alexw/tinyvla/outputs/tv2_C_morph_canon/final
11
+ resume_step: 0
12
+
13
+ datasets:
14
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
15
+ weight: 0.25
16
+ morph_key: so101
17
+ - repo_id: MolmoAct2-Bridge-Dataset
18
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
19
+ episodes: 15000
20
+ weight: 0.18
21
+ morph_key: bridge
22
+ - repo_id: MolmoAct2-RT-1-Dataset
23
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
24
+ episodes: 15000
25
+ weight: 0.18
26
+ morph_key: rt1
27
+ - repo_id: div_ur5
28
+ root: /home/alexw/tinyvla_data/diverse/div_ur5
29
+ weight: 0.10
30
+ morph_key: ur5
31
+ - repo_id: div_xarm
32
+ root: /home/alexw/tinyvla_data/diverse/div_xarm
33
+ weight: 0.09
34
+ morph_key: xarm
35
+ - repo_id: div_dlr_edan
36
+ root: /home/alexw/tinyvla_data/diverse/div_dlr_edan
37
+ weight: 0.05
38
+ morph_key: dlr_edan
39
+ - repo_id: heldout_tidybot
40
+ root: /home/alexw/tinyvla_data/heldout/heldout_tidybot
41
+ weight: 0.08
42
+ morph_key: tidybot
43
+ - repo_id: heldout_cmu_stretch
44
+ root: /home/alexw/tinyvla_data/heldout/heldout_cmu_stretch
45
+ weight: 0.07
46
+ morph_key: hello_stretch
47
+
48
+ policy:
49
+ chunk_size: 50
50
+ n_action_steps: 50
51
+ image_size: 256
52
+ freeze_lm: true
53
+ freeze_vision_encoder: true
54
+ num_embodiments: 16
55
+ conditioning: morph
56
+ action_space: canonical
57
+
58
+ batch_size: 64
59
+ grad_accum: 2
60
+ num_workers: 12
61
+ lr: 7.0e-5
62
+ warmup_steps: 500
63
+ steps: 25000
64
+ grad_clip: 10.0
65
+ log_freq: 50
66
+ save_freq: 5000
67
+ mixed_precision: bf16
tinyvla2/configs/tv2_C_diverse_slow.yaml ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Continue-train variant C on an EXPANDED, morphology-diverse mixture to fix the
2
+ # OOD descriptor extrapolation (original C's morph-encoder saw only 3 arm-ish
3
+ # embodiments → failed zero-shot on mobile TidyBot). Now 8 embodiments spanning
4
+ # fixed arms (SO101, WidowX, UR5, xArm) + mobile (Google-robot, dlr_edan wheelchair,
5
+ # TidyBot holonomic, Stretch). jaco held out entirely for the transfer test.
6
+ output_dir: /home/alexw/tinyvla/outputs/tv2_C_diverse_slow
7
+ wandb: null
8
+ seed: 42
9
+ morphology_descriptors: /home/alexw/tinyvla/configs/morphology/descriptors.yaml
10
+ resume_from: /home/alexw/tinyvla/outputs/tv2_C_diverse/final
11
+ resume_step: 0
12
+
13
+ datasets:
14
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
15
+ weight: 0.25
16
+ morph_key: so101
17
+ - repo_id: MolmoAct2-Bridge-Dataset
18
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
19
+ episodes: 15000
20
+ weight: 0.18
21
+ morph_key: bridge
22
+ - repo_id: MolmoAct2-RT-1-Dataset
23
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
24
+ episodes: 15000
25
+ weight: 0.18
26
+ morph_key: rt1
27
+ - repo_id: div_ur5
28
+ root: /home/alexw/tinyvla_data/diverse/div_ur5
29
+ weight: 0.10
30
+ morph_key: ur5
31
+ - repo_id: div_xarm
32
+ root: /home/alexw/tinyvla_data/diverse/div_xarm
33
+ weight: 0.09
34
+ morph_key: xarm
35
+ - repo_id: div_dlr_edan
36
+ root: /home/alexw/tinyvla_data/diverse/div_dlr_edan
37
+ weight: 0.05
38
+ morph_key: dlr_edan
39
+ - repo_id: heldout_tidybot
40
+ root: /home/alexw/tinyvla_data/heldout/heldout_tidybot
41
+ weight: 0.08
42
+ morph_key: tidybot
43
+ - repo_id: heldout_cmu_stretch
44
+ root: /home/alexw/tinyvla_data/heldout/heldout_cmu_stretch
45
+ weight: 0.07
46
+ morph_key: hello_stretch
47
+
48
+ policy:
49
+ chunk_size: 50
50
+ n_action_steps: 50
51
+ image_size: 256
52
+ freeze_lm: true
53
+ freeze_vision_encoder: true
54
+ num_embodiments: 16
55
+ conditioning: morph
56
+ morph_to_slow: true
57
+ action_space: canonical
58
+
59
+ batch_size: 64
60
+ grad_accum: 2
61
+ num_workers: 12
62
+ lr: 7.0e-5
63
+ warmup_steps: 500
64
+ steps: 25000
65
+ grad_clip: 10.0
66
+ log_freq: 50
67
+ save_freq: 5000
68
+ mixed_precision: bf16
tinyvla2/configs/tv2_C_incontext.yaml ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Variant: in-context few-shot DEMO conditioning — a few (obs, action) exemplar
2
+ # pairs from the target robot's own data, encoded via the shared spatial-aware
3
+ # vision encoder + a small MLP, WITHOUT any gradient update to adapt to that robot.
4
+ # Composed with the text-via-Qwen morphology channel (both active). Resumes from
5
+ # tv2_C_qwen_morph/final (LM already unfrozen, morph_out_proj already trained).
6
+ output_dir: /home/alexw/tinyvla/outputs/tv2_C_incontext
7
+ wandb: null
8
+ seed: 42
9
+ robot_prompts: /home/alexw/tinyvla/configs/morphology/robot_prompts.yaml
10
+ resume_from: /home/alexw/tinyvla/outputs/tv2_C_qwen_morph/final
11
+ resume_step: 0
12
+
13
+ datasets:
14
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
15
+ weight: 0.25
16
+ morph_key: so101
17
+ - repo_id: MolmoAct2-Bridge-Dataset
18
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
19
+ episodes: 15000
20
+ weight: 0.18
21
+ morph_key: bridge
22
+ - repo_id: MolmoAct2-RT-1-Dataset
23
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
24
+ episodes: 15000
25
+ weight: 0.18
26
+ morph_key: rt1
27
+ - repo_id: div_ur5
28
+ root: /home/alexw/tinyvla_data/diverse/div_ur5
29
+ weight: 0.10
30
+ morph_key: ur5
31
+ - repo_id: div_xarm
32
+ root: /home/alexw/tinyvla_data/diverse/div_xarm
33
+ weight: 0.09
34
+ morph_key: xarm
35
+ - repo_id: div_dlr_edan
36
+ root: /home/alexw/tinyvla_data/diverse/div_dlr_edan
37
+ weight: 0.05
38
+ morph_key: dlr_edan
39
+ - repo_id: heldout_tidybot
40
+ root: /home/alexw/tinyvla_data/heldout/heldout_tidybot
41
+ weight: 0.08
42
+ morph_key: tidybot
43
+ - repo_id: heldout_cmu_stretch
44
+ root: /home/alexw/tinyvla_data/heldout/heldout_cmu_stretch
45
+ weight: 0.07
46
+ morph_key: hello_stretch
47
+
48
+ policy:
49
+ chunk_size: 50
50
+ n_action_steps: 50
51
+ image_size: 256
52
+ freeze_lm: false
53
+ freeze_vision_encoder: true
54
+ num_embodiments: 16
55
+ conditioning: morph_qwen
56
+ num_morph_readout: 4
57
+ morph_text_max_len: 32
58
+ action_space: canonical
59
+ use_demo_conditioning: true
60
+ n_support: 3
61
+
62
+ backbone_lr_mult: 0.1
63
+ batch_size: 40
64
+ grad_accum: 2
65
+ num_workers: 12
66
+ lr: 7.0e-5
67
+ warmup_steps: 500
68
+ steps: 20000
69
+ grad_clip: 10.0
70
+ log_freq: 50
71
+ save_freq: 5000
72
+ mixed_precision: bf16
tinyvla2/configs/tv2_C_incontext_v2.yaml ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Variant: in-context few-shot DEMO conditioning — a few (obs, action) exemplar
2
+ # pairs from the target robot's own data, encoded via the shared spatial-aware
3
+ # vision encoder + a small MLP, WITHOUT any gradient update to adapt to that robot.
4
+ # Composed with the text-via-Qwen morphology channel (both active). Resumes from
5
+ # tv2_C_qwen_morph/final (LM already unfrozen, morph_out_proj already trained).
6
+ output_dir: /home/alexw/tinyvla/outputs/tv2_C_incontext_v2
7
+ wandb: null
8
+ seed: 42
9
+ robot_prompts: /home/alexw/tinyvla/configs/morphology/robot_prompts.yaml
10
+ resume_from: /home/alexw/tinyvla/outputs/tv2_C_qwen_morph/final
11
+ resume_step: 0
12
+
13
+ datasets:
14
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
15
+ weight: 0.25
16
+ morph_key: so101
17
+ - repo_id: MolmoAct2-Bridge-Dataset
18
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
19
+ episodes: 15000
20
+ weight: 0.18
21
+ morph_key: bridge
22
+ - repo_id: MolmoAct2-RT-1-Dataset
23
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
24
+ episodes: 15000
25
+ weight: 0.18
26
+ morph_key: rt1
27
+ - repo_id: div_ur5
28
+ root: /home/alexw/tinyvla_data/diverse/div_ur5
29
+ weight: 0.10
30
+ morph_key: ur5
31
+ - repo_id: div_xarm
32
+ root: /home/alexw/tinyvla_data/diverse/div_xarm
33
+ weight: 0.09
34
+ morph_key: xarm
35
+ - repo_id: div_dlr_edan
36
+ root: /home/alexw/tinyvla_data/diverse/div_dlr_edan
37
+ weight: 0.05
38
+ morph_key: dlr_edan
39
+ - repo_id: heldout_tidybot
40
+ root: /home/alexw/tinyvla_data/heldout/heldout_tidybot
41
+ weight: 0.08
42
+ morph_key: tidybot
43
+ - repo_id: heldout_cmu_stretch
44
+ root: /home/alexw/tinyvla_data/heldout/heldout_cmu_stretch
45
+ weight: 0.07
46
+ morph_key: hello_stretch
47
+
48
+ policy:
49
+ chunk_size: 50
50
+ n_action_steps: 50
51
+ image_size: 256
52
+ freeze_lm: false
53
+ freeze_vision_encoder: true
54
+ num_embodiments: 16
55
+ conditioning: morph_qwen
56
+ num_morph_readout: 4
57
+ morph_text_max_len: 32
58
+ action_space: canonical
59
+ use_demo_conditioning: true
60
+ n_support: 3
61
+ support_other_task: true
62
+
63
+ backbone_lr_mult: 0.1
64
+ batch_size: 40
65
+ grad_accum: 2
66
+ num_workers: 12
67
+ lr: 7.0e-5
68
+ warmup_steps: 500
69
+ steps: 20000
70
+ grad_clip: 10.0
71
+ log_freq: 50
72
+ save_freq: 5000
73
+ mixed_precision: bf16
tinyvla2/configs/tv2_C_incontext_v3.yaml ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Variant: in-context few-shot DEMO conditioning — a few (obs, action) exemplar
2
+ # pairs from the target robot's own data, encoded via the shared spatial-aware
3
+ # vision encoder + a small MLP, WITHOUT any gradient update to adapt to that robot.
4
+ # Composed with the text-via-Qwen morphology channel (both active). Resumes from
5
+ # tv2_C_qwen_morph/final (LM already unfrozen, morph_out_proj already trained).
6
+ output_dir: /home/alexw/tinyvla/outputs/tv2_C_incontext_v3
7
+ wandb: null
8
+ seed: 42
9
+ robot_prompts: /home/alexw/tinyvla/configs/morphology/robot_prompts.yaml
10
+ resume_from: /home/alexw/tinyvla/outputs/tv2_C_qwen_morph/final
11
+ resume_step: 0
12
+
13
+ datasets:
14
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
15
+ weight: 0.25
16
+ morph_key: so101
17
+ - repo_id: MolmoAct2-Bridge-Dataset
18
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
19
+ episodes: 15000
20
+ weight: 0.18
21
+ morph_key: bridge
22
+ - repo_id: MolmoAct2-RT-1-Dataset
23
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
24
+ episodes: 15000
25
+ weight: 0.18
26
+ morph_key: rt1
27
+ - repo_id: div_ur5
28
+ root: /home/alexw/tinyvla_data/diverse/div_ur5
29
+ weight: 0.10
30
+ morph_key: ur5
31
+ - repo_id: div_xarm
32
+ root: /home/alexw/tinyvla_data/diverse/div_xarm
33
+ weight: 0.09
34
+ morph_key: xarm
35
+ - repo_id: div_dlr_edan
36
+ root: /home/alexw/tinyvla_data/diverse/div_dlr_edan
37
+ weight: 0.05
38
+ morph_key: dlr_edan
39
+ - repo_id: heldout_tidybot
40
+ root: /home/alexw/tinyvla_data/heldout/heldout_tidybot
41
+ weight: 0.08
42
+ morph_key: tidybot
43
+ - repo_id: heldout_cmu_stretch
44
+ root: /home/alexw/tinyvla_data/heldout/heldout_cmu_stretch
45
+ weight: 0.07
46
+ morph_key: hello_stretch
47
+
48
+ policy:
49
+ chunk_size: 50
50
+ n_action_steps: 50
51
+ image_size: 256
52
+ freeze_lm: false
53
+ freeze_vision_encoder: true
54
+ num_embodiments: 16
55
+ conditioning: morph_qwen
56
+ num_morph_readout: 4
57
+ morph_text_max_len: 32
58
+ action_space: canonical
59
+ use_demo_conditioning: true
60
+ n_support: 3
61
+ support_other_task: true
62
+ demo_tokens_per_example: 4
63
+ demo_visual_groups: 4
64
+ demo_action_keys: 8
65
+ demo_hidden_mult: 4
66
+
67
+ backbone_lr_mult: 0.1
68
+ batch_size: 40
69
+ grad_accum: 2
70
+ num_workers: 12
71
+ lr: 7.0e-5
72
+ warmup_steps: 500
73
+ steps: 20000
74
+ grad_clip: 10.0
75
+ log_freq: 50
76
+ save_freq: 5000
77
+ mixed_precision: bf16
tinyvla2/configs/tv2_C_mega.yaml ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Continue-train C on the MAXIMALLY diverse robot set (9 embodiments) with BOTH
2
+ # channels describing the robot: numeric morphology descriptor (C scheme) AND a
3
+ # natural-language robot description in the Qwen prompt (slow path). Held out: LeKiwi
4
+ # (mobile SO-100 + kiwi base) — we have its zero-shot baseline (295mm) to compare.
5
+ output_dir: /home/alexw/tinyvla/outputs/tv2_C_mega
6
+ wandb: null
7
+ seed: 42
8
+ morphology_descriptors: /home/alexw/tinyvla/configs/morphology/descriptors.yaml
9
+ robot_prompts: /home/alexw/tinyvla/configs/morphology/robot_prompts.yaml
10
+ resume_from: /home/alexw/tinyvla/outputs/tv2_C_diverse/final
11
+ resume_step: 0
12
+
13
+ datasets:
14
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
15
+ weight: 0.20
16
+ morph_key: so101
17
+ - repo_id: MolmoAct2-Bridge-Dataset
18
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
19
+ episodes: 12000
20
+ weight: 0.15
21
+ morph_key: bridge
22
+ - repo_id: MolmoAct2-RT-1-Dataset
23
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
24
+ episodes: 12000
25
+ weight: 0.15
26
+ morph_key: rt1
27
+ - repo_id: div_ur5
28
+ root: /home/alexw/tinyvla_data/diverse/div_ur5
29
+ weight: 0.10
30
+ morph_key: ur5
31
+ - repo_id: div_xarm
32
+ root: /home/alexw/tinyvla_data/diverse/div_xarm
33
+ weight: 0.09
34
+ morph_key: xarm
35
+ - repo_id: div_dlr_edan
36
+ root: /home/alexw/tinyvla_data/diverse/div_dlr_edan
37
+ weight: 0.06
38
+ morph_key: dlr_edan
39
+ - repo_id: heldout_tidybot
40
+ root: /home/alexw/tinyvla_data/heldout/heldout_tidybot
41
+ weight: 0.09
42
+ morph_key: tidybot
43
+ - repo_id: heldout_cmu_stretch
44
+ root: /home/alexw/tinyvla_data/heldout/heldout_cmu_stretch
45
+ weight: 0.08
46
+ morph_key: hello_stretch
47
+ - repo_id: heldout_jaco_play
48
+ root: /home/alexw/tinyvla_data/heldout/heldout_jaco_play
49
+ weight: 0.08
50
+ morph_key: jaco
51
+
52
+ policy:
53
+ chunk_size: 50
54
+ n_action_steps: 50
55
+ image_size: 256
56
+ freeze_lm: true
57
+ freeze_vision_encoder: true
58
+ num_embodiments: 16
59
+ conditioning: morph
60
+ action_space: canonical
61
+
62
+ batch_size: 64
63
+ grad_accum: 2
64
+ num_workers: 12
65
+ lr: 7.0e-5
66
+ warmup_steps: 500
67
+ steps: 20000
68
+ grad_clip: 10.0
69
+ log_freq: 50
70
+ save_freq: 5000
71
+ mixed_precision: bf16
tinyvla2/configs/tv2_C_qwen_morph.yaml ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Variant: morphology conditioning via the SAME shared Qwen (text-only pass over a
2
+ # robot description, e.g. "Robot: LeKiwi, a 5-DOF SO-100 arm on a three-wheel
3
+ # holonomic mobile base."), replacing the from-scratch MLP-over-numbers encoder.
4
+ # ALSO unfreezes the LM (freeze_lm: false) — per user request, since the frozen LM
5
+ # was the likely reason the earlier text-prompt-in-instruction experiment failed to
6
+ # learn to use new text. Resumes from C-diverse (8 robots, our best checkpoint:
7
+ # 150mm on held-out jaco). LeKiwi held out again for direct comparison (prior
8
+ # baselines: C-diverse numeric-only zero-shot 295mm; C-mega text-in-prompt 522mm).
9
+ output_dir: /home/alexw/tinyvla/outputs/tv2_C_qwen_morph
10
+ wandb: null
11
+ seed: 42
12
+ robot_prompts: /home/alexw/tinyvla/configs/morphology/robot_prompts.yaml
13
+ resume_from: /home/alexw/tinyvla/outputs/tv2_C_diverse/final
14
+ resume_step: 0
15
+
16
+ datasets:
17
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
18
+ weight: 0.25
19
+ morph_key: so101
20
+ - repo_id: MolmoAct2-Bridge-Dataset
21
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
22
+ episodes: 15000
23
+ weight: 0.18
24
+ morph_key: bridge
25
+ - repo_id: MolmoAct2-RT-1-Dataset
26
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
27
+ episodes: 15000
28
+ weight: 0.18
29
+ morph_key: rt1
30
+ - repo_id: div_ur5
31
+ root: /home/alexw/tinyvla_data/diverse/div_ur5
32
+ weight: 0.10
33
+ morph_key: ur5
34
+ - repo_id: div_xarm
35
+ root: /home/alexw/tinyvla_data/diverse/div_xarm
36
+ weight: 0.09
37
+ morph_key: xarm
38
+ - repo_id: div_dlr_edan
39
+ root: /home/alexw/tinyvla_data/diverse/div_dlr_edan
40
+ weight: 0.05
41
+ morph_key: dlr_edan
42
+ - repo_id: heldout_tidybot
43
+ root: /home/alexw/tinyvla_data/heldout/heldout_tidybot
44
+ weight: 0.08
45
+ morph_key: tidybot
46
+ - repo_id: heldout_cmu_stretch
47
+ root: /home/alexw/tinyvla_data/heldout/heldout_cmu_stretch
48
+ weight: 0.07
49
+ morph_key: hello_stretch
50
+
51
+ policy:
52
+ chunk_size: 50
53
+ n_action_steps: 50
54
+ image_size: 256
55
+ freeze_lm: false
56
+ freeze_vision_encoder: true
57
+ num_embodiments: 16
58
+ conditioning: morph_qwen
59
+ num_morph_readout: 4
60
+ morph_text_max_len: 32
61
+ action_space: canonical
62
+
63
+ backbone_lr_mult: 0.1
64
+ batch_size: 48
65
+ grad_accum: 2
66
+ num_workers: 12
67
+ lr: 7.0e-5
68
+ warmup_steps: 500
69
+ steps: 20000
70
+ grad_clip: 10.0
71
+ log_freq: 50
72
+ save_freq: 5000
73
+ mixed_precision: bf16
tinyvla2/configs/tv2_C_scaled.yaml ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SCALED C: the setup that works (canonical actions + numeric morphology
2
+ # descriptor), given a real budget. The reality check showed the model barely beat
3
+ # the trivial baseline (mean ratio 0.99 in-training) because 40k+20k steps were
4
+ # spread across 8 morphologies with a frozen backbone. This run scales:
5
+ # data : full Bridge (53K eps) + full RT-1 (87K eps) instead of 12-15K subsets
6
+ # -> pool 4.4M -> ~9.1M frames
7
+ # steps: 60k (was 20k) at eff batch 128 -> 7.7M samples (~0.85 epoch)
8
+ # model: LM UNFROZEN at 0.1x lr (the single biggest lever in TinyVLA v1:
9
+ # frozen 60% -> unfrozen 86% on LIBERO)
10
+ # Held out: LeKiwi (mobile) + jaco stays in for training here, so held-out
11
+ # comparison uses LeKiwi; jaco numbers become in-distribution reference.
12
+ output_dir: /home/alexw/tinyvla/outputs/tv2_C_scaled
13
+ wandb: null
14
+ seed: 42
15
+ morphology_descriptors: /home/alexw/tinyvla/configs/morphology/descriptors.yaml
16
+ resume_from: /home/alexw/tinyvla/outputs/tv2_C_diverse/final
17
+ resume_step: 0
18
+
19
+ datasets:
20
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
21
+ weight: 0.22
22
+ morph_key: so101
23
+ - repo_id: MolmoAct2-Bridge-Dataset # FULL 53,192 eps (1.89M frames)
24
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
25
+ weight: 0.22
26
+ morph_key: bridge
27
+ - repo_id: MolmoAct2-RT-1-Dataset # FULL 87,212 eps (3.79M frames)
28
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
29
+ weight: 0.22
30
+ morph_key: rt1
31
+ - repo_id: div_ur5
32
+ root: /home/alexw/tinyvla_data/diverse/div_ur5
33
+ weight: 0.09
34
+ morph_key: ur5
35
+ - repo_id: div_xarm
36
+ root: /home/alexw/tinyvla_data/diverse/div_xarm
37
+ weight: 0.08
38
+ morph_key: xarm
39
+ - repo_id: div_dlr_edan
40
+ root: /home/alexw/tinyvla_data/diverse/div_dlr_edan
41
+ weight: 0.04
42
+ morph_key: dlr_edan
43
+ - repo_id: heldout_tidybot
44
+ root: /home/alexw/tinyvla_data/heldout/heldout_tidybot
45
+ weight: 0.05
46
+ morph_key: tidybot
47
+ - repo_id: heldout_cmu_stretch
48
+ root: /home/alexw/tinyvla_data/heldout/heldout_cmu_stretch
49
+ weight: 0.04
50
+ morph_key: hello_stretch
51
+ - repo_id: heldout_jaco_play
52
+ root: /home/alexw/tinyvla_data/heldout/heldout_jaco_play
53
+ weight: 0.04
54
+ morph_key: jaco
55
+
56
+ policy:
57
+ chunk_size: 50
58
+ n_action_steps: 50
59
+ image_size: 256
60
+ freeze_lm: false # the biggest known lever
61
+ freeze_vision_encoder: true
62
+ num_embodiments: 16
63
+ conditioning: morph # numeric descriptor: the channel that actually works
64
+ action_space: canonical
65
+
66
+ backbone_lr_mult: 0.1
67
+ batch_size: 48
68
+ grad_accum: 3 # eff batch 144
69
+ num_workers: 12
70
+ lr: 1.0e-4
71
+ warmup_steps: 1000
72
+ steps: 60000
73
+ grad_clip: 10.0
74
+ log_freq: 100
75
+ save_freq: 10000
76
+ mixed_precision: bf16
tinyvla2/configs/tv2_demoonly.yaml ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DEMO-ONLY: every robot-identity channel removed (no embodiment ID, no
2
+ # morphology descriptor, no robot text) — the K demo exemplars in the LM
3
+ # attention stream are the ONLY cue for which robot this is. Per-source action
4
+ # normalization creates the pressure: to predict correctly the model MUST infer
5
+ # the robot's action scale, and only the demos carry it.
6
+ # (was: RICH SLOW PATH) demos + robot text + instruction + image all inside the LM
7
+ # sequence (demos in the ATTENTION STREAM), LM emits latent tokens conditioning
8
+ # the fast path. Dual-rate PRESERVED (fast path unchanged, latent cacheable).
9
+ # (was: in-context few-shot DEMO conditioning) — a few (obs, action) exemplar
10
+ # pairs from the target robot's own data, encoded via the shared spatial-aware
11
+ # vision encoder + a small MLP, WITHOUT any gradient update to adapt to that robot.
12
+ # Composed with the text-via-Qwen morphology channel (both active). Resumes from
13
+ # tv2_C_qwen_morph/final (LM already unfrozen, morph_out_proj already trained).
14
+ output_dir: /home/alexw/tinyvla/outputs/tv2_demoonly
15
+ wandb: null
16
+ seed: 42
17
+ robot_prompts: /home/alexw/tinyvla/configs/morphology/robot_prompts.yaml
18
+ resume_from: /home/alexw/tinyvla/outputs/tv2_C_qwen_morph/final
19
+ resume_step: 0
20
+
21
+ datasets:
22
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
23
+ weight: 0.25
24
+ morph_key: so101
25
+ - repo_id: MolmoAct2-Bridge-Dataset
26
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
27
+ episodes: 15000
28
+ weight: 0.18
29
+ morph_key: bridge
30
+ - repo_id: MolmoAct2-RT-1-Dataset
31
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
32
+ episodes: 15000
33
+ weight: 0.18
34
+ morph_key: rt1
35
+ - repo_id: div_ur5
36
+ root: /home/alexw/tinyvla_data/diverse/div_ur5
37
+ weight: 0.10
38
+ morph_key: ur5
39
+ - repo_id: div_xarm
40
+ root: /home/alexw/tinyvla_data/diverse/div_xarm
41
+ weight: 0.09
42
+ morph_key: xarm
43
+ - repo_id: div_dlr_edan
44
+ root: /home/alexw/tinyvla_data/diverse/div_dlr_edan
45
+ weight: 0.05
46
+ morph_key: dlr_edan
47
+ - repo_id: heldout_tidybot
48
+ root: /home/alexw/tinyvla_data/heldout/heldout_tidybot
49
+ weight: 0.08
50
+ morph_key: tidybot
51
+ - repo_id: heldout_cmu_stretch
52
+ root: /home/alexw/tinyvla_data/heldout/heldout_cmu_stretch
53
+ weight: 0.07
54
+ morph_key: hello_stretch
55
+
56
+ policy:
57
+ chunk_size: 50
58
+ n_action_steps: 50
59
+ image_size: 256
60
+ freeze_lm: false
61
+ freeze_vision_encoder: true
62
+ num_embodiments: 16
63
+ conditioning: morph_qwen
64
+ num_morph_readout: 4
65
+ morph_text_max_len: 32
66
+ action_space: canonical
67
+ use_demo_conditioning: false # demos now go INTO the LM sequence, not the MLP encoder
68
+ n_support: 4
69
+ support_other_task: true
70
+ demo_tokens_per_example: 4
71
+ demo_visual_groups: 4
72
+ demo_action_keys: 8
73
+ demo_hidden_mult: 4
74
+ vlm_native: true
75
+ num_action_readout: 16
76
+ demo_action_keys_lm: 4
77
+ demo_only: true
78
+
79
+ backbone_lr_mult: 0.1
80
+ batch_size: 24
81
+ grad_accum: 3
82
+ num_workers: 12
83
+ lr: 7.0e-5
84
+ warmup_steps: 500
85
+ steps: 20000
86
+ grad_clip: 10.0
87
+ log_freq: 50
88
+ save_freq: 5000
89
+ mixed_precision: bf16
tinyvla2/configs/tv2_richslow.yaml ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # RICH SLOW PATH: demos + robot text + instruction + image all inside the LM
2
+ # sequence (demos in the ATTENTION STREAM), LM emits latent tokens conditioning
3
+ # the fast path. Dual-rate PRESERVED (fast path unchanged, latent cacheable).
4
+ # (was: in-context few-shot DEMO conditioning) — a few (obs, action) exemplar
5
+ # pairs from the target robot's own data, encoded via the shared spatial-aware
6
+ # vision encoder + a small MLP, WITHOUT any gradient update to adapt to that robot.
7
+ # Composed with the text-via-Qwen morphology channel (both active). Resumes from
8
+ # tv2_C_qwen_morph/final (LM already unfrozen, morph_out_proj already trained).
9
+ output_dir: /home/alexw/tinyvla/outputs/tv2_richslow
10
+ wandb: null
11
+ seed: 42
12
+ robot_prompts: /home/alexw/tinyvla/configs/morphology/robot_prompts.yaml
13
+ resume_from: /home/alexw/tinyvla/outputs/tv2_C_qwen_morph/final
14
+ resume_step: 0
15
+
16
+ datasets:
17
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
18
+ weight: 0.25
19
+ morph_key: so101
20
+ - repo_id: MolmoAct2-Bridge-Dataset
21
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
22
+ episodes: 15000
23
+ weight: 0.18
24
+ morph_key: bridge
25
+ - repo_id: MolmoAct2-RT-1-Dataset
26
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
27
+ episodes: 15000
28
+ weight: 0.18
29
+ morph_key: rt1
30
+ - repo_id: div_ur5
31
+ root: /home/alexw/tinyvla_data/diverse/div_ur5
32
+ weight: 0.10
33
+ morph_key: ur5
34
+ - repo_id: div_xarm
35
+ root: /home/alexw/tinyvla_data/diverse/div_xarm
36
+ weight: 0.09
37
+ morph_key: xarm
38
+ - repo_id: div_dlr_edan
39
+ root: /home/alexw/tinyvla_data/diverse/div_dlr_edan
40
+ weight: 0.05
41
+ morph_key: dlr_edan
42
+ - repo_id: heldout_tidybot
43
+ root: /home/alexw/tinyvla_data/heldout/heldout_tidybot
44
+ weight: 0.08
45
+ morph_key: tidybot
46
+ - repo_id: heldout_cmu_stretch
47
+ root: /home/alexw/tinyvla_data/heldout/heldout_cmu_stretch
48
+ weight: 0.07
49
+ morph_key: hello_stretch
50
+
51
+ policy:
52
+ chunk_size: 50
53
+ n_action_steps: 50
54
+ image_size: 256
55
+ freeze_lm: false
56
+ freeze_vision_encoder: true
57
+ num_embodiments: 16
58
+ conditioning: morph_qwen
59
+ num_morph_readout: 4
60
+ morph_text_max_len: 32
61
+ action_space: canonical
62
+ use_demo_conditioning: false # demos now go INTO the LM sequence, not the MLP encoder
63
+ n_support: 3
64
+ support_other_task: true
65
+ demo_tokens_per_example: 4
66
+ demo_visual_groups: 4
67
+ demo_action_keys: 8
68
+ demo_hidden_mult: 4
69
+ vlm_native: true
70
+ num_action_readout: 16
71
+ demo_action_keys_lm: 4
72
+
73
+ backbone_lr_mult: 0.1
74
+ batch_size: 24
75
+ grad_accum: 3
76
+ num_workers: 12
77
+ lr: 7.0e-5
78
+ warmup_steps: 500
79
+ steps: 20000
80
+ grad_clip: 10.0
81
+ log_freq: 50
82
+ save_freq: 5000
83
+ mixed_precision: bf16
tinyvla2/configs/tv2_robocasa.yaml ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Continue-train the best checkpoint (C-scaled, 60k) on RoboCasa365 — the first
2
+ # genuinely NEW domain in this project: simulated kitchen scenes (everything before
3
+ # was real-world tabletops) with PandaOmron, a Franka arm on a holonomic mobile base.
4
+ # 7356 eps / 1.5M frames / 20fps / 3 cameras.
5
+ #
6
+ # RoboCasa gets half the sampling weight; the other half replays the previous robots
7
+ # so we can tell integration from catastrophic forgetting (all old robots stay
8
+ # measurable with the same eval). LeKiwi remains fully held out.
9
+ output_dir: /home/alexw/tinyvla/outputs/tv2_robocasa
10
+ wandb: null
11
+ seed: 42
12
+ morphology_descriptors: /home/alexw/tinyvla/configs/morphology/descriptors.yaml
13
+ resume_from: /home/alexw/tinyvla/outputs/tv2_C_scaled/final
14
+ resume_step: 0
15
+
16
+ datasets:
17
+ - repo_id: robocasa365 # NEW domain: sim kitchens, PandaOmron
18
+ root: /home/alexw/tinyvla_data/robocasa365
19
+ weight: 0.50
20
+ morph_key: panda_omron
21
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
22
+ weight: 0.12
23
+ morph_key: so101
24
+ - repo_id: MolmoAct2-Bridge-Dataset
25
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
26
+ weight: 0.11
27
+ morph_key: bridge
28
+ - repo_id: MolmoAct2-RT-1-Dataset
29
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
30
+ weight: 0.11
31
+ morph_key: rt1
32
+ - repo_id: div_ur5
33
+ root: /home/alexw/tinyvla_data/diverse/div_ur5
34
+ weight: 0.05
35
+ morph_key: ur5
36
+ - repo_id: div_xarm
37
+ root: /home/alexw/tinyvla_data/diverse/div_xarm
38
+ weight: 0.04
39
+ morph_key: xarm
40
+ - repo_id: div_dlr_edan
41
+ root: /home/alexw/tinyvla_data/diverse/div_dlr_edan
42
+ weight: 0.02
43
+ morph_key: dlr_edan
44
+ - repo_id: heldout_tidybot
45
+ root: /home/alexw/tinyvla_data/heldout/heldout_tidybot
46
+ weight: 0.02
47
+ morph_key: tidybot
48
+ - repo_id: heldout_cmu_stretch
49
+ root: /home/alexw/tinyvla_data/heldout/heldout_cmu_stretch
50
+ weight: 0.02
51
+ morph_key: hello_stretch
52
+ - repo_id: heldout_jaco_play
53
+ root: /home/alexw/tinyvla_data/heldout/heldout_jaco_play
54
+ weight: 0.01
55
+ morph_key: jaco
56
+
57
+ policy:
58
+ chunk_size: 50
59
+ n_action_steps: 50
60
+ image_size: 256
61
+ freeze_lm: false
62
+ freeze_vision_encoder: true
63
+ num_embodiments: 16
64
+ conditioning: morph
65
+ action_space: canonical
66
+
67
+ backbone_lr_mult: 0.1
68
+ batch_size: 48
69
+ grad_accum: 3
70
+ num_workers: 12
71
+ lr: 7.0e-5
72
+ warmup_steps: 500
73
+ steps: 30000
74
+ grad_clip: 10.0
75
+ log_freq: 100
76
+ save_freq: 10000
77
+ mixed_precision: bf16
tinyvla2/configs/tv2_vlm_native.yaml ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # LLaVA-style: ALL modalities as tokens in one LM sequence, policy conditions on
2
+ # latent readout tokens. Demos live IN the attention stream (fix for v1-v3 failure).
3
+ # Trades away the dual-rate fast path.
4
+ # (was: in-context few-shot DEMO conditioning) — a few (obs, action) exemplar
5
+ # pairs from the target robot's own data, encoded via the shared spatial-aware
6
+ # vision encoder + a small MLP, WITHOUT any gradient update to adapt to that robot.
7
+ # Composed with the text-via-Qwen morphology channel (both active). Resumes from
8
+ # tv2_C_qwen_morph/final (LM already unfrozen, morph_out_proj already trained).
9
+ output_dir: /home/alexw/tinyvla/outputs/tv2_vlm_native
10
+ wandb: null
11
+ seed: 42
12
+ robot_prompts: /home/alexw/tinyvla/configs/morphology/robot_prompts.yaml
13
+ resume_from: /home/alexw/tinyvla/outputs/tv2_C_qwen_morph/final
14
+ resume_step: 0
15
+
16
+ datasets:
17
+ - root_glob: /home/alexw/tinyvla_data/so101_v3/*
18
+ weight: 0.25
19
+ morph_key: so101
20
+ - repo_id: MolmoAct2-Bridge-Dataset
21
+ root: /home/alexw/tinyvla_data/MolmoAct2-Bridge-Dataset
22
+ episodes: 15000
23
+ weight: 0.18
24
+ morph_key: bridge
25
+ - repo_id: MolmoAct2-RT-1-Dataset
26
+ root: /home/alexw/tinyvla_data/MolmoAct2-RT-1-Dataset
27
+ episodes: 15000
28
+ weight: 0.18
29
+ morph_key: rt1
30
+ - repo_id: div_ur5
31
+ root: /home/alexw/tinyvla_data/diverse/div_ur5
32
+ weight: 0.10
33
+ morph_key: ur5
34
+ - repo_id: div_xarm
35
+ root: /home/alexw/tinyvla_data/diverse/div_xarm
36
+ weight: 0.09
37
+ morph_key: xarm
38
+ - repo_id: div_dlr_edan
39
+ root: /home/alexw/tinyvla_data/diverse/div_dlr_edan
40
+ weight: 0.05
41
+ morph_key: dlr_edan
42
+ - repo_id: heldout_tidybot
43
+ root: /home/alexw/tinyvla_data/heldout/heldout_tidybot
44
+ weight: 0.08
45
+ morph_key: tidybot
46
+ - repo_id: heldout_cmu_stretch
47
+ root: /home/alexw/tinyvla_data/heldout/heldout_cmu_stretch
48
+ weight: 0.07
49
+ morph_key: hello_stretch
50
+
51
+ policy:
52
+ chunk_size: 50
53
+ n_action_steps: 50
54
+ image_size: 256
55
+ freeze_lm: false
56
+ freeze_vision_encoder: true
57
+ num_embodiments: 16
58
+ conditioning: id # unused in vlm_native; robot text goes into the LM sequence
59
+ num_morph_readout: 4
60
+ morph_text_max_len: 32
61
+ action_space: canonical
62
+ use_demo_conditioning: true
63
+ n_support: 3
64
+ support_other_task: true
65
+ demo_tokens_per_example: 4
66
+ demo_visual_groups: 4
67
+ demo_action_keys: 8
68
+ demo_hidden_mult: 4
69
+ vlm_native: true
70
+ num_action_readout: 16
71
+ demo_action_keys_lm: 4
72
+
73
+ backbone_lr_mult: 0.1
74
+ batch_size: 20
75
+ grad_accum: 4
76
+ num_workers: 12
77
+ lr: 7.0e-5
78
+ warmup_steps: 500
79
+ steps: 20000
80
+ grad_clip: 10.0
81
+ log_freq: 50
82
+ save_freq: 5000
83
+ mixed_precision: bf16
tinyvla2/scripts/__pycache__/fewshot_grid.cpython-312.pyc ADDED
Binary file (5.77 kB). View file
 
tinyvla2/scripts/_eval_richslow_10k.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Auto-eval for in-context demo conditioning on held-out LeKiwi.
3
+
4
+ The key test: give the model K=3 REAL (obs, action) example pairs from LeKiwi's
5
+ OWN data at test time — NO gradient update, NO fine-tuning — and see if that beats
6
+ zero-shot with no examples, wrong examples, or the numeric/text descriptor alone.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import numpy as np
12
+ import torch
13
+ import yaml
14
+ from lerobot.datasets.lerobot_dataset import LeRobotDataset, LeRobotDatasetMetadata
15
+ from transformers import AutoTokenizer
16
+
17
+ from tinyvla.data.canonical import CanonicalChunkStore, quantile_normalize
18
+ from tinyvla.modeling_tinyvla import TinyVLAPolicy
19
+
20
+ CKPT = "outputs/tv2_richslow/step_10000"
21
+ DS = "lekiwi_cleanup"
22
+ ROOT = f"/home/alexw/tinyvla_data/lekiwi/{DS}"
23
+ K = 3
24
+
25
+
26
+ @torch.no_grad()
27
+ def main():
28
+ pol = TinyVLAPolicy.from_pretrained(CKPT).cuda().eval()
29
+ cfg = pol.config
30
+ tok = AutoTokenizer.from_pretrained(cfg.lm_model_name)
31
+ prompts = yaml.safe_load(open("configs/morphology/robot_prompts.yaml"))
32
+
33
+ m = LeRobotDatasetMetadata(DS, root=ROOT)
34
+ ds = LeRobotDataset(DS, root=ROOT, delta_timestamps={"action": [t / m.fps for t in range(50)]},
35
+ video_backend="torchcodec")
36
+ store = CanonicalChunkStore(DS, src_fps=m.fps, chunk=50)
37
+ st = store.compute_stats()
38
+ q01, q99 = np.asarray(st["q01"]), np.asarray(st["q99"])
39
+ span = np.maximum(q99 - q01, 0.01 * np.median(np.abs(np.concatenate([q01, q99])) + 1e-6))
40
+ mid = 0.5 * (q01 + q99)
41
+
42
+ imk = sorted(k for k in ds.meta.features if k.startswith("observation.images"))
43
+ prim = next((k for k in imk if "front" in k or "base" in k), imk[0])
44
+
45
+ def integ(d):
46
+ return np.cumsum(d[:, :3], 0)
47
+
48
+ def img_at(idx, ep, ep_start):
49
+ it = ds[idx]
50
+ im = torch.nn.functional.interpolate(it[prim][None], size=(256, 256), mode="bilinear")[0]
51
+ raw = quantile_normalize(store.chunk_for(ep, idx - ep_start), q01, q99)
52
+ act = torch.from_numpy(raw[:, :7]).float()
53
+ act = torch.nn.functional.pad(act, (0, cfg.max_action_dim - 7))
54
+ return im, act
55
+
56
+ # REAL LeKiwi demo pairs from early episodes (support pool), disjoint from test
57
+ support_pool_eps = range(0, min(20, m.total_episodes - 15))
58
+ test = range(max(0, m.total_episodes - 15), m.total_episodes)
59
+
60
+ def get_real_support(k):
61
+ imgs, acts = [], []
62
+ for _ in range(k):
63
+ ep = int(np.random.choice(list(support_pool_eps)))
64
+ s = int(m.episodes["dataset_from_index"][ep])
65
+ e = int(m.episodes["dataset_to_index"][ep])
66
+ idx = int(np.random.randint(s, max(s + 1, e - 1)))
67
+ im, act = img_at(idx, ep, s)
68
+ imgs.append(im)
69
+ acts.append(act)
70
+ return torch.stack(imgs), torch.stack(acts)
71
+
72
+ def get_wrong_support(k):
73
+ # random noise images + random actions — a garbage support set control
74
+ return torch.rand(k, 3, 256, 256), torch.randn(k, 50, cfg.max_action_dim) * 0.3
75
+
76
+
77
+ # control: demos from a DIFFERENT ROBOT (jaco) — if the model truly extracts
78
+ # robot-specific dynamics from exemplars, these should be worse than LeKiwi's own
79
+ def get_otherrobot_support(k):
80
+ from lerobot.datasets.lerobot_dataset import LeRobotDataset as _LD, LeRobotDatasetMetadata as _LM
81
+ from tinyvla.data.canonical import CanonicalChunkStore as _CS
82
+ jr = "/home/alexw/tinyvla_data/heldout/heldout_jaco_play"
83
+ jm = _LM("heldout_jaco_play", root=jr)
84
+ jds = _LD("heldout_jaco_play", root=jr, delta_timestamps={"action": [t / jm.fps for t in range(50)]}, video_backend="torchcodec")
85
+ jstore = _CS("heldout_jaco_play", src_fps=jm.fps, chunk=50)
86
+ jst = jstore.compute_stats(200)
87
+ jq01, jq99 = np.asarray(jst["q01"]), np.asarray(jst["q99"])
88
+ jimk = sorted(kk for kk in jds.meta.features if kk.startswith("observation.images"))[0]
89
+ imgs, acts = [], []
90
+ for _ in range(k):
91
+ ep = int(np.random.randint(0, 50))
92
+ s = int(jm.episodes["dataset_from_index"][ep]); e = int(jm.episodes["dataset_to_index"][ep])
93
+ idx = int(np.random.randint(s, max(s + 1, e - 1)))
94
+ it = jds[idx]
95
+ im = torch.nn.functional.interpolate(it[jimk][None], size=(256, 256), mode="bilinear")[0]
96
+ raw = quantile_normalize(jstore.chunk_for(ep, idx - s), jq01, jq99)
97
+ act = torch.nn.functional.pad(torch.from_numpy(raw[:, :7]).float(), (0, cfg.max_action_dim - 7))
98
+ imgs.append(im); acts.append(act)
99
+ return torch.stack(imgs), torch.stack(acts)
100
+
101
+ def run(support_fn, desc_text):
102
+ ids_t = tok([desc_text] if desc_text else [""], padding="max_length", truncation=True,
103
+ max_length=cfg.morph_text_max_len, return_tensors="pt")
104
+ errs = []
105
+ for ep in test:
106
+ s = int(m.episodes["dataset_from_index"][ep])
107
+ e = int(m.episodes["dataset_to_index"][ep])
108
+ for idx in range(s, e - 1, 30):
109
+ item = ds[idx]
110
+ task = item.get("task") or ""
111
+ t = tok([task], padding=True, truncation=True, max_length=48, return_tensors="pt")
112
+ img = torch.nn.functional.interpolate(item[prim][None], size=(256, 256), mode="bilinear")[0]
113
+ stt = torch.nn.functional.pad(item["observation.state"].float()[:6], (0, cfg.max_state_dim - 6))
114
+ sup_img, sup_act = support_fn(K)
115
+ b = {"observation.images.cam0": img[None].cuda(),
116
+ "observation.images.cam1": torch.zeros_like(img)[None].cuda(),
117
+ "observation.state": stt[None].cuda(),
118
+ "observation.language.tokens": t["input_ids"].cuda(),
119
+ "observation.language.attention_mask": t["attention_mask"].bool().cuda(),
120
+ "morph_text_ids": ids_t["input_ids"].cuda(),
121
+ "morph_text_mask": ids_t["attention_mask"].bool().cuda(),
122
+ "support_images": sup_img[None].cuda(), "support_actions": sup_act[None].cuda(),
123
+ "embodiment_id": torch.tensor([0]).cuda()}
124
+ with torch.autocast("cuda", torch.bfloat16):
125
+ pr = pol.predict_action_chunk(b)[0].cpu().float().numpy()
126
+ gu = quantile_normalize(store.chunk_for(ep, idx - s), q01, q99)[:, :7] * span / 2 + mid
127
+ pu = pr[:, :7] * span / 2 + mid
128
+ errs.append(np.linalg.norm(integ(pu)[-1] - integ(gu)[-1]) * 1000)
129
+ return np.mean(errs)
130
+
131
+ print("=== RICH-SLOW (demos in LM attention stream, no FT) on held-out LeKiwi ===")
132
+ print(f"REAL LeKiwi demos (K={K}) + desc=lekiwi endpoint {run(get_real_support, prompts['lekiwi']):.1f}mm")
133
+ print(f"REAL LeKiwi demos (K={K}) + desc=none endpoint {run(get_real_support, None):.1f}mm")
134
+ print(f"WRONG/garbage demos + desc=lekiwi endpoint {run(get_wrong_support, prompts['lekiwi']):.1f}mm")
135
+ print(f"WRONG/garbage demos + desc=none endpoint {run(get_wrong_support, None):.1f}mm")
136
+ print(f"OTHER-ROBOT (jaco) demos + desc=none endpoint {run(get_otherrobot_support, None):.1f}mm")
137
+ print("\nprior baselines: C-diverse 295mm | C-qwen-morph desc=none 294mm | v1 demo(same-task) real=290.6 garbage=288.5 (dead)")
138
+
139
+
140
+ if __name__ == "__main__":
141
+ main()
tinyvla2/scripts/audit_so101_fk.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Gate 1: per-dataset SO101 FK calibration audit.
3
+
4
+ Same recorded degrees can map to different physical poses across lerobot
5
+ zero-conventions → FK garbage that would poison B/C canonical labels while
6
+ leaving A untouched (a confound AGAINST the hypothesis). Drop datasets whose
7
+ FK produces non-physical EE trajectories.
8
+
9
+ Checks per dataset (sampled frames across episodes):
10
+ - reach in [0.02, 0.40] m (SO101 max reach ~0.35)
11
+ - z above a floor (> -0.20 m; base frame origin at arm mount)
12
+ - trajectory smoothness (median consecutive EE step < 0.05 m at 30fps)
13
+ - FK(state) vs FK(action-target) diffs correlate (both go through FK, deltas
14
+ should track since action = commanded target of the same arm)
15
+
16
+ Writes ~/tinyvla_data/so101_fk_audit.json with per-dataset verdict.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import json
22
+ from pathlib import Path
23
+
24
+ import numpy as np
25
+
26
+ DATA_ROOT = Path.home() / "tinyvla_data" / "so101_v3"
27
+ OUT = Path.home() / "tinyvla_data" / "so101_fk_audit.json"
28
+
29
+
30
+ def audit_one(name, root, fk, n_frames=200):
31
+ from lerobot.datasets.lerobot_dataset import LeRobotDataset
32
+
33
+ ds = LeRobotDataset(name, root=root)
34
+ anames = ds.meta.features["action"]["shape"][0]
35
+ if anames != 6:
36
+ return {"verdict": "SKIP", "reason": f"action dim {anames} != 6"}
37
+ # smoothness needs CONSECUTIVE frames → sample a few contiguous windows;
38
+ # reach/corr can use sparse knots across the whole dataset
39
+ n = len(ds)
40
+ eps = ds.meta.episodes
41
+ sparse = np.linspace(0, n - 1, min(n_frames, n)).astype(int)
42
+ pos_state, pos_act = [], []
43
+ for i in sparse:
44
+ item = ds[int(i)]
45
+ pos_state.append(fk.ee_pose(item["observation.state"].numpy())[:3, 3])
46
+ pos_act.append(fk.ee_pose(item["action"].numpy())[:3, 3])
47
+ ps = np.array(pos_state)
48
+ pa = np.array(pos_act)
49
+ reach = np.linalg.norm(ps, axis=1)
50
+
51
+ # consecutive-frame EE steps within the first episode (real per-frame motion)
52
+ e0, e1 = int(eps["dataset_from_index"][0]), int(eps["dataset_to_index"][0])
53
+ consec = []
54
+ prev = None
55
+ for i in range(e0, min(e1, e0 + 150)):
56
+ p = fk.ee_pose(ds[i]["observation.state"].numpy())[:3, 3]
57
+ if prev is not None:
58
+ consec.append(np.linalg.norm(p - prev))
59
+ prev = p
60
+ steps = np.array(consec) if consec else np.array([0.0])
61
+ # correlation of state-vs-target displacement over sampled knots
62
+ d_state = np.diff(ps, axis=0).flatten()
63
+ d_act = np.diff(pa, axis=0).flatten()
64
+ corr = float(np.corrcoef(d_state, d_act)[0, 1]) if d_state.std() > 1e-9 else 0.0
65
+
66
+ ok_reach = bool(0.02 < reach.mean() < 0.40 and reach.max() < 0.50)
67
+ ok_z = bool(ps[:, 2].min() > -0.20)
68
+ ok_smooth = bool(np.median(steps) < 0.06)
69
+ ok_corr = bool(corr > 0.5)
70
+ verdict = "KEEP" if (ok_reach and ok_z and ok_smooth and ok_corr) else "DROP"
71
+ return {
72
+ "verdict": verdict,
73
+ "reach_mean": round(float(reach.mean()), 3),
74
+ "reach_max": round(float(reach.max()), 3),
75
+ "z_min": round(float(ps[:, 2].min()), 3),
76
+ "step_median": round(float(np.median(steps)), 4),
77
+ "corr_state_target": round(corr, 3),
78
+ "flags": {"reach": ok_reach, "z": ok_z, "smooth": ok_smooth, "corr": ok_corr},
79
+ }
80
+
81
+
82
+ def main():
83
+ from tinyvla.data.kinematics_so101 import SO101FK
84
+
85
+ fk = SO101FK()
86
+ results = {}
87
+ roots = sorted(DATA_ROOT.iterdir())
88
+ for r in roots:
89
+ if not (r / "meta" / "info.json").exists():
90
+ continue
91
+ try:
92
+ res = audit_one(r.name, r, fk)
93
+ except Exception as e:
94
+ res = {"verdict": "ERROR", "reason": f"{type(e).__name__}: {str(e)[:120]}"}
95
+ results[r.name] = res
96
+ print(f"{res['verdict']:6} {r.name}: {res}")
97
+ OUT.write_text(json.dumps(results, indent=1))
98
+ keep = sum(1 for v in results.values() if v["verdict"] == "KEEP")
99
+ print(f"\nKEEP {keep}/{len(results)} -> {OUT}")
100
+
101
+
102
+ if __name__ == "__main__":
103
+ main()
tinyvla2/scripts/build_ee_sidecar.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Build per-frame canonical EE-pose sidecars (offline, once).
3
+
4
+ For each frame stores the absolute end-effector pose implied by BOTH the
5
+ observation state and the action target, in the robot base frame:
6
+ cols: episode_index, frame_index,
7
+ s_px s_py s_pz s_qx s_qy s_qz s_qw (state EE pose)
8
+ a_px a_py a_pz a_qx a_qy a_qz a_qw (action-target EE pose)
9
+ grip (raw gripper channel, for q1-q99 later)
10
+
11
+ Canonical chunk deltas are assembled at train time from these poses (anchor,
12
+ frequency, rotation convention are all decided there — this file is convention-free).
13
+
14
+ Source kinds:
15
+ so101 : FK(joint_deg) for state and action (both are joint vectors).
16
+ ee : state/action already EE; reconstruct absolute pose. Bridge/LIBERO/DROID
17
+ store EE pose in observation.state; action is a delta → target = state ⊕ action.
18
+ (implemented per-source as adapters are verified.)
19
+
20
+ Usage:
21
+ python scripts/build_ee_sidecar.py --kind so101 --glob '~/tinyvla_data/so101_v3/*'
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import argparse
27
+ from pathlib import Path
28
+
29
+ import numpy as np
30
+ import pyarrow as pa
31
+ import pyarrow.parquet as pq
32
+
33
+ OUT_DIR = Path.home() / "tinyvla_data" / "ee_sidecar"
34
+
35
+
36
+ def _read_cols(name, root):
37
+ """State/action/ep/frame columns WITHOUT video decode (raw parquet)."""
38
+ from lerobot.datasets.lerobot_dataset import LeRobotDataset
39
+
40
+ ds = LeRobotDataset(name, root=root)
41
+ hf = ds.reader.hf_dataset.with_format("numpy")
42
+ return (
43
+ np.asarray(hf["observation.state"], dtype=np.float64),
44
+ np.asarray(hf["action"], dtype=np.float64),
45
+ np.asarray(hf["episode_index"]).astype(int),
46
+ np.asarray(hf["frame_index"]).astype(int),
47
+ )
48
+
49
+
50
+ def build_so101(name, root, fk):
51
+ from scipy.spatial.transform import Rotation
52
+
53
+ state, action, ep, fr = _read_cols(name, root)
54
+ n = len(state)
55
+ cols = ["episode_index", "frame_index",
56
+ "s_px", "s_py", "s_pz", "s_qx", "s_qy", "s_qz", "s_qw",
57
+ "a_px", "a_py", "a_pz", "a_qx", "a_qy", "a_qz", "a_qw", "grip"]
58
+ buf = {k: np.empty(n, dtype=np.float64) for k in cols}
59
+ buf["episode_index"] = ep.astype(np.float64)
60
+ buf["frame_index"] = fr.astype(np.float64)
61
+ for i in range(n):
62
+ Ts = fk.ee_pose(state[i])
63
+ Ta = fk.ee_pose(action[i])
64
+ sp, sq = Ts[:3, 3], Rotation.from_matrix(Ts[:3, :3]).as_quat()
65
+ ap, aq = Ta[:3, 3], Rotation.from_matrix(Ta[:3, :3]).as_quat()
66
+ for j, k in enumerate(("s_px", "s_py", "s_pz")): buf[k][i] = sp[j]
67
+ for j, k in enumerate(("s_qx", "s_qy", "s_qz", "s_qw")): buf[k][i] = sq[j]
68
+ for j, k in enumerate(("a_px", "a_py", "a_pz")): buf[k][i] = ap[j]
69
+ for j, k in enumerate(("a_qx", "a_qy", "a_qz", "a_qw")): buf[k][i] = aq[j]
70
+ buf["grip"][i] = action[i, 5]
71
+ return pa.table(buf)
72
+
73
+
74
+ def build_ee_from_state(name, root):
75
+ """EE-native sources (Bridge, RT-1): state carries absolute EE pose.
76
+
77
+ Bridge state = [x y z roll pitch yaw pad gripper]; RT-1 state =
78
+ [x y z rx ry rz rw gripper] (quat). We store the STATE pose as canonical
79
+ absolute pose (both s_* and a_* set to state pose; targets reconstructed at
80
+ train time as state ⊕ action-delta if needed, but state-derived deltas are
81
+ the uniform choice per plan → a_* == next-frame not needed here).
82
+ """
83
+ from lerobot.datasets.lerobot_dataset import LeRobotDataset
84
+ from scipy.spatial.transform import Rotation
85
+
86
+ ds = LeRobotDataset(name, root=root)
87
+ snames = ds.meta.features["observation.state"].get("names", {})
88
+ flat = snames.get("motors", snames) if isinstance(snames, dict) else snames
89
+ state, _, ep, fr = _read_cols(name, root)
90
+ n = len(state)
91
+ # RoboCasa/PandaOmron: state = base_pos(3) base_quat(4) ee_pos(3) ee_quat(4) grip(2)
92
+ # -> the EE block is not at the front, slice it out first
93
+ if state.shape[1] == 16:
94
+ state = np.concatenate([state[:, 7:14], state[:, 14:15]], axis=1) # pos,quat,grip
95
+ # detect quaternion: named "rw", or 8-dim state whose dims 3:7 are unit-norm
96
+ is_quat = ("rw" in flat) or (
97
+ state.shape[1] >= 8 and abs(np.linalg.norm(state[:200, 3:7], axis=1).mean() - 1.0) < 1e-2
98
+ )
99
+ cols = ["episode_index", "frame_index",
100
+ "s_px", "s_py", "s_pz", "s_qx", "s_qy", "s_qz", "s_qw",
101
+ "a_px", "a_py", "a_pz", "a_qx", "a_qy", "a_qz", "a_qw", "grip"]
102
+ buf = {k: np.empty(n, dtype=np.float64) for k in cols}
103
+ buf["episode_index"] = ep.astype(np.float64)
104
+ buf["frame_index"] = fr.astype(np.float64)
105
+ pos = state[:, :3]
106
+ if is_quat: # [x y z rx ry rz rw gripper]
107
+ quat = state[:, 3:7]
108
+ grip = state[:, 7]
109
+ else: # [x y z roll pitch yaw (pad) (gripper)]
110
+ quat = Rotation.from_euler("xyz", state[:, 3:6]).as_quat()
111
+ grip = state[:, 7] if state.shape[1] > 7 else np.zeros(n) # free-body (UAV): no gripper
112
+ for j, k in enumerate(("s_px", "s_py", "s_pz")): buf[k] = pos[:, j]
113
+ for j, k in enumerate(("s_qx", "s_qy", "s_qz", "s_qw")): buf[k] = quat[:, j]
114
+ for j, k in enumerate(("a_px", "a_py", "a_pz")): buf[k] = pos[:, j]
115
+ for j, k in enumerate(("a_qx", "a_qy", "a_qz", "a_qw")): buf[k] = quat[:, j]
116
+ buf["grip"] = grip
117
+ return pa.table(buf)
118
+
119
+
120
+ def main():
121
+ parser = argparse.ArgumentParser()
122
+ parser.add_argument("--kind", choices=["so101", "ee"], required=True)
123
+ parser.add_argument("--glob", required=True)
124
+ parser.add_argument("--audit", default=str(Path.home() / "tinyvla_data/so101_fk_audit.json"),
125
+ help="skip datasets not marked KEEP")
126
+ args = parser.parse_args()
127
+
128
+ import glob as _glob
129
+ import json
130
+
131
+ OUT_DIR.mkdir(parents=True, exist_ok=True)
132
+ keep = None
133
+ audit_path = Path(args.audit).expanduser()
134
+ if args.kind == "so101" and audit_path.exists():
135
+ audit = json.loads(audit_path.read_text())
136
+ keep = {k for k, v in audit.items() if v.get("verdict") == "KEEP"}
137
+
138
+ fk = None
139
+ if args.kind == "so101":
140
+ from tinyvla.data.kinematics_so101 import SO101FK
141
+ fk = SO101FK()
142
+
143
+ roots = sorted(Path(p) for p in _glob.glob(str(Path(args.glob).expanduser())))
144
+ done = 0
145
+ for r in roots:
146
+ if not (r / "meta" / "info.json").exists():
147
+ continue
148
+ if keep is not None and r.name not in keep:
149
+ print(f"skip {r.name} (not KEEP)")
150
+ continue
151
+ out = OUT_DIR / f"{r.name}.parquet"
152
+ if out.exists():
153
+ done += 1
154
+ continue
155
+ try:
156
+ if args.kind == "so101":
157
+ tbl = build_so101(r.name, r, fk)
158
+ else:
159
+ tbl = build_ee_from_state(r.name, r)
160
+ pq.write_table(tbl, out)
161
+ done += 1
162
+ print(f"[{done}] {r.name}: {tbl.num_rows} frames -> {out}")
163
+ except Exception as e:
164
+ print(f"FAIL {r.name}: {type(e).__name__}: {str(e)[:150]}")
165
+ print(f"done {done}")
166
+
167
+
168
+ if __name__ == "__main__":
169
+ main()
tinyvla2/scripts/diag_obs_gap.py ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Diagnose the env-obs adapter: compare policy predictions from env-rendered
3
+ observations vs dataset observations at the SAME init state.
4
+
5
+ If pred(dataset obs) is close to GT but pred(env obs) differs, the observation
6
+ adapter (image orientation/cameras/state) is the remaining gap.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+
13
+ import numpy as np
14
+ import torch
15
+ from scipy.spatial.transform import Rotation
16
+
17
+
18
+ @torch.no_grad()
19
+ def main():
20
+ parser = argparse.ArgumentParser()
21
+ parser.add_argument("--checkpoint", default="outputs/libero_ft2/step_15000")
22
+ parser.add_argument("--suite", default="libero_spatial")
23
+ parser.add_argument("--tasks", type=int, default=3)
24
+ args = parser.parse_args()
25
+
26
+ from lerobot.datasets.lerobot_dataset import LeRobotDataset, LeRobotDatasetMetadata
27
+ from lerobot.envs.factory import make_env, make_env_config
28
+ from transformers import AutoTokenizer
29
+ from tinyvla.data.mixture import CanonicalSource
30
+ from tinyvla.modeling_tinyvla import TinyVLAPolicy
31
+
32
+ policy = TinyVLAPolicy.from_pretrained(args.checkpoint).cuda().eval()
33
+ cfg = policy.config
34
+ tok = AutoTokenizer.from_pretrained(cfg.lm_model_name)
35
+
36
+ meta = LeRobotDatasetMetadata("HuggingFaceVLA/libero")
37
+ ds = LeRobotDataset(
38
+ "HuggingFaceVLA/libero",
39
+ delta_timestamps={"action": [t / meta.fps for t in range(cfg.chunk_size)]},
40
+ video_backend="torchcodec",
41
+ )
42
+ src = CanonicalSource(ds, 2, cfg.image_size, cfg.max_state_dim, cfg.max_action_dim)
43
+ s_stats = meta.stats["observation.state"]
44
+ s_mean = torch.as_tensor(s_stats["mean"]).flatten().float()
45
+ s_std = torch.as_tensor(s_stats["std"]).flatten().float().clamp(min=1e-6)
46
+
47
+ env_cfg = make_env_config("libero", task=args.suite)
48
+ task_envs = make_env(env_cfg, n_envs=1)[args.suite]
49
+ env_by_task = {}
50
+ for tid, env in task_envs.items():
51
+ desc = env.get_attr("task_description")[0]
52
+ env_by_task[desc.strip().lower()] = (tid, env)
53
+
54
+ eps_meta = ds.meta.episodes
55
+ first_ep_by_task = {}
56
+ for ep in range(ds.num_episodes):
57
+ start = int(eps_meta["dataset_from_index"][ep])
58
+ task = ds[start]["task"].strip().lower()
59
+ if task in env_by_task and task not in first_ep_by_task:
60
+ first_ep_by_task[task] = ep
61
+
62
+ def tok_batch(task_text):
63
+ t = tok([task_text], padding=True, truncation=True,
64
+ max_length=cfg.tokenizer_max_length, return_tensors="pt")
65
+ return t["input_ids"].cuda(), t["attention_mask"].bool().cuda()
66
+
67
+ def env_to_batch(obs, task_text):
68
+ imgs = {}
69
+ for slot, key in (("cam0", "image"), ("cam1", "image2")):
70
+ x = torch.as_tensor(np.asarray(obs["pixels"][key]))[0].flip(0).flip(1)
71
+ x = x.permute(2, 0, 1).float() / 255.0
72
+ x = torch.nn.functional.interpolate(x[None], size=(cfg.image_size, cfg.image_size),
73
+ mode="bilinear", align_corners=False)[0]
74
+ imgs[slot] = x
75
+ rs = obs["robot_state"]
76
+ pos = np.asarray(rs["eef"]["pos"]).flatten()
77
+ quat = np.asarray(rs["eef"]["quat"]).flatten()
78
+ rotvec = Rotation.from_quat(quat).as_rotvec()
79
+ if rotvec[0] < 0:
80
+ th = np.linalg.norm(rotvec)
81
+ rotvec = rotvec * (th - 2 * np.pi) / th
82
+ grip = np.asarray(rs["gripper"]["qpos"]).flatten()
83
+ state = torch.tensor(np.concatenate([pos, rotvec, grip]), dtype=torch.float32)
84
+ state = (state - s_mean) / s_std
85
+ state = torch.nn.functional.pad(state, (0, cfg.max_state_dim - state.shape[-1]))
86
+ ids, mask = tok_batch(task_text)
87
+ return {
88
+ "observation.images.cam0": imgs["cam0"][None].cuda(),
89
+ "observation.images.cam1": imgs["cam1"][None].cuda(),
90
+ "observation.state": state[None].cuda(),
91
+ "observation.language.tokens": ids,
92
+ "observation.language.attention_mask": mask,
93
+ "embodiment_id": torch.tensor([2], device="cuda"),
94
+ }
95
+
96
+ def ds_to_batch(item):
97
+ ids, mask = tok_batch(item.pop("task"))
98
+ b = {k: v[None].cuda() for k, v in item.items() if torch.is_tensor(v)}
99
+ b["observation.language.tokens"] = ids
100
+ b["observation.language.attention_mask"] = mask
101
+ return b
102
+
103
+ for task, ep in list(first_ep_by_task.items())[: args.tasks]:
104
+ tid, env = env_by_task[task]
105
+ obs, _ = env.reset(seed=0)
106
+ start = int(eps_meta["dataset_from_index"][ep])
107
+ item = src[start]
108
+ gt = item["action"].clone()[None].cuda()
109
+
110
+ env_b = env_to_batch(obs, task)
111
+ ds_b = ds_to_batch(dict(item))
112
+
113
+ torch.manual_seed(0)
114
+ pred_env = policy.predict_action_chunk(env_b)
115
+ torch.manual_seed(0)
116
+ pred_ds = policy.predict_action_chunk(ds_b)
117
+
118
+ m = item["action_dim_mask"]
119
+ d_env_gt = ((pred_env[0, :, m].cpu() - gt[0, :, m].cpu()) ** 2).mean().sqrt().item()
120
+ d_ds_gt = ((pred_ds[0, :, m].cpu() - gt[0, :, m].cpu()) ** 2).mean().sqrt().item()
121
+ d_env_ds = ((pred_env[0, :, m] - pred_ds[0, :, m]) ** 2).mean().sqrt().item()
122
+
123
+ # also compare the raw inputs
124
+ img_gap = (env_b["observation.images.cam0"] - ds_b["observation.images.cam0"]).abs().mean().item()
125
+ st_gap = (env_b["observation.state"] - ds_b["observation.state"]).abs().max().item()
126
+ print(f"task {tid}: RMSE(pred_env, GT)={d_env_gt:.3f} | RMSE(pred_ds, GT)={d_ds_gt:.3f} "
127
+ f"| RMSE(pred_env, pred_ds)={d_env_ds:.3f} | img L1 gap={img_gap:.4f} | state max gap={st_gap:.2f}")
128
+
129
+
130
+ if __name__ == "__main__":
131
+ main()
tinyvla2/scripts/download_subsets.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Download Stage-2 dataset subsets under a hard disk budget.
3
+
4
+ Usage:
5
+ python scripts/download_subsets.py --root ~/tinyvla_data [--dry-run]
6
+
7
+ Downloads metadata first, measures actual on-disk size of a small episode
8
+ sample, then extrapolates before committing to the full subset download.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import shutil
15
+ from pathlib import Path
16
+
17
+ DISK_BUDGET_GB = 400
18
+
19
+ # (repo_id, num_episodes or None for all)
20
+ SUBSETS = [
21
+ ("HuggingFaceVLA/community_dataset_v1", None),
22
+ ("nvidia/BridgeData2_LeRobot_v3", 10_000),
23
+ ("IPEC-COMMUNITY/fractal20220817_data_lerobot", 8_000),
24
+ ]
25
+
26
+
27
+ def free_gb(path: Path) -> float:
28
+ return shutil.disk_usage(path).free / 1e9
29
+
30
+
31
+ def used_gb(path: Path) -> float:
32
+ return sum(f.stat().st_size for f in path.rglob("*") if f.is_file()) / 1e9
33
+
34
+
35
+ def main():
36
+ parser = argparse.ArgumentParser()
37
+ parser.add_argument("--root", type=Path, default=Path.home() / "tinyvla_data")
38
+ parser.add_argument("--dry-run", action="store_true")
39
+ parser.add_argument("--only", type=str, default=None, help="substring filter on repo_id")
40
+ args = parser.parse_args()
41
+
42
+ args.root.mkdir(parents=True, exist_ok=True)
43
+
44
+ from lerobot.datasets.lerobot_dataset import LeRobotDataset, LeRobotDatasetMetadata
45
+
46
+ for repo_id, n_eps in SUBSETS:
47
+ if args.only and args.only not in repo_id:
48
+ continue
49
+ print(f"\n=== {repo_id} (episodes: {n_eps or 'all'}) ===")
50
+ meta = LeRobotDatasetMetadata(repo_id)
51
+ total_eps = meta.total_episodes
52
+ print(f"total episodes: {total_eps}, fps: {meta.fps}")
53
+
54
+ episodes = list(range(min(n_eps, total_eps))) if n_eps else None
55
+
56
+ # probe with 1% to estimate size
57
+ probe_n = max(10, (len(episodes) if episodes else total_eps) // 100)
58
+ probe_dir = args.root / "_probe" / repo_id.replace("/", "__")
59
+ if not args.dry_run and not probe_dir.exists():
60
+ LeRobotDataset(repo_id, root=probe_dir, episodes=list(range(probe_n)))
61
+ probe_gb = used_gb(probe_dir)
62
+ est_gb = probe_gb / probe_n * (len(episodes) if episodes else total_eps)
63
+ print(f"probe: {probe_n} eps = {probe_gb:.2f}GB -> estimated full subset {est_gb:.0f}GB")
64
+ if est_gb > free_gb(args.root) - 100 or used_gb(args.root) + est_gb > DISK_BUDGET_GB:
65
+ print(f"SKIP {repo_id}: would exceed budget ({DISK_BUDGET_GB}GB) or disk")
66
+ continue
67
+
68
+ if args.dry_run:
69
+ continue
70
+
71
+ target = args.root / repo_id.replace("/", "__")
72
+ LeRobotDataset(repo_id, root=target, episodes=episodes)
73
+ print(f"downloaded {repo_id}: {used_gb(target):.1f}GB, total used {used_gb(args.root):.1f}GB")
74
+
75
+
76
+ if __name__ == "__main__":
77
+ main()
tinyvla2/scripts/eval_canonical.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Physical-space canonical-action eval (variants A/B/C, TinyVLA-2 headline metric).
3
+
4
+ Compares A/B/C in ONE fair space: canonical base-frame EE deltas, unnormalized to
5
+ physical units — position error (mm), rotation error (deg), gripper error ([0,1]).
6
+
7
+ - B/C predict canonical directly → unnormalize with dataset canonical stats.
8
+ - A predicts NATIVE actions → for SO101 map joint predictions through FK to EE deltas;
9
+ for EE-native sources A's native deltas are already comparable (convention aside).
10
+ - Held-out embodiment: A/B have no ID row → oracle over trained IDs (best), making any
11
+ C win conservative. C uses the written descriptor (zero-shot by construction).
12
+
13
+ Usage:
14
+ python scripts/eval_canonical.py --checkpoint outputs/tv2_C_morph_canon/final \
15
+ --dataset heldout_jaco_play --root ~/tinyvla_data/heldout/heldout_jaco_play \
16
+ --morph-key jaco --episodes 20
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import argparse
22
+ from pathlib import Path
23
+
24
+ import numpy as np
25
+ import torch
26
+
27
+
28
+ @torch.no_grad()
29
+ def main():
30
+ ap = argparse.ArgumentParser()
31
+ ap.add_argument("--checkpoint", type=Path, required=True)
32
+ ap.add_argument("--dataset", required=True)
33
+ ap.add_argument("--root", required=True)
34
+ ap.add_argument("--morph-key", default=None, help="descriptor key for variant C zero-shot")
35
+ ap.add_argument("--oracle-ids", type=int, default=8, help="A/B: try IDs 0..N-1, report best")
36
+ ap.add_argument("--episodes", type=int, default=20)
37
+ ap.add_argument("--ep-start", type=int, default=0, help="first episode index (few-shot: eval on held-out test split after FT episodes)")
38
+ ap.add_argument("--stride", type=int, default=30)
39
+ args = ap.parse_args()
40
+
41
+ import yaml
42
+ from lerobot.datasets.lerobot_dataset import LeRobotDataset, LeRobotDatasetMetadata
43
+ from scipy.spatial.transform import Rotation
44
+ from transformers import AutoTokenizer
45
+
46
+ from tinyvla.data.canonical import CanonicalChunkStore, quantile_normalize
47
+ from tinyvla.modeling_tinyvla import TinyVLAPolicy
48
+ from tinyvla.modules.embodiment import MORPH_FIELDS
49
+
50
+ policy = TinyVLAPolicy.from_pretrained(args.checkpoint).cuda().eval()
51
+ cfg = policy.config
52
+ tok = AutoTokenizer.from_pretrained(cfg.lm_model_name)
53
+ chunk = cfg.chunk_size
54
+
55
+ meta = LeRobotDatasetMetadata(args.dataset, root=args.root)
56
+ ds = LeRobotDataset(args.dataset, root=args.root,
57
+ delta_timestamps={"action": [t / meta.fps for t in range(chunk)]},
58
+ video_backend="torchcodec")
59
+ store = CanonicalChunkStore(args.dataset, src_fps=ds.fps, chunk=chunk)
60
+ stats = store.compute_stats()
61
+ q01, q99 = np.asarray(stats["q01"]), np.asarray(stats["q99"])
62
+
63
+ # morphology descriptor (variant C)
64
+ morph = None
65
+ if cfg.conditioning == "morph" and args.morph_key:
66
+ raw = yaml.safe_load(open("configs/morphology/descriptors.yaml"))[args.morph_key]
67
+ _sc = {"arm_dof": 0.1, "reach_m": 2, "gripper_width_m": 10, "num_cameras": 1 / 3,
68
+ "control_hz": 1 / 30, "joint_lo_mean": 1 / 3.1416, "joint_hi_mean": 1 / 3.1416,
69
+ "workspace_x": 2, "workspace_y": 2, "workspace_z": 2, "payload_kg": 0.2}
70
+ morph = torch.tensor([raw.get(f, 0) * _sc.get(f, 1) for f in MORPH_FIELDS],
71
+ dtype=torch.float32)
72
+
73
+ image_keys = sorted(k for k in ds.meta.features if k.startswith("observation.images"))
74
+
75
+ def to_batch(item, emb_id, morph_vec):
76
+ import re
77
+ from tinyvla.data.mixture import PRIMARY_RE, WRIST_RE
78
+ prim = next((k for k in image_keys if PRIMARY_RE.search(k)), image_keys[0])
79
+ wr = next((k for k in image_keys if k != prim and WRIST_RE.search(k)), None)
80
+
81
+ def img(x):
82
+ if x.shape[-2:] != (cfg.image_size, cfg.image_size):
83
+ x = torch.nn.functional.interpolate(x[None], size=(cfg.image_size, cfg.image_size),
84
+ mode="bilinear", align_corners=False)[0]
85
+ return x
86
+ cam0 = img(item[prim])
87
+ cam1 = img(item[wr]) if wr else torch.zeros_like(cam0)
88
+ state = item["observation.state"].float()
89
+ state = torch.nn.functional.pad(state, (0, cfg.max_state_dim - state.shape[-1]))
90
+ t = tok([item.get("task") or ""], padding=True, truncation=True,
91
+ max_length=cfg.tokenizer_max_length, return_tensors="pt")
92
+ b = {"observation.images.cam0": cam0[None].cuda(),
93
+ "observation.images.cam1": cam1[None].cuda(),
94
+ "observation.state": state[None].cuda(),
95
+ "observation.language.tokens": t["input_ids"].cuda(),
96
+ "observation.language.attention_mask": t["attention_mask"].bool().cuda(),
97
+ "embodiment_id": torch.tensor([emb_id], device="cuda")}
98
+ if morph_vec is not None:
99
+ b["morphology"] = morph_vec[None].cuda()
100
+ return b
101
+
102
+ # native action stats (for variant A unnormalization) — physical EE deltas
103
+ native_stats = ds.meta.stats.get("action", {})
104
+ nat_mean = np.asarray(native_stats.get("mean", np.zeros(7)))
105
+ nat_std = np.asarray(native_stats.get("std", np.ones(7)))
106
+ is_native = cfg.action_space == "native"
107
+
108
+ def _unnorm_canon(x):
109
+ span = np.maximum(q99 - q01, 0.01 * np.median(np.abs(np.concatenate([q01, q99])) + 1e-6))
110
+ mid = 0.5 * (q01 + q99)
111
+ return x[:, :7] * span / 2 + mid
112
+
113
+ def _integrate(deltas):
114
+ """(T,6+) physical per-step EE deltas -> (T,3) cumulative positions,
115
+ list of cumulative rotations. Amplifies per-step differences into a
116
+ trajectory with real dynamic range (per-step motion is tiny)."""
117
+ pos = np.cumsum(deltas[:, :3], axis=0)
118
+ R = Rotation.identity()
119
+ rots = []
120
+ for k in range(len(deltas)):
121
+ R = Rotation.from_rotvec(deltas[k, 3:6]) * R
122
+ rots.append(R)
123
+ return pos, rots
124
+
125
+ def canonical_phys_err(pred_norm, gt_canon):
126
+ """Integrated-trajectory error (the per-step delta floor ~3.6mm has no
127
+ dynamic range — see control). Returns:
128
+ ep_mm : endpoint position error over the 5s chunk (mm)
129
+ path_mm: mean cumulative-position error along the chunk (mm)
130
+ ep_deg : endpoint cumulative-rotation error (deg)
131
+ """
132
+ gu = _unnorm_canon(gt_canon)
133
+ if is_native:
134
+ d = pred_norm.shape[-1]
135
+ pu = pred_norm[:, : min(d, 7)] * nat_std[: min(d, 7)] + nat_mean[: min(d, 7)]
136
+ if pu.shape[-1] < 7:
137
+ pu = np.concatenate([pu, gu[:, pu.shape[-1]:7]], axis=1)
138
+ else:
139
+ pu = _unnorm_canon(pred_norm)
140
+ gp, gr = _integrate(gu)
141
+ pp, pr = _integrate(pu)
142
+ ep_mm = np.linalg.norm(pp[-1] - gp[-1]) * 1000
143
+ path_mm = np.linalg.norm(pp - gp, axis=1).mean() * 1000
144
+ ep_deg = np.degrees((pr[-1] * gr[-1].inv()).magnitude())
145
+ return ep_mm, path_mm, ep_deg
146
+ pos_mm = np.linalg.norm(pu[:, :3] - gu[:, :3], axis=1).mean() * 1000
147
+ # rotation error: geodesic between rotvec deltas
148
+ rp = Rotation.from_rotvec(pu[:, 3:6])
149
+ rg = Rotation.from_rotvec(gu[:, 3:6])
150
+ rot_deg = np.degrees((rp * rg.inv()).magnitude()).mean()
151
+ grip = np.abs(pu[:, 6] - gu[:, 6]).mean()
152
+ return pos_mm, rot_deg, grip
153
+
154
+ eps = list(range(args.ep_start, min(args.ep_start + args.episodes, ds.num_episodes)))
155
+ # candidate embodiment conditionings
156
+ if cfg.conditioning == "morph":
157
+ candidates = [("morph", morph)]
158
+ else:
159
+ candidates = [(f"id{i}", i) for i in range(args.oracle_ids)]
160
+
161
+ best = None
162
+ for label, cand in candidates:
163
+ errs = []
164
+ for ep in eps:
165
+ start = int(ds.meta.episodes["dataset_from_index"][ep])
166
+ end = int(ds.meta.episodes["dataset_to_index"][ep])
167
+ for idx in range(start, end - 1, args.stride):
168
+ gt = quantile_normalize(store.chunk_for(ep, idx - start), q01, q99)
169
+ if cfg.conditioning == "morph":
170
+ b = to_batch(ds[idx], 0, cand)
171
+ else:
172
+ b = to_batch(ds[idx], cand, None)
173
+ pred = policy.predict_action_chunk(b)[0].cpu().numpy()
174
+ errs.append(canonical_phys_err(pred, gt))
175
+ errs = np.array(errs)
176
+ m = errs.mean(0)
177
+ if best is None or m[0] < best[1][0]:
178
+ best = (label, m)
179
+ print(f" {label}: endpoint {m[0]:.1f}mm path {m[1]:.1f}mm rot {m[2]:.1f}deg")
180
+
181
+ print(f"\n=== {args.dataset} | {args.checkpoint.name} | cond={cfg.conditioning} ===")
182
+ print(f"BEST ({best[0]}): endpoint {best[1][0]:.1f}mm path {best[1][1]:.1f}mm rot {best[1][2]:.1f}deg")
183
+
184
+
185
+ if __name__ == "__main__":
186
+ main()