From e32974823ef1f478856e2bfe1d91350012d9b56a Mon Sep 17 00:00:00 2001 From: CarolinePascal Date: Sun, 5 Jul 2026 17:15:01 +0200 Subject: [PATCH] fix(dynamixel): AX-aware calibration for Protocol 1 AX-series motors expose no Homing_Offset/Min|Max_Position_Limit/Drive_Mode registers and cannot Sync Read. Read/write calibration now branch on protocol, using CW/CCW angle limits as the position range (homing offset fixed at 0) and sequential reads. _get_half_turn_homings raises a clear error on Protocol 1 since AX has no homing-offset register. --- src/lerobot/motors/dynamixel/dynamixel.py | 29 ++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/lerobot/motors/dynamixel/dynamixel.py b/src/lerobot/motors/dynamixel/dynamixel.py index e94969ac3..5d59d3973 100644 --- a/src/lerobot/motors/dynamixel/dynamixel.py +++ b/src/lerobot/motors/dynamixel/dynamixel.py @@ -210,6 +210,20 @@ class DynamixelMotorsBus(SerialMotorsBus): return self.calibration == self.read_calibration() def read_calibration(self) -> dict[str, MotorCalibration]: + if self.protocol_version == 1: + # AX-series (Protocol 1.0) has no Homing_Offset/Drive_Mode registers, uses CW/CCW angle + # limits as its position range, and does not support Sync Read. + calibration = {} + for motor, m in self.motors.items(): + calibration[motor] = MotorCalibration( + id=m.id, + drive_mode=0, + homing_offset=0, + range_min=int(self.read("CW_Angle_Limit", motor, normalize=False)), + range_max=int(self.read("CCW_Angle_Limit", motor, normalize=False)), + ) + return calibration + offsets = self.sync_read("Homing_Offset", normalize=False) mins = self.sync_read("Min_Position_Limit", normalize=False) maxes = self.sync_read("Max_Position_Limit", normalize=False) @@ -229,9 +243,13 @@ class DynamixelMotorsBus(SerialMotorsBus): def write_calibration(self, calibration_dict: dict[str, MotorCalibration], cache: bool = True) -> None: for motor, calibration in calibration_dict.items(): - self.write("Homing_Offset", motor, calibration.homing_offset) - self.write("Min_Position_Limit", motor, calibration.range_min) - self.write("Max_Position_Limit", motor, calibration.range_max) + if self.protocol_version == 1: + self.write("CW_Angle_Limit", motor, calibration.range_min) + self.write("CCW_Angle_Limit", motor, calibration.range_max) + else: + self.write("Homing_Offset", motor, calibration.homing_offset) + self.write("Min_Position_Limit", motor, calibration.range_min) + self.write("Max_Position_Limit", motor, calibration.range_max) if cache: self.calibration = calibration_dict @@ -273,6 +291,11 @@ class DynamixelMotorsBus(SerialMotorsBus): On Dynamixel Motors: Present_Position = Actual_Position + Homing_Offset """ + if self.protocol_version == 1: + raise NotImplementedError( + "AX-series motors (Protocol 1.0) have no Homing_Offset register; " + "calibrate the position range via CW/CCW angle limits instead." + ) half_turn_homings: dict[NameOrID, Value] = {} for motor, pos in positions.items(): model = self._get_motor_model(motor)