microduck-walk-rough

Walking gait fine-tuned on rough terrain from Nbz-Bread/microduck-walk (velocity + head/body pose command).

A perpetual policy for the microduck (61-D observation, 14 actions, 50 Hz). Runs until told otherwise โ€” a gait for the walk slot.

Run it on a robot

sudo robotctl policy load walk Nbz-Bread/microduck-walk-rough

The observation normalizer is baked into policy.onnx; feed raw observations. manifest.json follows schema 2 of the microduck policy manifest (docs/policy-manifest.md in the daemon repo).

A remix of Nbz-Bread/microduck-walk.

Demo

12 s simulation rollout of the final checkpoint (replay.mp4), one take. It was rendered on the stairs and uneven-grid terrain only (no flat patches), at the hardest difficulty rows (0.9-1.0), with a steady forward command of 0.25-0.3 m/s. That is the top of the difficulty range used in training, where it still falls on about 1 in 6 episodes (see Evaluation).

How it was fine-tuned

This is a fine-tune of Nbz-Bread/microduck-walk (the same velocity-command walking gait, trained 50,000 iterations on flat ground, run syozsxo0). Same robot, same 61-D observation and 14 actions, same rewards and domain randomization; only the terrain changes.

  • Start: the base policy's final checkpoint (model_49999.pt), weights, observation normalizer and optimizer.
  • Warm start: MICRODUCK_WARM_START=1, so the iteration counter and the step-based curricula (reward-weight ramps, command and DR ranges) restart from 0 instead of jumping to their final stage.
  • Task: Mjlab-Velocity-Rough-MicroDuck. Terrain is a grid of 8 m patches in random mode: every patch independently draws a type (flat 25%, stairs 25%, uneven grid 30%, gentle slopes 20%) and a difficulty uniformly from 0 to 1. Difficulty scales the feature size linearly: stairs up to 1.5 cm steps, uneven grid up to 1 cm blocks, slopes about 1.7 to 5.7 degrees. So training saw every difficulty, including the hardest, from the first iteration. The training config ended up as a 5 x 5 patch grid (25 patches), not the 10 x 20 grid defined in the task file, because the play-mode settings are written onto the same shared terrain object; the patch draw uses a random seed that was not recorded.
  • Terrain level: mjlab's terrain_levels curriculum moves robots between the 5 grid rows (levels 0-4) depending on how far they walk. In random mode a row does not set the difficulty, so the "terrain level" in the log is not a difficulty measure.
  • Budget: 20,000 iterations with 2048 parallel envs (4096 ran out of the 16 GB GPU memory on the rough task's larger contact buffers), about 14 h 20 min on an RTX 5060 Ti.
MICRODUCK_WARM_START=1 uv run train Mjlab-Velocity-Rough-MicroDuck \
  --env.scene.num-envs 2048 --agent.max_iterations 20000 \
  --agent.resume True --agent.load-run 2026-10-27_20-47-38_velocity \
  --agent.load-checkpoint model_49999.pt --agent.run-name rough_finetune

Performance

From the training log (wandb run nubletz-mianbao/mjlab_microduck/lfg93zyk), simulation only, domain randomization on, each column averaged over a 500-iteration window:

Metric iter 0-0.5k iter 2.0-2.5k iter 10.0-10.5k final 500
Mean reward 125.3 115.4 118.2 117.5
Mean episode length (steps, cap 1000) 923 969 974 970
Linear velocity error error_vel_xy 0.487 0.446 0.448 0.451
Yaw rate error error_vel_yaw 1.718 1.219 1.180 1.201
Foot slip velocity 0.044 0.027 0.027 0.027
Falls per step (fell_over) 0.201 0.163 0.138 0.159
Timeouts per step (time_out) 2.189 2.245 2.253 2.236
Fall share 8.4% 6.8% 5.8% 6.6%
Terrain row index, mean (0-4) 0.45 0.002 0.002 0.002
Terrain row index, max (0-4) 3.2 1.0 1.0 1.0

Fall share = fell_over / (fell_over + time_out), summed over each window (both are counts of episode endings per env step; an episode that does not fall runs to the 1000-step limit).

The terrain row index only says which grid row a robot sits in (most robots were moved to row 0); it is not a difficulty measure (see "How it was fine-tuned").

Fall share and terrain row index during the fine-tune

Evaluation: base vs this fine-tune

Headless battery (scripts/eval_terrain.py in the training repo): both policies run in the same rough-terrain env, one terrain type at a fixed difficulty (0.05 to 1.0), 256 envs for 2,100 steps (about 500 episodes per cell), the play-config commands and domain randomization, and pushes at the training interval. Fall share = fell_over / (fell_over + time_out). Base = Nbz-Bread/microduck-walk (model_49999.pt), fine-tune = this repo (model_19999.pt).

Terrain Difficulty Base falls Fine-tune falls
stairs 0.05 5.0% 3.1%
stairs 0.5 8.0% 4.5%
stairs 0.75 23.3% 8.7%
stairs 1.0 37.5% 17.6%
uneven grid 0.05 3.1% 5.4%
uneven grid 0.5 7.0% 6.6%
uneven grid 0.75 18.9% 7.0%
uneven grid 1.0 37.0% 17.7%
slope 0.05 5.6% 3.1%
slope 0.5 6.2% 3.9%
slope 0.75 5.7% 5.3%
slope 1.0 5.9% 4.1%
flat 0.05 4.9% 2.7%
flat 0.5 4.7% 2.1%
flat 0.75 5.5% 4.3%
flat 1.0 6.2% 4.5%

Fall share by terrain and difficulty

  • Hard stairs and uneven grid (difficulty 0.75-1.0): the fine-tune falls about half as often, which fits it having trained on those difficulties while the base only saw flat ground. Stairs at 1.0: 37.5% to 17.6%; uneven grid at 1.0: 37.0% to 17.7%; stairs at 0.75: 23.3% to 8.7%.
  • Easy terrain and slopes: little difference. Up to difficulty 0.5, and on slopes at every difficulty, both policies stay at about 3-7%. On flat ground the fine-tune is a little lower in this run (about 3.5% vs 5.4% averaged over the five flat cells).
  • Noise: the five flat cells are replicates of the same terrain, and they spread over 4.7-6.2% (base) and 2.2-4.5% (fine-tune). Differences of 1-2 points are not meaningful; the stairs and grid gaps at 0.75-1.0 are far larger.
  • Absolute numbers are higher than the training-log figures below because this evaluation uses the play config, not the training curriculum state.

Limitations

  • The fall share in the training log did not improve over the fine-tune: 8.4% in the first window, 5-7% afterwards, 6.6% at the end. That is for the training terrain mix (all difficulties from 0 to 1) and is not comparable to the base policy's 2.3% on flat ground.
  • Small, random training terrain: only 25 patches, drawn at random with an unrecorded seed, so how many hard patches the policy actually saw is unknown. The 10 x 20 grid in the task file is not what training used.
  • It still falls on about 18% of episodes on the hardest stairs and uneven grid (difficulty 1.0).
  • Yaw tracking error ended at 1.20 in training, against 0.81 for the base policy on flat ground (different terrain).
  • The demo clip is one take on the hardest difficulty; about 1 in 6 episodes there ends in a fall.
  • Single evaluation run per cell (about 500 episodes), simulation only, no real-robot testing.

Training

  • task_id: Mjlab-Velocity-Rough-MicroDuck
  • repo: pollen-robotics/microduck_rl
  • branch: develop
  • commit: b02fdea49
  • run: nubletz-mianbao/mjlab_microduck/lfg93zyk
  • checkpoint: 19999
Downloads last month
18
Video Preview
loading

Model tree for Nbz-Bread/microduck-walk-rough

Finetuned
(1)
this model