Unitree A1 · DreamWaQ++ · clean-Isaac evals
Every trained arm & seed measured on the four axes that decide deployment, all in Isaac PhysX (the MuJoCo terrace was retracted — foot-clipping faked both its thermal and curb numbers). Gain & grav-ff sweeps for the front-runner below. Built to extend with the multi-objective arm.
All checkpoints at model_2999. Cell color = good→bad for that metric's own direction. arm4 = low swing (~0.034 m foot), arm5 = high swing.
| policy | swing (m) | falls @3.0 | curb ≥3 | Tss °C | mechCoT |
|---|---|---|---|---|---|
| arm5 · Tall36Clear · high swing (foot_clearance −2.5) | |||||
| s1 † | high | 29 | 53.9 | 62.4 | — |
| seed2 | high | 84 | 53.5 | 60.4 | 1.212 |
| seed3 | high | 31 | 52.7 | 61.2 | — |
| seed4 | high | 69 | 48.0 | 61.9 | — |
| arm4 · Tall36Sharp · low swing (foot_clearance −1.0) | |||||
| s1 | 0.034 | 24 | 50.8 | 64.0 | — |
| seed2 | 0.035 | 23 | 46.1 | 58.7 | 0.856 |
| seed3 | 0.032 | 71 | 52.0 | 86.7 | 0.703 |
| seed4 | 0.035 | 70 | 50.8 | 62.2 | 0.885 |
† arm5 s1 is a dead rear-leg-drag basin: its "survival" is stuck-but-upright, not a real gait. mechCoT measured only for the CoT-comparison subset. Deploy pick: arm5 seed2 — best falls (84), best curb (53.5), coolest (60.4), but thirstiest (mechCoT 1.21).
Survival % across push magnitude (push_eval, Kp34/Kd0.8, 256 envs). The 3.0 column discriminates; ≤2.0 is near-100 everywhere.
| policy | 1.5 m/s | 2.0 | 2.5 | 3.0 | drop-off |
|---|---|---|---|---|---|
| arm5 | |||||
| s1 | 98 | 92 | 63 | 29 | |
| seed2 | 100 | 99 | 99 | 84 | |
| seed3 | 97 | 93 | 75 | 31 | |
| seed4 | 100 | 99 | 96 | 69 | |
| arm4 | |||||
| s1 | 99 | 91 | 71 | 24 | |
| seed2 | 98 | 91 | 77 | 23 | |
| seed3 | 98 | 93 | 87 | 71 | |
| seed4 | 98 | 92 | 91 | 70 | |
Why we don't just take model_2999: checkpoint quality is non-monotonic and invisible in TensorBoard (7/16 prior checkpoints didn't walk under the deterministic action). iter 2500 is a 30-point fall pothole; 2999 is genuinely best.
| iteration | falls @3.0 | curb ≥3 | verdict |
|---|---|---|---|
| 1500 | 79 | 38.3 | under-converged |
| 2000 | 84 | 53.9 | tied-best |
| 2500 | 54 | 53.1 | fall pothole |
| 2999 | 84 | 54.3 | best pick |
Trained DR: Kp 22–34.5 / Kd 0.45–0.80. Deployed at Kp34/Kd0.8/grav-ff1.0 — the top edge, and sub-optimal on every count below.
| Kd 0.6 | Kd 0.7 | Kd 0.8 | |
|---|---|---|---|
| Kp 28 | 88 | 82 | 62 |
| Kp 31 | 87 | 82 | 74 |
| Kp 34 | 88 | 89 | 82 ·dep |
Lower Kd = more robust. Kp34/Kd0.7 is the optimum (89); the deployed Kd0.8 (82) is the worst damping.
| Kp | grav-ff | trunk h (m) | droop | τ²/m |
|---|---|---|---|---|
| 28 | 0.0 | 0.334 | 26 mm | 192 |
| 28 | 0.5 | 0.343 | 17 mm | 174 |
| 28 | 1.0 | 0.355 | 5 mm | 181 |
| 31 | 0.0 | 0.339 | 21 mm | 187 |
| 31 | 0.5 | 0.350 | 10 mm | 172 |
| 31 | 1.0 | 0.357 | 3 mm | 207 |
| 34 | 0.0 | 0.350 | 10 mm | 185 |
| 34 | 0.5 | 0.352 | 8 mm | 189 |
| 34 | 1.0 | 0.364 | −4 mm | 266 |
★ = current deploy point. grav-ff cancels droop (more at low Kp) but the deployed Kp34/grav-ff1.0 over-shoots stance and spikes torque to 266 (vs ~185). Better: grav-ff 0.5.
arm5 seed2 @ model_2999, gains Kp34 / Kd0.7 / grav-ff0.5 — beats the current Kp34/Kd0.8/grav-ff1.0 on falls (89 vs 82%) and torque (189 vs 266), same stance hold. Pending robot permission.
Mechanical CoT = ∫|τ·ω| / (m g d), the swing-sensitive energy term (stance holding is copper/τ², measured separately). Identical gains, flat, 1 m/s.
| policy | swing | mech CoT | mech power | vs seed2 |
|---|---|---|---|---|
| arm5 seed2 | high | 1.212 | 142.6 J/m | baseline |
| arm4 seed4 | low | 0.885 | 104.2 J/m | −27% |
| arm4 seed2 | low | 0.856 | 100.7 J/m | −29% |
| arm4 seed3 | low | 0.703 | 82.7 J/m | −42% but 86.7°C |
foot_clearance −2.5 bought zero curb capability — climbing is stance-push, not swing height.The principled attempt to collapse the robustness-vs-efficiency frontier: arm4 base + efficiency (joint_power −6e-5) + thermal safety (torque_spread) + robustness (push ±2.0). Target: a seed that dominates seed2 on all axes — or an honest Pareto frontier if they trade.
| policy | falls @3.0 | curb ≥3 | Tss °C | mechCoT | Isaac L/R |
|---|---|---|---|---|---|
| reference (previous best of each earlier arm) | |||||
| arm5 seed2 | 84 | 53.5 | 60.4 | 1.212 | ~1.04 |
| arm4 seed4 | 70 | 50.8 | 62.2 | 0.885 | — |
| mo1 · multi-obj (joint_power −6e-5 + torque_spread + push ±2.0) · n=5 | |||||
| seed1 deployed | 98 | 54 | 58.9 | 0.862 | 1.01 |
| seed2 | 89 | 50 | 59.9 | 0.841 | 1.01 |
| seed3 | 100 | 51 | 60.7 | 0.759 | 1.01 |
| seed5 weak draw | 67 | 58 | 64.5 | 0.794 | 1.01 |
| seed6 | 98 | 50 | 63.3 | 0.670 | 1.00 |
| mean | 90 | 53 | 61.5 | 0.785 | 1.01 |
| mo1-sym · mo1 + mirror loss · n=3 | |||||
| seed1 | 91 | 51 | 59.5 | 0.802 | 1.00 |
| seed2 | 100 | 47 | 61.8 | 0.907 | 1.00 |
| seed3 | 73 | 47 | 62.0 | 0.811 | 1.01 |
| mean | 88 | 48 | 61.1 | 0.840 | 1.00 |
mo1 (plain) is the recipe; seed1 stays the deployed pick. All five seeds walk, stay efficient (0.67–0.86), symmetric (L/R 1.01), and curb ~50% (a config property — every arm lands there). But the extra two seeds broke the "dominates every axis" story: falls now span 67–100% and Tss 58.9–64.5 °C. seed6 is excellent (0.67 CoT, 98% falls) but runs hot; seed5 is a genuine dud (67% falls, 64.5 °C). So the recipe reliably yields a good gait, but not every seed is deploy-grade — pick the seed (seed1/3/6 pass, seed5 fails). Consistent with this body's known seed/checkpoint variance. Symmetry, by contrast, is rock-solid across all five (L/R 1.01).
Mirror loss (mo1-sym) is a net negative at n=3. It's no more symmetric (both recipes ~1.00 in Isaac), less robust with a wider spread (mean 88 vs 96, seed3 down at 73%), and ~1.3 °C hotter (two seeds cross 60 °C at ~17 min); efficiency and curb are a wash. So it's dropped.
Why symmetry was never the lever. The asymmetry that motivated it (MuJoCo calf L/R 1.10) was a measurement artifact: the menagerie trunk carries the same +4.1 mm COM-y bias Isaac zeroes, and mujoco_walk_check wasn't removing it — so it measured a body the policy never trained on. On the symmetric body both recipes are L/R ≈ 1.00, so the mirror loss had nothing to fix. (Implementation aside: use_mirror_loss had been a silent no-op — the hand-copied CENet update() dropped the mirror-loss term; it was fixed and Loss/symmetry now converges, but the result stands.) The 3 MuJoCo harnesses now default to the symmetric body (--no-symmetrise for the raw sim2real asset).
The operator eyeballed that mo1 mounts tall steps less confidently than earlier arms. The d0.5 curb metric (0.14 m steps) can't see it — everything lands ~50%. A taller-step sweep on identical 0.23-range terrain does.
joint_power −6e-5 + torque_spread — mounting a tall step is a high-power, asymmetric push-off, exactly what mo1 penalises. It's the efficiency stack, not the foot-clearance frame (all use ground-relative).
| arm | d0.5 · 0.14 m | d0.8 · 0.19 m | d1.0 · 0.23 m |
|---|---|---|---|
| mo1 deployed | 56 | 36 | 3.5 |
| climb arm episode 40 s — no gain | 56 | 34 | 0.4 |
| tallprd5 | 47 | 36 | 8.6 |
| fc05 | 48 | 50 | 23 |
| stand_spread | 54 | 54 | 36.7 |
curb ≥3 %, climbing 3 steps of that height, identical 0.23-range terrain. Two arms were tried and both failed: the episode-40 s climb arm (no gain — curriculum time can't overcome it) and a joint_power-gated arm (mo1 with the efficiency penalty released on stepped terrain). The gated arm is in the table below.
| arm | d0.5 · 0.14 m | d0.8 · 0.19 m | d1.0 · 0.23 m |
|---|---|---|---|
| mo1 | 58.6 | 32.8 | 4.7 |
| joint_power-gated no gain — refutes the hypothesis | 54.3 | 28.9 | 0.8 |
| soft-kernel std 0.5 — refutes the hypothesis | 43.4 | 23.4 | 3.5 |
| push ±1.0 calmer gait, but no climb — refutes the hypothesis | 53.5 | 21.1 | 1.2 |
| stand_spread | 52.3 | 44.1 | 31.2 |
The joint_power attribution was wrong. The gated arm was the single-variable test — releasing joint_power on stairs bought back no climbing (0.8% vs mo1's 4.7% at 0.23 m; both sims agree). So the −6e-5 penalty was not what suppressed mo1's climbing.
The soft-tracking-kernel hypothesis was also wrong. stand_spread runs a constant soft kernel (std 0.5) where mo1's Tall36Sharp anneals to 0.25 — the theory being that a sharp velocity-error penalty punishes the slow-down needed to mount a riser. The single-variable arm (mo1 + std 0.5) refuted it decisively: soft-kernel is ≤ mo1 at every difficulty (d0.5 43.4 vs 58.6, d0.8 23.4 vs 32.8, d1.0 3.5 ≈ 4.7) and nowhere near stand_spread. Softening the kernel just loosens gait discipline — it climbs no better and (operator-observed, borne out as a higher fall rate) twitches on missed footholds. MuJoCo agrees end-to-end, including at matched iteration 2500 (soft 0.8 vs mo1 1.1 steps). Flat tracking survived (95.6% at cmd 1.0), so the cost is climbing for nothing.
The push-magnitude hypothesis was wrong too — and it was the last one. mo1 raised the disturbance kick ±1.0 → ±2.0; stand_spread kept ±1.0. The single-variable arm (mo1 + push ±1.0, from scratch) did not recover the edge: d1.0 1.2% — below mo1 (3.5–4.7%) and nowhere near stand_spread (31–37%); d0.5 53.5 ≈ mo1, d0.8 21.1 < mo1. MuJoCo agrees (1.5 steps, inside the mo1 seed band 0.7–4.3). But it delivered the calmer gait it was also meant to: 3% MuJoCo falls, the lowest of the whole mo1 family (seed1 6%, seed5 20%), tracking held (94.8%). So ±1.0 is a real calmness/efficiency win — just not the climbing lever, and (see below) it is not a top-end robustness win.
Deploy axes vs the mo1 seeds — a Pareto trade, not a free win. Benched identically (Kp34/Kd0.8): push±1.0 is efficient and cool but collapses on hard shoves — falls@3.0 7% vs the mo1 band's 67–100. Training kicks at ±1.0 never taught it a 3 m/s shove (3× its envelope); its survival curve is 94/70/37/7 across 1.5/2.0/2.5/3.0. It wins efficiency (mechCoT 0.715, cheap end) and ties thermal (Tss 60.1 °C) and symmetry (L/R 1.00). So it sits in the calm/cheap corner: attractive only if the robot faces gentle perturbations, disqualifying if it faces 3 m/s shoves.
| arm | falls @3.0 | Tss °C | mechCoT | L/R |
|---|---|---|---|---|
| push ±1.0 | 7 | 60.1 | 0.715 | 1.00 |
| mo1 seed1 deployed | 98 | 58.9 | 0.862 | 1.01 |
| mo1 mean (n=5) | 90 | 61.5 | 0.785 | 1.01 |
Verdict: stand_spread's climbing edge is not any single config knob. joint_power, kernel, and push were each ruled out alone (and the mo1 seed sweep shows climbing is heavily seed-dependent, 0.7–4.3 steps, none reaching stand_spread's 6.2). The remaining explanation is an interaction — or a seed-lucky basin. No more single-variable arms: the clean next step is a direct A/B of the two full configs at matched seed, or accepting the split — stand_spread the climber, mo1 the efficient/robust deploy pick — as a genuine Pareto frontier rather than one recipe that wins everything.
| arm (pre-mo1) | falls @3.0 | curb ≥3 (0.14 m) | Tss °C | mechCoT |
|---|---|---|---|---|
| fc05 | 16 | 53.9 | 124.4 | 0.729 |
| tallprd5 | 45 | 55.5 | 71.6 | 1.001 |
| stand_spread | 47 | 48.4 | 60.4 | 0.905 |
The pre-mo1 climbers pay for it: far less shove-proof (16–47% vs mo1's 96%) and hotter (fc05 is a 124 °C hot-calf disaster — the basin mo1's torque_spread was built to kill). So mo1's robustness+thermal are real wins; its climbing is the cost. All three single-variable arms (joint_power, kernel, push) are now spent without recovering the edge — the climbing gap is an interaction, not a knob. The remaining move is a full-config A/B or accepting the Pareto split (stand_spread climbs, mo1 deploys).
Generated from docs/RUNLOG.md and logs/ · clean-Isaac evals (push_eval falls, stair_eval curbs, isaac_thermal_table Tss, mujoco_walk_check mechCoT) · A1 DreamWaQ++ deploy campaign.