Sim to real · Whole-body control · October 2026

One policy moves
the whole robot.

A learned whole-body controller for MILO, a wheeled robot with a lift, a pitching torso, a 3-DoF neck and two 7-DoF arms. Give it where the head and both wrists should be; it moves the base, lift, torso and arms together to put them there. Trained with PPO in Isaac Lab 3 on the Newton physics backend. At run time it reads only what the real robot measures: its posture reference is solved by inverse kinematics from the targets, not taken from the simulator.

Montage: rollouts rendered from the Newton simulator (kinematic replay in MuJoCo). Orange and blue: wrist targets; pink: head target. Props are placed from the targets for context; the controller never sees them.

Results

99.4 % of episodes
at full difficulty.

4096 evaluation episodes of 20 s, curriculum at its widest, every domain randomisation on (masses, hand payload, centre of mass, joint friction, actuator gains, gravity feed-forward error, command delay, lift speed, base acceleration and traction, shoves). An episode succeeds if the robot neither falls, hits a joint end-stop, holds a joint at its torque limit for 0.25 s, collides with itself or loses a target, and its mean errors stay under 5 cm and 0.25 rad for the wrists and the head.

PolicySuccessEpisodesWrist pos.Wrist rot.Head pos.Head rot.
Deployable: reference posture from IK on the targets99.4 %40961.4 cm0.047 rad2.0 cm0.038 rad
Same network, simulator reference posture (upper bound)99.9 %40961.1 cm0.047 rad0.7 cm0.039 rad

Mean errors over each episode after the first second. Failed episodes, by first failed criterion: early termination 9, wrist pos 10, head pos 8. Mean change of the policy's action per 20 ms step: 0.198 (actions are in units of the per-joint scale). All numbers are in simulation; nothing here has run on the robot yet. Evaluation ↗

Design

What the robot
is asked, and how it answers.

The structure follows Dyna Robotics' Dyna-2.1 controller: task-space targets in, joint targets and base velocity out, priorities in the reward weights, gains, masses, friction and delays randomised. The controller is adapted to MILO's hardware interface, read from the MILO code base.

In (deployable)Head and wrist targets in the base frame, current and previous step; footprint reference and its velocity; joint position, velocity and motor torque; base twist and gravity direction from odometry and IMU; the lift height and actions it sent.
Policy (50 Hz)MLP 512-256-128 with PPO. Besides the sensors it reads a reference posture solved by whole-body IK from the targets (two damped Gauss-Newton steps per control step, warm-started). Outputs lift height, torso and 14 arm setpoints and a small base residual.
Controller layersFootprint tracking: reference velocity + odometry feedback + the policy's residual. Neck: IK from the head-orientation target, the split MILO's own teleop retargeter uses.
Plant (as on MILO)Motor-side MIT law with gravity feed-forward and per-joint clamps (7–40 N m), setpoint slew 1.5–4 rad/s, lift at 35 mm/s, swerve base with 0.75 m/s² limit and traction cap; tipping is physical.

Rewards: position and orientation tracking (wrists and head equal), footprint, posture (towards the human-like reference), mechanical power, torque, action rate, drift (motion the target does not ask for), support margin of the zero-moment point in the wheel triangle, tilt, self-collision, joint-range proximity. Terminations: fall (tilt over 0.25 rad), joint end-stop, sustained torque saturation, collision, lost target, time-out.

Targets come from 125 k feasible keyframes: inside the joint ranges with margin, collision-free, holdable within the torque limits, wrists in front of the body and below 1.85 m, and with the head looking at a hand in 70 % of them. They are chained into human-paced behaviours (working along a table, reaching up to a shelf, reaching low, carrying while driving, side reaches) and a curriculum grows them from small motions around the robot to the full workspace.

Rollouts

Behaviours.

Segments of evaluation episodes (full difficulty, randomisation on), rendered at 1x.

Engineering log

What broke,
and what fixed it.

SymptomCauseFix
Arm roll joints oscillate at zero actionexplicit 200 Hz PD on low-inertia jointsMIT gains as the solver's implicit drive (the motor loop runs at kHz)
Base does not moveMuJoCo takes the larger friction of a contact pairlow floor and wheel friction; traction lives in the base model
Joints hit their end-stopsready pose 0.08 rad from the J2/J4 stops; torso saggingIK ready pose with 0.3 rad clearance; keyframes 0.15 rad and setpoints 0.12 rad from every stop
Torso saturates under explorationone action scale for all jointsper-joint action scales
Curriculum never starts near the robotshelf and side poses are far from the default by definitioneach goal = default + level x (library pose - default)
Commands dither up to 0.8 rad per stepthe hardware slew hides dithering from trackingaction-rate penalty x10
A 99 % policy degrades with more trainingrunning observation normaliser drifts with the policynormaliser folded into the first layer, then frozen
Base heading drifts, arms compensateno feed-forward for the footprint; weak yaw loopfootprint tracking layer under the policy (reference velocity + odometry feedback)
Neck and torso learn stuck offsetsPPO settles in coordinated local optima of a 20-DoF redundant bodyneck IK for head orientation; posture reward; a reference posture input
High shelf and side reaches never convergeactions clipped at 1.5 x 0.8 rad: setpoints limited to +-1.2 rad around the ready posewider action clip; velocity feed-forward as MILO's MIT stream sends it
Policy without the reference posture stays at 26 % at full difficultythe posture is what resolves the redundancysolve it: whole-body IK from the targets (15 constraints, 16 joints) gives it to within 0.003 rad
Training degrades as targets grow8 % of keyframes needed more than the torso's torque limit to holdkeyframes must be holdable at <= 45 % (torso) / 60 % (arms) of the clamp

Limitations

What is measured.
What is not.

  1. Simulation only. The controller has not run on MILO. The plant model is built from MILO's code, gains and logs, and its dynamics are randomised rather than identified.
  2. Torso torque is assumed. MILO's driver clamps the torso at 10 N m and relies on joint friction; holding the torso pitched needs 17 N m or more. Training uses 40 N m. This must be checked on the motor before deployment.
  3. Shoulder motor identity is unresolved. MILO's records disagree on J1/J2 (HM9816G9 vs DM-J8009); 40 N m is used. MILO's HM torque feedback is reported to read 2–3x high.
  4. MILO cannot reach the floor. With the lift down and the torso fully pitched the lowest wrist position is about 0.5 m; "reach low" means bins and bottom shelves.
  5. Collision is approximate. Self- and floor-contact use a sphere model; fingers are not modelled.
  6. Targets are procedural. They come from a feasible pose library and behaviour scripts, not from human motion capture.

Code: the wbc/ harness in the Rehearse repository (private), with the model assumptions in docs/MODEL.md. Isaac Lab 3.0 with the Newton (MuJoCo-Warp) backend, RSL-RL 5.5. Videos are kinematic replays of simulator states rendered in MuJoCo.