E1 · Recorded test
5 minInstall the toolbox and run the bench test.
Done when: python3 kit/coletar.py --saida ~/fth/log.npz ends with gravado: ~/fth/log.npz, showing enc_left_knee … 50.0°.
Lesson 3 · 17 min
Randomizing covers what you don't know; identifying shrinks what you don't know. In this lesson you hang the "simulated real" robot on the gantry, make the leg swing in a frequency sweep, record the encoders, and use MuJoCo's own system identification toolbox to find the kp and kd the motor really delivers.
Start the lesson
Goal: identify the actuator's kp and kd in a bench test with the mujoco.sysid toolbox and check them against the hidden value.
Click each block.
Each test identifies what it excites. A leg in the air says nothing about foot friction.
mujoco.sysid arrived in MuJoCo 3.5.0 (02/12/2026); here, 3.16.0 with the [sysid] extra. Tap each step.
Each parameter has a nominal value, a range, and a function that writes the value into the MjSpec (the editable model). In the kit: kp and kd scale the gains from deploy.yaml on the four tested joints.
Joins the spec, the initial state, the command (TimeSeries.from_control_names), and the measured sensors (TimeSeries.from_names).
Real trap: the model's sensors must be exactly the ones in the log. The Menagerie G1 comes with an IMU; the bench test removes it.
Recompiles the model on each attempt, runs the rollout, and minimizes the difference. On CPU, a few seconds to a minute for two parameters.
On a real robot there is no hidden value to check against. Here there is: --revelar opens real.json and tells you whether you landed within ±10 %.
The honest check on the robot is another test, one the fit never saw.
The [sysid] extra brings scipy, matplotlib, and plotly. coletar.py records the test of the hanging "real robot".
F=${FTH:-$HOME/fth}; cd $F && . .venv/bin/activate
pip install -q "mujoco[sysid]==3.16.0"
python3 -c "from mujoco import sysid; print('mujoco.sysid ok:', sysid.optimize.__name__, sysid.Parameter.__name__)"F=${FTH:-$HOME/fth}; cd $F/sim2real && . $F/.venv/bin/activate
python3 kit/coletar.py --saida $F/log.npzensaio de bancada no mundo 'real': 1999 amostras em 4.0 s · 4 encoders + velocidades enc_left_hip_pitch amplitude medida 18.6° enc_left_hip_roll amplitude medida 29.6° enc_left_knee amplitude medida 50.0° enc_left_ankle_pitch amplitude medida 19.3° gravado: ~/fth/log.npz
Four joints, 4 s, 1,999 samples. The knee swings 50° from end to end.
mujoco.sysid ok: optimize Parameter
Without the extra, mujoco.sysid exists but has no optimize: the module hides whatever depends on scipy.
Starts from the nominal model (kp = kd = 1.0 × the one in deploy.yaml) and adjusts both.
F=${FTH:-$HOME/fth}; cd $F/sim2real && . $F/.venv/bin/activate
python3 kit/sysid.py $F/log.npz --revelar; echo "saída: $?"F=${FTH:-$HOME/fth}; cd $F/sim2real && . $F/.venv/bin/activate
python3 kit/sysid.py $F/log.npz --parametros kp --revelar | tail -4; echo "saída: ${PIPESTATUS[0]}"F=${FTH:-$HOME/fth}; cd $F/sim2real && . $F/.venv/bin/activate
python3 kit/coletar.py --mundo sim --saida $F/log-sim.npz | tail -1
python3 kit/sysid.py $F/log-sim.npz | tail -4log: ~/fth/log.npz · 1999 amostras · sinais: 8 (posição e velocidade de 4 juntas) resíduo, modelo nominal: 0.034 resíduo, modelo identificado: 0.014 erro das posições na bancada, modelo nominal: 8.16° (RMS) erro das posições na bancada, modelo identificado: 3.14° (RMS) kp nominal 1.00 → identificado 0.57 kd nominal 1.00 → identificado 1.08 kp escondido 0.60 · dentro de ±10 %: sim kd escondido 1.00 · dentro de ±10 %: sim saída: 0
kp comes back near 0.6 (the motor delivers 60% of the commanded gain) and kd stays near 1.0, because the "real" robot did not touch kd. The position error drops to less than half.
erro das posições na bancada, modelo nominal: 8.16° (RMS) erro das posições na bancada, modelo identificado: 3.55° (RMS) kp nominal 1.00 → identificado 0.52 kp escondido 0.60 · dentro de ±10 %: não saída: 1
With a single parameter, the optimizer dumps everything the model doesn't explain onto kp, including the 20 ms latency that lesson 4 measures. A parameter missing from the model leaks into the others: give the fit the degrees of freedom the test excites.
gravado: ~/fth/log-sim.npz erro das posições na bancada, modelo nominal: 0.11° (RMS) erro das posições na bancada, modelo identificado: 0.11° (RMS) kp nominal 1.00 → identificado 1.00 kd nominal 1.00 → identificado 1.00
In the world with no hidden difference, the fit has nothing to correct: kp and kd stay at 1.00, and the error that remains, 0.11°, is encoder noise (0.002 rad).
Measure the actuator of the "simulated real" robot.
Install the toolbox and run the bench test.
Done when: python3 kit/coletar.py --saida ~/fth/log.npz ends with gravado: ~/fth/log.npz, showing enc_left_knee … 50.0°.
Run the system identification and check it against the hidden value.
Done when: python3 kit/sysid.py ~/fth/log.npz --revelar; echo "saída: $?" → the kp line ends with dentro de ±10 %: sim (hidden 0.60) and saída: 0.
Run the same test in the "sim" world and identify (last block). Then explain in two lines why only kp, in the "real" log, falls outside ±10 %.
Done when: in the sim block, the kp line ends with identificado 1.00; you wrote that the latency (delay) missing from the model ended up in kp.
The explanation appears after your answer.
Question 1 of 3
Why is the test done with the robot hanging from the gantry?skill
Question 2 of 3
Identifying kp alone, it comes out 0.52; with kp and kd together, close to 0.6. What happened?
Question 3 of 3
On the real robot, how do you check an identified parameter?
Pick one answer. The explanation appears after you try.