diff --git a/uc2rest/motor.py b/uc2rest/motor.py index a02c88d..d60c6ff 100644 --- a/uc2rest/motor.py +++ b/uc2rest/motor.py @@ -91,12 +91,19 @@ def _callback_motor_status(self, data): nSteppers = len(data["steppers"]) stepSizes = np.array((self.stepSizeA, self.stepSizeX, self.stepSizeY, self.stepSizeZ)) for iMotor in range(nSteppers): - stepperID = data["steppers"][iMotor]["stepperid"] + stepper = data["steppers"][iMotor] + # Intermediate status frames during a move carry only + # {stepperid, isDone} with no "position" key — skip them instead + # of raising KeyError 'position' (which previously aborted the + # whole callback and left currentPosition stale). + if "position" not in stepper: + continue + stepperID = stepper["stepperid"] # Hardware returns raw steps in firmware frame; convert to physical units # in user frame: phys = hw_steps * stepSize * direction. The direction sign # hides any wiring polarity flip from the caller. self.currentPosition[stepperID] = ( - data["steppers"][iMotor]["position"] + stepper["position"] * stepSizes[stepperID] * self.direction[stepperID] ) @@ -333,6 +340,26 @@ def setup_motor(self, axis, minPos, maxPos, stepSize, backlash, direction=None): self.stepSizeA = stepSize self.backlash[axisIdx] = backlash + def set_backlash(self, axis, backlash): + """Set the per-axis backlash (in hardware steps) used as a reversal overshoot. + + ``axis`` may be a name (\"X\"/\"Y\"/\"Z\"/\"A\") or a hardware index (0-3). + The value is stored in :attr:`backlash` and consumed by + :meth:`move_stepper`, which adds ``direction * backlash`` extra steps + whenever the axis changes direction, taking up the mechanical slack so the + stage still reaches the commanded target. Pass ``0`` to disable + compensation for that axis. Typically fed from a camera-based backlash + measurement (microns) after converting to steps with the axis step size. + """ + idx = self.xyztTo1230(axis) if isinstance(axis, str) else int(axis) + self.backlash[idx] = backlash + return float(self.backlash[idx]) + + def get_backlash(self, axis): + """Return the per-axis backlash (in hardware steps).""" + idx = self.xyztTo1230(axis) if isinstance(axis, str) else int(axis) + return float(self.backlash[idx]) + def xyztTo1230(self, axis): axis = axis.upper() if axis == "X": @@ -705,7 +732,7 @@ def move_stepper(self, steps=(0,0,0,0), speed=(1000,1000,1000,1000), is_absolute motorProp["acceleration"] = int(acceleration[iMotor]) else: motorProp["accel"] = self.DEFAULT_ACCELERATION - motorProp["acceleleration"] = self.DEFAULT_ACCELERATION + motorProp["acceleration"] = self.DEFAULT_ACCELERATION motorPropList.append(motorProp) if len(motorPropList)==0: return "{'return':-1}"