E1 · Measured latency
3 minApply the step to the knee in both worlds.
Done when: python3 kit/atraso.py --medir → atraso medido (real − sim): 22 ms.
Lesson 4 · 16 min
Between the policy deciding and the motor turning there is a network, the controller loop and the motor PD. In this lesson you measure the "real robot" latency (delay) with a step on the knee, count the falls that 20 and 40 ms cause under a push, and compensate the kp identified in lesson 3.
Start the lesson
Goal: measure the actuator latency on the bench, count the falls it causes under a push, and compensate kp in the command.
Click each block.
Latency and saturation are invisible in a calm walk and show up under a push.
From the simplest to what you use when the simple one is not enough.
What MuJoCo and Isaac Lab do without you asking: the torque comes out instantly, exactly equal to the PD. It is the "sim" from lesson 1.
Adds the measured latency and the declared torque limit. MuJoCo 3.5+ handles the latency by itself (delay and nsample on the actuator); the kit fills the history with the first command so it does not start from a zero target.
With sysid (lesson 3) and randomization around the measured value, this is what many projects do.
A small neural network learns, from real motor data, the relationship between command, position history, and delivered torque, and enters the simulator in place of the ideal PD.
Worth it when the actuator has effects that a single parameter does not capture. It requires a motor test bench; this course only mentions it.
NVIDIA's GR00T-WholeBodyControl stack added, on 08/31/2026, per-motor kp and kd scaling against stumbling in the SONIC policy: sim-to-real is also fine-tuning the PD at deploy time.
E3 in this lesson does the minimal version: it multiplies the commanded kp by the inverse of the identified one.
--medir gives a 0.3 rad step at the knee with the robot hanging. --ms runs the walk with 6 pushes (120, 150, and 180 N for 0.2 s, forward and sideways) per latency.
F=${FTH:-$HOME/fth}; cd $F/sim2real && . $F/.venv/bin/activate
python3 kit/atraso.py --medirF=${FTH:-$HOME/fth}; cd $F/sim2real && . $F/.venv/bin/activate
python3 kit/atraso.py --ms 0 20 40degrau de 0,3 rad no joelho esquerdo, robô pendurado: tempo até o encoder mexer 0,005 rad sim: 8 ms real: 30 ms atraso medido (real − sim): 22 ms
The "real" takes 22 ms longer than the sim to move the knee: the 20 ms of hidden latency plus a bit from the softer kp, which also slows the response.
atraso quedas em 6 empurrões (120/150/180 N, frente e lado, 0,2 s, t = 3 s) 0 ms 2/6 . . . . x x 20 ms 3/6 x x x . . . 40 ms 6/6 x x x x x x (x = caiu, . = ficou de pé; ordem: 120, 150, 180 N para a frente e depois para o lado)
At 40 ms, it falls in all six. At 0 ms, it still falls in two side pushes; at 20 ms, in the three front ones.
Don't conclude that "20 ms is better from the side than 0 ms." These are six trials with one seed: the fall pattern wobbles; what you read is the count rising with latency (2, 3, 6). To claim more, add more pushes and directions.
Lesson 3 identified kp ≈ 0.57 of the commanded value. Sending 1/0.57 ≈ 1.75 times the kp, the "real" motor delivers close to what the policy expects.
F=${FTH:-$HOME/fth}; cd $F/sim2real && . $F/.venv/bin/activate
python3 kit/dois_mundos.py --kp-comando 1.75; echo "saída: $?"política: unitree_rl_lab G1 29 GDL velocity v0 (ONNX, 480 entradas) · comando 0.50 m/s · 10 s sim queda: não · andou 5.1 m em 10 s (vel. 0.51 m/s; pedida 0.50) real simulado queda: não · andou 4.5 m em 10 s (vel. 0.45 m/s; pedida 0.50) saída: 0
The "simulated real" now walks 4.5 m and doesn't fall (exit 0). The sim, with the stiffer kp, keeps walking. The latency, the mass, and the friction are still there: the policy handles it, because the biggest difference was corrected.
Actuator timing and torque.
Apply the step to the knee in both worlds.
Done when: python3 kit/atraso.py --medir → atraso medido (real − sim): 22 ms.
Run the walk with pushes at 0, 20, and 40 ms.
Done when: python3 kit/atraso.py --ms 0 20 40 → 0 ms 2/6, 20 ms 3/6, 40 ms 6/6.
Use the kp you identified in lesson 3 (1 divided by it) and run both worlds again.
Done when: python3 kit/dois_mundos.py --kp-comando 1.75; echo "saída: $?" → real simulado … queda: não and saída: 0.
The explanation appears after your answer.
Question 1 of 3
With no push, the policy walks with the "real" robot's 20 ms of latency (lesson 1). With a push, 40 ms knocks it down in 6 of 6. What does this teach?skill
Question 2 of 3
MuJoCo 3.5+ models latency in the actuator itself. What does the kit do in addition at startup?
Question 3 of 3
When is it worth training an actuator network instead of PD with latency?
Pick one answer. The explanation appears after you try.