Skip to main content
Tests one or more Kp/Kd candidates for a single joint at ~100 Hz — pass several values to either flag to sweep the whole grid in one session — measures tracking error (RMS, max, overshoot, settling), ranks the candidates, and can save the winner to this robot’s calibration file. Also available from the diagnostics dashboard (axol serve). The feedforward path defaults to exactly what the production control loop applies — gravity + friction + inertia (j_eff) + host-side damping (kd_host) — so gains found here transfer 1:1 to teleop. Use --ff to tune against a reduced feedforward instead (e.g. --ff none for bare PD, or --ff friction to see how the joint behaves without gravity compensation), and --host-kd to override the host-side damping (the shoulders need it — their motor firmware’s velocity estimate is too filtered to damp the ~2 Hz closed-loop resonance, so firmware kd alone leaves them ringing). During every probe the other joints are held at rest on impedance at the calibration gains, with gravity fed forward (--a4-holders holds them on the motors’ own position loops instead — stiffer, but with the arm held that way the MyActuator 0xA4 loop has set a shoulder oscillating), and the tool round-robins one position read per cycle across them to prove they actually stayed put: each candidate’s report ends with a Holder peak line listing how far every holder moved from its pre-step baseline. Past ~0.5° the structure was flexing under the reaction torque, and part of the measured ring belongs to a compliant neighbour rather than the joint under test.

Per-robot calibration

--save writes the winning gains to ~/.almond/calibration.json, which AxolConfig loads on construction — every operation on the machine (teleop, data collection, policies) then uses the saved values instead of the shared defaults, while explicit overrides (--axol.left.elbow.kp 60, the panel’s Advanced settings) still win over both. Saved kp/kd are the tuned top (s=1, the production default) of the stiffness blend, like the defaults they replace. Friction values saved by tune.friction --save live in the same file.