wire_mode is a4 (see the config reference). Streams a sine or a constant-speed triangle target over 0xA4 at --rate Hz with the firmware gains, 0xA4 speed cap and planner acceleration you choose, reads the fine 0.01° position (0x92) every cycle, scores tracking and creep smoothness, and saves the run for the diagnostics dashboard (Tuning → Firmware loop). Also available from the dashboard (axol serve).
tune.pid tunes the MIT impedance frame, whose gains are the host’s. Under 0xA4 the whole controller is the motor’s own position PI → speed PI → current loop, so the knobs here are the firmware gains (position_kp/ki/kd, speed_kp/ki, current_kp/ki), the speed cap, and the position planner’s acceleration: 0 puts the loop in direct PI tracking of the stream, and the protocol maximum 60000 makes the planner finish each 200 Hz step inside the tick — anything in between re-plans every streamed target and the joint will not follow the wave. On the X6-P20 elbow 60000 tracked a 3 deg/s triangle to 0.02° RMS with 4 ms lag against 0.23° / 74 ms for direct tracking, so try both. The tool writes the planner before the mode-switch reset: on the elbow’s 2025-07 firmware a 0 written into a running position loop is silently ignored (the joint holds and executes nothing), while the same 0 applied through the reset works.
The triangle is the stick-slip probe: every pass runs at one creep speed, so the behaviour is not confined to the sine’s turnarounds. Compare runs on velocity ripple (std of measured minus commanded velocity over the commanded speed: the MIT frame’s stick-slip sits near 0.8, smooth is under 0.2), stuck windows, the 1–4 Hz error band, lag and >10 Hz buzz.
Safety, built in:
- Gains are written to RAM (0x31) unless
--persist, and the pre-run values are written back when the run ends;--keepleaves a winner in place. Gains are written after homing and the mode switch, since those reset the motor and reload ROM. - A buzz guard aborts on high-frequency position motion or excess current and restores the previous gains at once. Start every sweep from the stock values in small steps: shoulder_1 at 3× stock
speed_kpvibrated immediately. - The joint holds position stiffly in this mode and pushes back against contact up to motor torque. Keep the workspace clear and the e-stop in reach.
A winning set does not have to be persisted from here. The robot config carries per-joint
firmware.* gains (see the config reference); enable() compares them with the motor’s ROM while the joint is still disabled and writes only what differs. Every joint’s firmware block is empty by default (the arms run impedance, where these loops are inert), so set the gains for an a4 run with tune.motion --gain SIDE.JOINT.firmware.FIELD=VALUE or in your config.