PD control, gains and armature
A walking policy (the trained neural-network controller of page 16) does not command torques. Fifty times a second it sends each joint a target angle, and a much faster proportional-derivative (PD) loop turns the gap between target and actual angle into torque. How stiff and how damped that loop is, and how much hidden motor inertia sits behind it, decides whether a policy trained in simulation moves the same way on the robot.
Why a humanoid needs this: the “brain” (the policy) is too slow and too coarse to drive each motor’s current directly, the same way you decide “put my foot there” without thinking about each muscle fibre. A small, fast reflex in every joint does the last step. This page is about tuning that reflex, and about one hidden number (the motor’s spinning inertia) that simulators get wrong if nobody writes it down.
The idea in one picture
Section titled “The idea in one picture”Picture the knee as a heavy door on a hinge, with a spring that pulls it towards a chosen angle and a shock absorber that resists speed. The spring’s stiffness is the proportional gain kp, the shock absorber is the derivative gain kd. Move the chosen angle and the door swings towards it. Too little spring and gravity wins; too much spring and too little damping and it overshoots and rings; damping matched to stiffness and inertia and it glides in. The “door’s” inertia is not only the leg: the motor’s rotor, spinning nine times faster than the joint through the gearbox, adds an inertia that the joint feels as if it were 81 times larger. That hidden inertia is called armature. (The 81 is the gear ratio squared, 9 × 9; the maths is below.)
Playground: a joint’s step response
Section titled “Playground: a joint’s step response”One joint, one link, gravity, a motor with a torque limit and a torque-speed line, and a PD loop that runs at a chosen rate, possibly with a delay. At t = 0.05 s the target jumps. The simulation integrates the joint with 4th-order Runge-Kutta at 0.1 ms steps and holds the torque constant between controller updates, as a digital motor driver does.
Start here: press Marvin Bot knee, then Too stiff and Too soft, and compare the curves. How to read the plot: the blue line is where the joint actually is, the orange line is where it was told to go; the closer and quicker blue settles on orange without swinging past it, the better. The green line (lower plot) is the motor torque, which must stay inside the red limit. Good readouts: overshoot under about 20%, settling time a few tenths of a second, small steady-state error. Several sliders are on a log scale so they can span tiny wrists and heavy knees.
From first principles
Section titled “From first principles”A joint is a rotating mass
Section titled “A joint is a rotating mass”Notation: q is the joint angle; a dot on top means “rate of change”, so q̇ (“q-dot”) is the joint’s speed and q̈ (“q-double-dot”) its acceleration. An equation that links a quantity to its own rates of change, like the one below, is called a differential equation; the simulator solves it by taking small time steps (page 14).
Newton’s second law for rotation says J q̈ = τtotal: the angular acceleration q̈ (rad/s²) equals the net torque (N·m) divided by the inertia J (kg·m²). For one joint carrying one link,
J q̈ = τmotor − m g r sin q − τfriction
where m g r is the gravity moment (mass × 9.81 m/s² × distance from the joint to the link’s centre of mass) and q is measured from hanging straight down. This page holds every other joint still; in a real robot the inertia each joint feels changes with the pose of all the others.[1]
PD control
Section titled “PD control”A PD controller sets the motor torque from the position error and the velocity:
τ = kp (q* − q) − kd q̇
q* is the target angle, kp (N·m/rad) the stiffness, kd (N·m·s/rad) the damping. RobStride’s MIT-style operation mode computes exactly this inside the motor driver, with an extra feed-forward torque term, from a target, a target velocity, kp and kd sent over CAN (robot/drivers/robstride.py; for the RS04 kp may be 0-5000 and kd 0-100).[4]
Without gravity and friction, substituting gives J q̈ + kd q̇ + kp q = kp q*, a mass-spring-damper. Its behaviour is set by two numbers:[2]
ωn = √(kp / J) ζ = kd / (2 √(kp J))
The natural frequency ωn (rad/s; divide by 2π for Hz) says how fast the joint responds; the damping ratio ζ says how it settles. Below ζ = 1 a step overshoots by e−πζ/√(1−ζ²) of the step size (ζ = 0.5: 16%; ζ = 0.7: 4.6%); at ζ = 1 it arrives as fast as possible without overshoot; above 1 it creeps in. With gravity the joint stops short of the target by the steady-state error m g r sin q / kp: a spring only pushes back when stretched. (A policy compensates by asking for a slightly higher target; an integral term would remove it but is rarely used under a learned policy.)
Armature: the inertia you cannot see
Section titled “Armature: the inertia you cannot see”The motor’s rotor turns N times for each turn of the joint (N = 9 for the RS04). Its kinetic energy is ½ Irotor (N q̇)² = ½ (Irotor N²) q̇², so seen from the joint the rotor behaves like an extra inertia
Iarmature = Irotor × N²
that adds to the link’s: J = Ilink + Iarmature. It does not depend on the pose and it is not part of any link’s mass, so a URDF cannot hold it; MuJoCo and Isaac Lab add it to the joint directly (armature).[3][6] BeyondMimic reports that setting armature to zero in simulation gives excessive joint accelerations, overswinging and self-collisions.[5] It also matters numerically: a light link on a stiff PD loop has a very high ωn, which the simulator’s timestep may not resolve; armature lowers ωn and keeps the joint stable.
Torque limits and the torque-speed line
Section titled “Torque limits and the torque-speed line”A real motor cannot deliver any torque at any speed. Isaac Lab’s DC motor model (DCMotor) clips the PD torque to a line that falls from the peak torque at standstill to zero at the no-load speed: τmax(q̇) = τpeak (1 − q̇/ω0), and the mirror image for braking.[6] When the PD torque is clipped the joint stops behaving like a linear spring-damper: steps take longer and overshoot changes, which is why a large stiff step looks different from a small one.
Why a slow policy and a fast PD loop
Section titled “Why a slow policy and a fast PD loop”The policy is a neural network that sees the whole robot; running it costs milliseconds, and the walking dynamics it controls (steps, sway) happen at a few hertz, so 50 Hz is plenty. The joint’s own spring-mass dynamics are faster (the knee here: about 4 Hz with today’s gains, the wrist 8 Hz, and much faster for a stiffer loop), and a digital controller that updates its torque every T seconds adds a lag of about T/2. As ωnT approaches 1 the loop loses damping and at around 2 it goes unstable. So the policy outputs targets at 50 Hz, and the PD loop runs at the simulator’s physics step (5 ms in Isaac Lab, 2 ms in MuJoCo) or, on the robot, inside each motor driver, fed new targets at 500 Hz on the legs and 250 Hz on the arms.[7]
A delay of τd seconds shifts the phase of a signal at frequency ω by ω τd radians. Where the delay sits matters. A delay on the target (the policy’s command arriving late over CAN) leaves the PD loop untouched and simply makes every response late: harmless for the joint, but it matters for the policy’s balance loop, which is why training randomises it. A delay inside the loop (the torque computed from an old measurement, as if the PD ran on the main computer over CAN) eats phase margin (the loop’s safety buffer against the correction arriving so late that it pushes the wrong way): for the knee at today’s gains the loop crosses over near 30 rad/s, so 10 ms costs about 17° of a roughly 45° margin, and 25 ms nearly all of it.
How Marvin Bot does it
Section titled “How Marvin Bot does it”Trade-offs and alternatives
Section titled “Trade-offs and alternatives”| Choice | For | Against |
|---|---|---|
| Stiff gains (high kp) | Tracks targets closely, small gravity sag; a policy’s actions map almost directly to angles | Amplifies sensor noise, hits torque limits, needs a fast loop, little passive compliance on impact; sim-to-real gaps in inertia show up as ringing |
| Soft gains | Compliant on contact, robust to model error, gentle on gears | Large tracking error that the policy must learn to compensate; the action scale has to grow |
| BeyondMimic rule: kp = Iarmω², kd = 2Iarmζω, ω = 10 Hz, ζ = 2[5] | One principled rule for every joint, tied to the motor; ζ = 2 because armature alone underestimates the inertia | Needs real rotor inertias (Marvin Bot’s are placeholders); light joints get very soft |
| Torque control (policy outputs torque) | No hidden PD dynamics | The policy must run fast and stabilise every joint itself; much harder to train and transfer |
| Learned actuator network[8] | Captures the real motor’s dynamics, delay and friction from bench data | Needs a bench and data per motor type; a black box |
The BeyondMimic rule applied to the knee: Iarm = 0.03 kg·m², ω = 2π × 10 = 62.8 rad/s gives kp = 118 N·m/rad and kd = 7.5 N·m·s/rad, close to today’s 150 / 5. Including the shin, the knee’s true ζ is then 2 × √(0.03/0.214) = 0.75 and its true ωn 3.7 Hz, which is why the rule picks ζ = 2 rather than 1. Try the BeyondMimic rule preset.
Decisions you can make here
Section titled “Decisions you can make here”- Gain policy: keep the placeholder table, or adopt one rule (BeyondMimic’s) for all joints once rotor inertias are measured. The trade: the table works today and matches other open humanoids; the rule is consistent and explainable, but is only as good as the rotor inertias you feed it, and it makes light joints such as the wrists very soft.
- How much delay to randomise in training (today 0-10 ms on commands, 0-20 ms on observations), based on the bench’s measured CAN round trip. More randomised delay makes the policy robust to a slow bus, but it learns to move more cautiously; too little and a real delay may surprise it.
- What the bench must measure: rotor inertia (armature), friction, and the torque-speed curve at 48 V, per motor model. Each measurement costs bench time in phase 2 but replaces a placeholder that every trained policy currently depends on.
Built from your settings above. Paste it into an issue, a design discussion or an AI assistant session.
Further reading
Section titled “Further reading”- R. Tedrake, Underactuated Robotics (MIT 6.832): the pendulum chapters build intuition for gravity, damping and stability.
- Lynch and Park, Modern Robotics, chapter 11 (robot control) and section 8.9 (motors, gearing and reflected inertia).
- Liao et al., BeyondMimic: the supplementary material’s gain and armature heuristic.
- Isaac Lab: Actuators: ideal PD, DC motor, delayed and learned actuator models.
Sources
Section titled “Sources”- K. M. Lynch and F. C. Park, Modern Robotics, Cambridge University Press 2017 — used for: joint dynamics, configuration-dependent inertia, reflected rotor inertia through a gearbox.
- MIT OpenCourseWare 2.004 Dynamics and Control II — used for: second-order system, natural frequency, damping ratio, overshoot formula.
- MuJoCo documentation, XML Reference (joint armature) — used for: armature as rotor inertia added to a joint.
- RobStride, Product_Information (manuals) — used for: operation-control (MIT) mode, peak torque, no-load speed, gear ratio.
- Q. Liao, T. E. Truong, X. Huang, Y. Gao, G. Tevet, K. Sreenath, C. K. Liu, BeyondMimic: From Motion Tracking to Versatile Humanoid Control via Guided Diffusion, arXiv 2508.08241, 2025 — used for: armature = rotor inertia × gear ratio², gain rule kp = Iω², kd = 2Iζω with ω = 10 Hz, ζ = 2, action scale 0.25 τmax/kp, effects of zero armature.
- Isaac Lab documentation, Actuators — used for: DC motor model with torque-speed clipping, delayed actuators, armature.
- N. Rudin, D. Hoeller, P. Reist, M. Hutter, Learning to Walk in Minutes Using Massively Parallel Deep Reinforcement Learning, CoRL 2021 — used for: policies outputting joint position targets to PD controllers at a lower rate than the physics.
- J. Hwangbo et al., Learning agile and dynamic motor skills for legged robots, Science Robotics 2019 — used for: learned actuator networks as an alternative to analytic motor models.