Skip to content

Commit e575cff

Browse files
Alex-Hacheclaude
andcommitted
feat: 5D delta, replay_sequence, Test Zero PASS (BACKLOG 6A.3)
- Extended move_to_delta to 5D: (dx,dy,dz,dtheta,dgripper) — wrist_roll and gripper changes integrated into the single delta primitive; grasp()/ release() now thin wrappers, segmenter no longer emits them - Segmenter v2 outputs (dx,dy,dz,dtheta,dgripper) per segment; wrist_roll interpolated linearly across IK waypoints in move_cartesian_delta - Added --angle / --dip / --min-dist CLI overrides to segment_trajectory.py; custom density files named episode_NNN_aA_dD_mM.json - Fixed _wait_until_settled: always sends one command before convergence check; gripper excluded from arm-joint error (prevents early exit) - New scripts/replay_sequence.py: reads starting joints from Parquet, loads segment JSON by convention, replays on real hardware via LeRobotInterface directly (bypasses MCP async stack) - Recorded datasets/sticks_v3 (1 episode, 714 frames); pre-segmented at medium/high and four custom angle thresholds (10°–40°) - Test Zero PASSED 2026-05-17: approach→descent→grasp zone→transit→ release→retreat structure correct on hardware; gravity sag in extended- arm sections noted and tracked as 6A.1.x follow-up - Updated EMERGENT_ROBOTICS_PLAN.md with verified status; merged Path A/B into unified VQ-VAE + conditioned policy + RAG-over-codes direction in ARCHITECTURE.md (decisions #8, #9); Phase 6D/6E/6F added to BACKLOG.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c8c3441 commit e575cff

20 files changed

Lines changed: 2857 additions & 134 deletions

‎ARCHITECTURE.md‎

Lines changed: 64 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -60,32 +60,21 @@ about task semantics.
6060

6161
---
6262

63-
## Two Research Paths
63+
## Unified Research Direction (2026-05)
6464

65-
### Path B — The 4-Weekend PoC (ACTIVE, CURRENT FOCUS)
65+
Path A and Path B have been merged. See `EMERGENT_ROBOTICS_PLAN.md` for the full plan.
6666

67-
**Goal**: Demonstrate the full loop — record demos, decompose into primitives, use them
68-
to improve LLM tool-call selection.
67+
**Three-layer architecture with learned interfaces:**
6968

70-
- Object-relative primitives (move_to_object, not move_left 0.05)
71-
- Trivial demo parser (greedy axis segmentation — already built)
72-
- Imitation learning: demo → primitive sequence → fine-tune LLM tool calling
73-
- RAG retrieval: given task description, find similar demo sequences as context
69+
1. **Conditioned policy** (50-100Hz): `(state, latent_code) → delta`. Small MLP/GRU trained by behavioral cloning on segmented demos.
70+
2. **Action VQ-VAE** (self-supervised): trained on delta-sequence segments. Codebook (K≈8-12) IS the vocabulary — discovered, not designed.
71+
3. **LLM planner** (0.5-2Hz): emits sequences of latent codes. Names codes by observing scene-graph before/after of each code across demos. Reactive replan after each code execution.
7472

75-
**Why this first**: It proves the thesis end-to-end without requiring research breakthroughs.
76-
If this works, even crudely, it validates the entire architecture.
73+
**RAG over discovered codes** replaces RAG over raw demos. Every successful run stores `(task, scene_features, code_sequence)`. Retrieval keyed on task embedding + scene graph (object positions, gripper state, starting EE pose) — not task string alone. Few-shot inject into LLM prompt. Cold-start by encoding the original 30 demos through the VQ-VAE.
7774

78-
### Path A — Research Track (FUTURE)
75+
**Why this composition:** the VQ-VAE solves the abstraction mismatch — segmentation and vocabulary are co-discovered from the same self-supervised loss. The LLM stays in its strength zone (discrete reasoning over a small symbol set). The conditioned policy absorbs state-dependent execution detail without needing language. RAG compounds with use without retraining.
7976

80-
**Goal**: Discover the right primitive vocabulary from data, bottom-up.
81-
82-
- High-frequency axis-relative primitives (the current 20 tools)
83-
- Embed tool-call sequences using structured tokenization
84-
- Bottom-up vocabulary discovery: cluster sequences into higher-level skills
85-
- This is where the JEPA + differentiable MPC work eventually connects
86-
87-
**Why later**: This requires solving the abstraction mismatch problem (see below).
88-
Path B proves the architecture; Path A optimizes it.
77+
**Why merged:** Path B's "demo → primitive sequence → fine-tune" assumed hand-designed primitives. Path A's "bottom-up vocabulary discovery" assumed bespoke clustering. The VQ-VAE does both jobs at once, with a proven technique (VQ-BeT, VQ-VAE for action discretization). No need to keep the paths separate.
8978

9079
---
9180

@@ -132,48 +121,68 @@ we're researching — the planning/execution decomposition is. ArUco lets us sid
132121
vision uncertainty entirely for the PoC. The plan is to use a phone as camera via
133122
IP Webcam app, which avoids USB camera driver headaches on Linux.
134123

135-
### 6. No IK — Data-Driven Joint-Space Lookup (CURRENT BLOCKER)
124+
### 6. Joint-Space Lookup Table — ABANDONED (resolved 2026-04-12)
136125

137-
**What**: Replace the placo/PyBullet IK solver with a lookup table built from real
138-
teleoperation data. Teleoperate the arm to a grid of ~75 positions across the workspace,
139-
record (Cartesian_position, joint_angles) pairs. For arbitrary targets, interpolate between
140-
nearest recorded neighbors using KNN + inverse-distance weighting (optionally scipy RBF).
126+
**What was proposed**: Replace placo IK with a KNN lookup over a teleoperated calibration
127+
grid (~75 positions, ruler-measured xyz → joint angles).
141128

142-
**Why**: The available IK solutions for SO-100/101 have inaccurate DH parameters — the
143-
arm moves to wrong positions. This is inherent to 3D-printed arms where the actual
144-
geometry doesn't match the URDF/DH model precisely. The lookup table bypasses this
145-
entirely: the mapping comes from the physical arm, not a kinematic model.
129+
**Why abandoned**: The root cause of "broken IK on hardware" turned out to be a software
130+
bug, not bad DH parameters. `joints_to_cartesian()` and `cartesian_to_joints()` were
131+
silently defaulting all input joints to 0° because LeRobot returns `"shoulder_pan.pos"`
132+
keys but `kinematics.py` looked up `"shoulder_pan"`. Fix: `_normalize_joint_dict()` in
133+
kinematics.py. Validated on hardware with 10cm XY square. placo IK is now accurate
134+
enough; the lookup table approach would be high effort for marginal gain.
146135

147-
**Status**: This is the current blocker. The existing 20 MCP primitives (move_left, etc.)
148-
use placo IK via `kinematics.py`, which produces incorrect positions on real hardware.
149-
Until the lookup table replaces this, Cartesian-space primitives are unreliable, and
150-
segmenter output from teleoperation data maps to primitives that don't execute correctly.
136+
**Residual issue**: position-only IK changes wrist_flex during EE-space moves → small
137+
Z drift when moving in X. Tracked as a known limitation, not a blocker.
151138

152-
**Control pipeline (target architecture)**:
153-
```
154-
MCP plan(skill="move_toward", target="cup")
155-
→ Resolve "cup" → Cartesian position from scene graph
156-
→ JointLookup.solve(target_xyz) → KDTree nearest neighbors
157-
→ inverse-distance interpolation
158-
→ target joint angles
159-
→ minimum_jerk_joint_trajectory(current, target, duration)
160-
→ TrajectoryExecutor: send joints at 50Hz via robot.send_action()
161-
```
139+
### 7. `move_to_delta` as the Canonical EE-Space Primitive (2026-04-19)
140+
141+
**What**: One primitive — `move_to_delta(dx, dy, dz)` — handles all EE-space translation.
142+
Axis-aligned primitives (`move_left`, `move_up`, etc.) are thin aliases that call it with
143+
two zero components. Plans all sub-waypoints upfront from FK seed, chains IK seeds,
144+
uses convergence-based wait + active-hold the final pose against gravity droop.
145+
146+
**Why**: Diagonal moves in a single IK call, no staircase artifacts in segmenter output,
147+
and the prior implementation's two bugs (mid-motion `get_observation()` reads + 200ms
148+
inner interpolation servos couldn't track) are fixed. Hardware-validated: 80-95% reach
149+
on horizontal/down moves, ~6mm gravity-sag floor.
150+
151+
**Known hardware limit**: +Z up moves only reach 25-50% of commanded distance due to
152+
Feetech servo torque insufficient to lift the arm against gravity. Tracked in BACKLOG.
153+
154+
### 8. Emergent Vocabulary via Action VQ-VAE (2026-05)
155+
156+
**What**: Stop hand-designing the LLM's primitive vocabulary. Train a VQ-VAE on delta
157+
sequences from teleop demos. The discrete codebook IS the vocabulary. A conditioned
158+
policy executes any code from any state. The LLM names the codes by observing scene-graph
159+
outcomes and plans by sequencing them.
160+
161+
**Why**: Hand-designed primitives create a ceiling on what the system can learn — the
162+
engineer's intuitions about action granularity may not match the robot's actual behavioral
163+
structure. VQ-VAE is well-understood in low-dim action spaces (VQ-BeT etc.). Self-supervised
164+
on cheap teleop data. Decouples reasoning (LLM) from structure discovery (VQ-VAE) from
165+
control (conditioned policy) — each can be improved independently.
166+
167+
**What this replaces**: the previous plan of "fine-tune the LLM on (task, tool_call_sequence)
168+
pairs over hand-designed primitives." Fine-tuning becomes optional / Phase 7+ if it's even
169+
needed.
170+
171+
### 9. RAG over Discovered Codes (2026-05)
172+
173+
**What**: Retrieve past `(task, scene_features, code_sequence)` tuples by cosine similarity
174+
on task embedding + scene-graph features. Inject the top-K as few-shot in the LLM prompt.
162175

163-
**Calibration grid spec**:
164-
- X: 0.15m–0.35m from base (5 points), Y: ±0.15m (5 points), Z: table to +0.15m (3 heights)
165-
- ~75 positions + 5-10 extra at key locations (home, grasp height)
166-
- Recording: teleoperate with leader arm, record follower joint angles
167-
- Position measurement: manual (ruler) initially, ArUco-based later
168-
- Safety: refuse targets more than ~5cm from any recorded point
176+
**Why this over fine-tuning**: code sequences are tiny (token IDs), self-improving (every
177+
successful run adds an entry), no retraining loop, and the reactive replan loop corrects
178+
when the retrieved sequence diverges from the current scene. Scene-aware retrieval is what
179+
matters — task-string-only retrieval can't tell "pick from far-left" from "pick from center."
169180

170-
### 7. Minimum-Jerk Interpolation in Joint Space
181+
**Cold start**: bootstrap by encoding the original 30 demos through the trained VQ-VAE.
182+
Day one of Phase 4 already has 30 entries.
171183

172-
**What**: Deterministic, closed-form trajectory generation. 20 lines of code.
173-
`x(t) = x₀ + (x_f - x₀) × [10(t/T)³ - 15(t/T)⁴ + 6(t/T)⁵]`, applied per-joint.
174-
**Why**: Produces smooth trajectories from coarse waypoints. No learned trajectory
175-
generation needed. Combined with the lookup table, this gives reliable, smooth motion
176-
from any current position to any target in the recorded workspace.
184+
**Reuses**: existing `decras/imitation/store.py` schema. A `Primitive` becomes
185+
`{code: int, name: str}` instead of `{tool: "move_to_delta", args: {...}}`.
177186

178187
---
179188

‎BACKLOG.md‎

Lines changed: 111 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,8 @@
9191

9292
### 6A.3 Test Zero
9393

94-
- [ ] **Replay segmenter v2 output on hardware** — Take sticks_v2 episode 0, run through segmenter v2 (high density), replay the `move_to_delta` sequence via MCP tools on the real arm. Film it. Compare to the original teleop recording.
95-
- *Done when*: Side-by-side comparison video. Honest assessment: does the arm do the same thing?
96-
- *Time*: 30 min
97-
- *Tag*: `hardware`
98-
- **This is the critical gate.** If this fails, Path B needs rethinking.
94+
- [x] **Replay segmenter v2 output on hardware** — sticks_v3 ep0, tested at 19/21/31/49 primitives. Structure correct (approach → descent → grasp zone → transit → release → retreat). **PASSES.** Gravity sag causes drift in extended-arm sections — tracked separately.
95+
- *Done 2026-05-17*: `scripts/replay_sequence.py` (new) reads starting joints from Parquet and replays via `LeRobotInterface` directly
9996

10097
---
10198

@@ -129,33 +126,131 @@
129126

130127
---
131128

132-
## Phase 6C — Demo Store & Retrieval (AFTER 6A + 6B)
129+
## Phase 6C — Demo Store (DONE, retriever/RAG superseded by Phase 6F)
133130

134-
> Only worth building once Test Zero passes and the LLM loop exists.
131+
> Schema + writer are built. Retriever / RAG / fine-tuning items now live in Phase 6F
132+
> (RAG over discovered codes, not over raw `move_to_delta` sequences).
133+
> See `EMERGENT_ROBOTICS_PLAN.md` and ARCHITECTURE.md decisions #8 and #9.
135134
136135
- [x] **Design demo store schema** — `decras/imitation/retrieval.py` — `Demo`, `Primitive`, `DemoMetadata` dataclasses.
136+
- [x] **Build demo store writer** — `decras.imitation.store` (save_demo/load_demo/list_demos/ingest_sequence), CLI `scripts/add_demo.py`, deterministic id, provenance fields.
137+
138+
---
139+
140+
## Phase 6D — Data Collection for Vocabulary Discovery (NEXT)
141+
142+
> 30+ teleop demos with deliberate variation so the VQ-VAE can discover meaningful action
143+
> structure. Plan reference: Phase 1 in `EMERGENT_ROBOTICS_PLAN.md`.
144+
>
145+
> **Gate**: Test Zero (6A.3) must pass first. If delta replay fails on hardware, the
146+
> conditioned policy in 6E will fail for the same reason.
147+
148+
### 6D.1 Recorder upgrades
149+
150+
- [ ] **Gripper force in observation** — Plan wants `gripper.force`. Decide: read force from Feetech if supported, or drop force and use gripper position deltas as the open/close signal. Document.
151+
- *Tag*: `research`, then `claude-code`
152+
- *Time*: 30 min
153+
154+
- [ ] **Wire camera frames into recorder** — Capture from `DECRAS_CAMERA` at 5Hz (every 6th control frame), store aligned with Parquet rows. Needed for later JEPA / scene graph annotation.
155+
- *Tag*: `claude-code`
156+
- *Time*: 30 min
157+
158+
### 6D.2 Record 30 demos with variation
137159

138-
- [x] **Build demo store writer** — Segmenter v2 output + task string → Demo JSON on disk. Store at `demos/` with deterministic id `<dataset>_ep<NNN>_<density>.json`. `decras.imitation.store` has `save_demo` / `load_demo` / `list_demos` / `ingest_sequence`. CLI: `uv run python -m scripts.add_demo <sequence.json> --task "..."`. Duplicates raise unless `--overwrite`. Provenance fields added at ingest: `created_at`, `source_sequence_path`, `segmenter_git_sha`.
160+
- [ ] **Pick 2-3 task types** — e.g. cup→plate, block→stack, stick→bin. 10 demos each. Document in `datasets/README.md`.
161+
- *Tag*: `hardware`
162+
163+
- [ ] **Vary starting positions deliberately** — Object positions, arm start poses, approach angles. At least 3 distinct starting regions per task. The plan is explicit: uniform starts kill conditioned-policy generalization.
164+
- *Tag*: `hardware`
165+
- *Time*: 30-45 min per batch of 10
166+
167+
- [ ] **Coverage sanity check** — `scripts/visualize_trajectory.py` over the new dataset. EE paths should span the workspace.
168+
- *Tag*: `claude-code`
169+
170+
---
171+
172+
## Phase 6E — Action VQ-VAE (after 6D)
173+
174+
> ~300 LOC PyTorch. Plan reference: Phase 2.
175+
176+
### 6E.1 Baseline segments
177+
178+
- [ ] **Use segmenter v2 (medium) as segment source** — Already built. Per demo: ordered list of (delta_sequence, start_state, end_state) segments.
139179
- *Tag*: `claude-code`
140180

141-
- [ ] **Build demo retriever** — TF-IDF or sentence-transformer cosine similarity on task descriptions. `retrieve("pick up the stick") → [demo_1, demo_3]`.
181+
- [ ] **Segment-distribution viz** — Histogram of segment lengths, directions, cause (gripper / direction change / speed dip). Informs choice of K.
142182
- *Tag*: `claude-code`
143183

144-
- [ ] **RAG integration** — Inject retrieved demos into LLM prompt via `llm_controller/prompt.py`.
184+
### 6E.2 Train VQ-VAE
145185

146-
- [ ] **Record more demos** — 5-10 episodes of different tasks (pick-place, push, stack).
186+
- [ ] **Build `decras/imitation/vqvae.py`** — 1D CNN or small GRU encoder, learnable codebook (K=8 first; try 12 and 16), decoder reconstructs delta sequence. Reconstruction + commitment losses.
187+
- *Tag*: `claude-code`
188+
- *Time*: 2-3h
189+
190+
- [ ] **`scripts/train_vqvae.py`** — Read `demos/` segments, train, save checkpoint + codebook.
191+
- *Tag*: `claude-code`
192+
193+
### 6E.3 Validate vocabulary
194+
195+
- [ ] **Codebook usage report** — Dead codes → reduce K. Wildly different members → increase K.
196+
- [ ] **Per-code member viz** — Overlay all delta trajectories mapping to code k. Should look similar in direction + speed profile.
197+
- [ ] **Encode all demos to code sequences** — Persist `segment_id → code` and per-demo `code_sequence` into the demo store.
198+
199+
---
200+
201+
## Phase 6F — Conditioned Policy + LLM Grounding + RAG-over-codes
202+
203+
> ~200 LOC for the policy, rest is glue. Plan reference: Phases 3 + 4.
204+
205+
### 6F.1 Conditioned policy
206+
207+
- [ ] **Build `decras/imitation/policy.py`** — Small MLP/GRU. Input: current joints + gripper + one-hot code. Output: next delta + gripper action. BC training on (state_t, gripper_t, code) → (delta_t, gripper_action_t).
208+
- *Tag*: `claude-code`
209+
210+
- [ ] **`scripts/train_policy.py`**
211+
- *Tag*: `claude-code`
212+
213+
- [ ] **Held-out replay test** — Held-out demo, encoded to codes, executed by the policy from the recorded starting state. Does it match the demo?
214+
- *Tag*: `hardware`
215+
216+
- [ ] **Generalization test** — Same code from a different starting state. Sensible behavior?
147217
- *Tag*: `hardware`
148218

149-
- [ ] **Export fine-tuning pairs** — `(task_description, tool_call_sequence)` JSONL for fine-tuning.
219+
### 6F.2 LLM grounding
220+
221+
- [ ] **Collect before/after scene-graphs per code** — For each code, gather scene graphs before/after every segment that maps to it. Feed to LLM, save the assigned name in codebook metadata.
222+
- *Tag*: `research`
223+
224+
### 6F.3 MCP `execute_code(k)` tool
225+
226+
- [ ] **Add `execute_code(code: int)` MCP tool** — Loads conditioned policy, runs at 50Hz from current state + code, returns post-execution scene graph.
227+
- *Tag*: `claude-code`
228+
229+
- [ ] **Keep `move_to_delta` registered** — Useful for debugging and fallback.
230+
231+
### 6F.4 RAG over codes
232+
233+
- [ ] **Extend `Primitive` dataclass** — Add code-mode variant `{code: int, name: str}` alongside the current tool-call variant.
150234
- *Tag*: `claude-code`
151235

236+
- [ ] **Code-sequence retriever** — Cosine similarity over `(task_embedding, scene_features)` where scene_features = object positions + gripper state + starting EE pose. Top-K past code sequences.
237+
- *Tag*: `claude-code`
238+
239+
- [ ] **RAG integration** — Inject retrieved code sequences into `llm_controller/prompt.py`. Bootstrap by encoding all 30 Phase 6D demos.
240+
- *Tag*: `claude-code`
241+
242+
- [ ] **End-to-end demo** — Natural-language task → LLM plans code sequence (with RAG few-shot) → MCP executes via `execute_code` → reactive replan after each code. Film a full pick-and-place.
243+
- *Tag*: `hardware`
244+
152245
---
153246

154-
## Phase 7 — Memory & Context (FUTURE — don't start yet)
247+
## Phase 7 — Rotation, JEPA, Memory (FUTURE)
155248

156-
- [ ] Design persistent memory format (what worked, what failed, object locations)
157-
- [ ] Implement task decomposition in LLM prompt
158-
- [ ] Build error recovery loop (grasp failed → re-observe → retry with adjustment)
249+
- [ ] Extend deltas to (dx, dy, dz, dθ); retrain VQ-VAE + policy with 4D action space
250+
- [ ] JEPA enters: encode (delta_sequence, latent_state_transition) pairs so vocabulary is grounded in what the world does, not just what the robot does
251+
- [ ] Persistent memory across sessions (what worked, what failed, object locations)
252+
- [ ] Error recovery loop (grasp failed → re-observe → retry with adjustment)
253+
- [ ] Optional: fine-tune LLM on accumulated (task, scene, code_sequence) data if RAG hits a ceiling
159254

160255
---
161256

0 commit comments

Comments
 (0)