Modify range of motion _move_back_until_motion_detected to move in SMALL_STEP steps

This will carry on until at least 1.5x BIG_STEP in total motion. If
no motion is detected then something is up.
This commit is contained in:
Julian Stirling 2025-10-21 14:14:06 +01:00
parent bffd7f9d0b
commit b8eb1907c3
2 changed files with 23 additions and 25 deletions

View file

@ -5,8 +5,8 @@ enough positions to predict future z positions. Next, one 'big' step is taken fo
by 3 'small' steps to test that the stage is still moving as expected and has not by 3 'small' steps to test that the stage is still moving as expected and has not
reached the edge. Once the edge has been found and to account for the possibility that reached the edge. Once the edge has been found and to account for the possibility that
the edge was reached during a "big" step, the stage is moved in a sequence of steps the edge was reached during a "big" step, the stage is moved in a sequence of steps
(incrementing in size) in the opposite direction until motion is detected. This is in the opposite direction until motion is detected. This is position is taken as the
position is taken as the true final position. true final position.
Throughout the test, parasitic motion (motion in the axis not being measured) Throughout the test, parasitic motion (motion in the axis not being measured)
is tracked and an error is raised if it exceeds a minimum amount. Currently is tracked and an error is raised if it exceeds a minimum amount. Currently
@ -421,30 +421,29 @@ class RangeofMotionThing(lt.Thing):
"""Move the stage against the direction of test unil motion is detected. """Move the stage against the direction of test unil motion is detected.
In the case that the stage has reached the end of the motion, this method moves In the case that the stage has reached the end of the motion, this method moves
the motor in the opposite direction until motion is detected. the motor in the opposite direction until reliable motion is detected.
:param axis: The axis in which the stage is moving. This must be 'x' or 'y'. :param axis: The axis in which the stage is moving. This must be 'x' or 'y'.
:param direction: The direction in which the stage was moving during the test, :param direction: The direction in which the stage was moving during the test,
this method will move in the opposite direction. this method will move in the opposite direction.
:param rom_deps: All dependencies that were passed to the calling Action :param rom_deps: All dependencies that were passed to the calling Action
""" """
movement = self._movement_in_img_coords(
fov_perc=SMALL_STEP, axis=axis, direction=-direction
)
def _reverse_move_dict(displacement: float) -> dict[str, float]: max_moves = int(1.5 * BIG_STEP / SMALL_STEP)
move = {"x": 0.0, "y": 0.0} # minimum number of pixels for motion to be detected as reliable
move[axis] = displacement * direction * -1 motion_minimum = abs(movement[axis] * DETECT_MOTION_TOL)
return move
# Array of increasing pixel sizes (powers of 2 from 1 to 512) for i in range(max_moves):
movements = [_reverse_move_dict(2**i) for i in range(10)]
motion_minimum = 20 # minimum number of pixels for motion to be detected
for movement in movements:
# Increment movement # Increment movement
rom_deps.logger.info(f"Testing with step size {movement[axis]}") rom_deps.logger.info(f"Testing with step size {movement[axis]}")
# Run an autofocus every 3 steps
run_autofocus = i % 3 == 2
offset = self._move_and_measure( offset = self._move_and_measure(
movement=movement, rom_deps=rom_deps, perform_autofocus=False movement=movement, rom_deps=rom_deps, perform_autofocus=run_autofocus
) )
rom_deps.logger.info(f"Offset measured as {abs(offset[axis])}") rom_deps.logger.info(f"Offset measured as {abs(offset[axis])}")

View file

@ -279,19 +279,18 @@ def test_move_back_until_motion_detected(rom_thing, mock_rom_deps, mocker):
The moves for this method should be in opposite direction to the direction The moves for this method should be in opposite direction to the direction
specified as this is moving back after the stage reaches end of its movement. specified as this is moving back after the stage reaches end of its movement.
""" """
def gen_offset(*_args, **_kwargs):
return {"x": 0, "y": 0}
mock_move_n_meas = mocker.patch.object( mock_move_n_meas = mocker.patch.object(
rom_thing, "_move_and_measure", side_effect=gen_offset rom_thing, "_move_and_measure", return_value={"x": 0, "y": 0}
) )
with pytest.raises(RuntimeError, match="Cannot detect motion again"): with pytest.raises(RuntimeError, match="Cannot detect motion again"):
rom_thing._move_back_until_motion_detected("y", -1, rom_deps=mock_rom_deps) rom_thing._move_back_until_motion_detected("y", -1, rom_deps=mock_rom_deps)
assert mock_move_n_meas.call_count == 10
for i, call_args in enumerate(mock_move_n_meas.call_args_list): max_tries = int(1.5 * stage_measure.BIG_STEP / stage_measure.SMALL_STEP)
call_args.kwargs["movement"] = {"x": 0, "y": 2**i} expected_step = 800 * stage_measure.SMALL_STEP / 100
assert mock_move_n_meas.call_count == max_tries
for _i, call_args in enumerate(mock_move_n_meas.call_args_list):
call_args.kwargs["movement"] = {"x": 0, "y": expected_step}
call_args.kwargs["perform_autofocus"] = False call_args.kwargs["perform_autofocus"] = False
## Reset mock and change the side effect ## Reset mock and change the side effect
@ -299,7 +298,7 @@ def test_move_back_until_motion_detected(rom_thing, mock_rom_deps, mocker):
mock_move_n_meas.side_effect = ( mock_move_n_meas.side_effect = (
{"x": 0, "y": 0}, {"x": 0, "y": 0},
{"x": 0, "y": 0}, {"x": 0, "y": 0},
{"x": 21, "y": 0}, {"x": expected_step * 0.8, "y": 0},
) )
# Other axis and direction this time # Other axis and direction this time
@ -307,8 +306,8 @@ def test_move_back_until_motion_detected(rom_thing, mock_rom_deps, mocker):
# Should only be called 3 times # Should only be called 3 times
assert mock_move_n_meas.call_count == 3 assert mock_move_n_meas.call_count == 3
for i, call_args in enumerate(mock_move_n_meas.call_args_list): for _i, call_args in enumerate(mock_move_n_meas.call_args_list):
call_args.kwargs["movement"] = {"x": -(2**i), "y": 0} call_args.kwargs["movement"] = {"x": -expected_step, "y": 0}
call_args.kwargs["perform_autofocus"] = False call_args.kwargs["perform_autofocus"] = False