Unitree A1 · DreamWaQ++ · clean-Isaac evals

Deploy Candidate Scoreboard

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.

falls @3.0 survival % under a 3 m/s push · higher better curb % climbing ≥3 stairs (stair_eval) · higher better Tss projected hottest-calf °C · lower better mechCoT mechanical cost of transport · lower better

Master scoreboard

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.

policyswing (m)falls @3.0curb ≥3Tss °CmechCoT
arm5 · Tall36Clear · high swing (foot_clearance −2.5)
s1 †high2953.962.4—
seed2high8453.560.41.212
seed3high3152.761.2—
seed4high6948.061.9—
arm4 · Tall36Sharp · low swing (foot_clearance −1.0)
s10.0342450.864.0—
seed20.0352346.158.70.856
seed30.0327152.086.70.703
seed40.0357050.862.20.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).

Fall-rate detail

Survival % across push magnitude (push_eval, Kp34/Kd0.8, 256 envs). The 3.0 column discriminates; ≤2.0 is near-100 everywhere.

policy1.5 m/s2.02.53.0drop-off
arm5
s198926329
seed2100999984
seed397937531
seed4100999669
arm4
s199917124
seed298917723
seed398938771
seed498929170

seed2 checkpoint sweep

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.

iterationfalls @3.0curb ≥3verdict
15007938.3under-converged
20008453.9tied-best
25005453.1fall pothole
29998454.3best pick

Gain sweeps seed2 @2999

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.

Kp × Kd → fall survival @3.0 m/s

 Kd 0.6Kd 0.7Kd 0.8
Kp 28888262
Kp 31878274
Kp 34888982 ·dep

Lower Kd = more robust. Kp34/Kd0.7 is the optimum (89); the deployed Kd0.8 (82) is the worst damping.

Grav-ff × Kp → stance droop & torque (flat, Kd0.8)

Kpgrav-fftrunk h (m)droopτ²/m
280.00.33426 mm192
280.50.34317 mm174
281.00.3555 mm181
310.00.33921 mm187
310.50.35010 mm172
311.00.3573 mm207
340.00.35010 mm185
340.50.3528 mm189
341.00.364−4 mm266

★ = 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.

↳ Recommended deploy config: 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.

Cost of transport

Mechanical CoT = ∫|τ·ω| / (m g d), the swing-sensitive energy term (stance holding is copper/τ², measured separately). Identical gains, flat, 1 m/s.

policyswingmech CoTmech powervs seed2
arm5 seed2high1.212142.6 J/mbaseline
arm4 seed4low0.885104.2 J/m−27%
arm4 seed2low0.856100.7 J/m−29%
arm4 seed3low0.70382.7 J/m−42% but 86.7°C

Key findings

Multi-objective arm

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.

✓ n=5 — the recipe is real, but with honest seed spread. Every mo1 seed walks, is efficient (mechCoT 0.67–0.86), symmetric (Isaac L/R 1.01), and curb-capable (~50%). What the two extra seeds exposed that n=3 flattered: robustness and thermal vary seed-to-seed — falls 67–100%, Tss 58.9–64.5 °C — so seed selection still matters (seed1/3/6 strong; seed5 weak, hot). The robustness-vs-efficiency trade is still not fundamental; the mirror-loss variant remains dropped.
policyfalls @3.0curb ≥3Tss °CmechCoTIsaac L/R
reference (previous best of each earlier arm)
arm5 seed28453.560.41.212~1.04
arm4 seed47050.862.20.885—
mo1 · multi-obj (joint_power −6e-5 + torque_spread + push ±2.0) · n=5
seed1 deployed985458.90.8621.01
seed2895059.90.8411.01
seed31005160.70.7591.01
seed5 weak draw675864.50.7941.01
seed6985063.30.6701.00
mean905361.50.7851.01
mo1-sym · mo1 + mirror loss · n=3
seed1915159.50.8021.00
seed21004761.80.9071.00
seed3734762.00.8111.01
mean884861.10.8401.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).

Stair-mounting capability — the efficiency trade

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.

Confirmed: mo1 trades stair-mounting for efficiency. At 0.14 m steps all arms are ~50%. At 0.23 m mo1 collapses to 3.5% while stand_spread holds 36.7% (10×). The climbers (stand_spread, fc05) lack mo1's 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).
armd0.5 · 0.14 md0.8 · 0.19 md1.0 · 0.23 m
mo1 deployed56363.5
climb arm episode 40 s — no gain56340.4
tallprd547368.6
fc05485023
stand_spread545436.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.

armd0.5 · 0.14 md0.8 · 0.19 md1.0 · 0.23 m
mo158.632.84.7
joint_power-gated no gain — refutes the hypothesis54.328.90.8
soft-kernel std 0.5 — refutes the hypothesis43.423.43.5
push ±1.0 calmer gait, but no climb — refutes the hypothesis53.521.11.2
stand_spread52.344.131.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.

armfalls @3.0Tss °CmechCoTL/R
push ±1.0760.10.7151.00
mo1 seed1 deployed9858.90.8621.01
mo1 mean (n=5)9061.50.7851.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.0curb ≥3 (0.14 m)Tss °CmechCoT
fc051653.9124.40.729
tallprd54555.571.61.001
stand_spread4748.460.40.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.