Backlash, and which side the encoder sits on
Last updated
Backlash is the free play in a geared transmission: on reversal the input turns a little before the output starts moving.
Every main microduck_rl task has a Backlash twin with ±1° (2° total) of play on each of the 14 servo joints:
Mjlab-Velocity-Flat-Backlash-MicroDuck
Insert -Backlash before MicroDuck in the task id.
The encoder’s position is the point
This is the part that is easy to get wrong and the most worth learning.
On the real robot, the encoder sits on the output side of the play.
So the simulation has to match that structure. The official implementation does two things:
- Each servo gets an unactuated
passive_<joint>_backlashhinge. - Both the firmware PD emulation (
BacklashEncoderBamActuator) and thejoint_pos/joint_velobservations read through the backlash —qpos[servo] + qpos[backlash].
Modelling it on the wrong side
Put the encoder on the input side and the policy believes it knows the joint position exactly, while on hardware it reads the value after the play.
That difference has an unfriendly property: it is invisible while the joint holds still and arrives all at once on reversal. The robot stands fine and starts misbehaving the moment it walks — and you go looking at the gait policy rather than at the observation.
One engineering benefit
Observation and action dimensions are unchanged, so ONNX export and the runtime need no changes at all. A policy trained with backlash and one trained without are directly interchangeable.
See src/mjlab_microduck/tasks/backlash.py.