No error on parasitic motion after small step checking for stage movement, use background detect
This commit is contained in:
parent
72c35c302c
commit
7f657ae853
2 changed files with 34 additions and 24 deletions
|
|
@ -329,7 +329,13 @@ class RangeofMotionThing(lt.Thing):
|
||||||
rom_deps.logger.info(f"Offset measured as {offset[axis]}")
|
rom_deps.logger.info(f"Offset measured as {offset[axis]}")
|
||||||
|
|
||||||
self._rom_data.record_movement(rom_deps.stage.position, offset)
|
self._rom_data.record_movement(rom_deps.stage.position, offset)
|
||||||
_detect_parasitic_motion(movement, offset)
|
if _parasitic_motion_detected(movement, offset):
|
||||||
|
raise ParasiticMotionError(
|
||||||
|
"Parasitic motion detected during initial images to calculate "
|
||||||
|
"z-curvature. This may indicate you have started at the end of the "
|
||||||
|
"range of travel, or that camera stage mapping is poorly "
|
||||||
|
"calibrated."
|
||||||
|
)
|
||||||
|
|
||||||
def _big_z_corrected_movement(
|
def _big_z_corrected_movement(
|
||||||
self, axis: Literal["x", "y"], direction: Literal[1, -1], rom_deps: RomDeps
|
self, axis: Literal["x", "y"], direction: Literal[1, -1], rom_deps: RomDeps
|
||||||
|
|
@ -386,17 +392,17 @@ class RangeofMotionThing(lt.Thing):
|
||||||
rom_deps=rom_deps,
|
rom_deps=rom_deps,
|
||||||
# Don't autofocus initially
|
# Don't autofocus initially
|
||||||
perform_autofocus=False,
|
perform_autofocus=False,
|
||||||
# But retry focus 3 times if detected motion is too small
|
# But retry focus 3 times if detected motion is too small or parasitic
|
||||||
max_autofocus_repeats=3,
|
max_autofocus_repeats=3,
|
||||||
abs_min_offset=abs_min_offset,
|
abs_min_offset=abs_min_offset,
|
||||||
)
|
)
|
||||||
|
parasitic_motion = _parasitic_motion_detected(movement, offset)
|
||||||
rom_deps.logger.info(f"Offset measured as {offset[axis]}")
|
rom_deps.logger.info(f"Offset measured as {offset[axis]}")
|
||||||
|
|
||||||
self._rom_data.record_movement(rom_deps.stage.position, offset)
|
self._rom_data.record_movement(rom_deps.stage.position, offset)
|
||||||
_detect_parasitic_motion(movement, offset)
|
|
||||||
|
|
||||||
if abs(offset[axis]) < abs_min_offset:
|
if abs(offset[axis]) < abs_min_offset or parasitic_motion:
|
||||||
# If the offset is below the abs min offset, the edge has been found.
|
# If the offset is below the abs min offset, or significant
|
||||||
|
# parasitic motion is detected then the edge has been found.
|
||||||
rom_deps.logger.info("Edge has been found.")
|
rom_deps.logger.info("Edge has been found.")
|
||||||
return False
|
return False
|
||||||
# If we reached here the stage is still moving fine
|
# If we reached here the stage is still moving fine
|
||||||
|
|
@ -477,7 +483,8 @@ class RangeofMotionThing(lt.Thing):
|
||||||
if max_autofocus_repeats > 0:
|
if max_autofocus_repeats > 0:
|
||||||
axis = _axis_from_movement_dict(movement)
|
axis = _axis_from_movement_dict(movement)
|
||||||
af_repeats = 0
|
af_repeats = 0
|
||||||
while abs(offset[axis]) < abs_min_offset:
|
parasitic_motion = _parasitic_motion_detected(movement, offset)
|
||||||
|
while abs(offset[axis]) < abs_min_offset or parasitic_motion:
|
||||||
af_repeats += 1
|
af_repeats += 1
|
||||||
rom_deps.logger.info(
|
rom_deps.logger.info(
|
||||||
f"Motion not detected. Refocusing to check. Attempt {af_repeats}/3."
|
f"Motion not detected. Refocusing to check. Attempt {af_repeats}/3."
|
||||||
|
|
@ -485,6 +492,7 @@ class RangeofMotionThing(lt.Thing):
|
||||||
rom_deps.autofocus.looping_autofocus(dz=800)
|
rom_deps.autofocus.looping_autofocus(dz=800)
|
||||||
# Re-take image and calculate offset
|
# Re-take image and calculate offset
|
||||||
offset = self._offset_from(before_img, rom_deps=rom_deps)
|
offset = self._offset_from(before_img, rom_deps=rom_deps)
|
||||||
|
parasitic_motion = _parasitic_motion_detected(movement, offset)
|
||||||
if af_repeats >= max_autofocus_repeats:
|
if af_repeats >= max_autofocus_repeats:
|
||||||
# break if the maximum number of tries are exceeded.
|
# break if the maximum number of tries are exceeded.
|
||||||
break
|
break
|
||||||
|
|
@ -500,6 +508,11 @@ class RangeofMotionThing(lt.Thing):
|
||||||
:param rom_deps: All dependencies that were passed to the calling Action
|
:param rom_deps: All dependencies that were passed to the calling Action
|
||||||
:return: The calculated offset as a dictionary in pixels
|
:return: The calculated offset as a dictionary in pixels
|
||||||
"""
|
"""
|
||||||
|
is_sample, _bg_message = rom_deps.cam.image_is_sample()
|
||||||
|
if not is_sample:
|
||||||
|
raise RuntimeError(
|
||||||
|
"No sample detected. Sample must cover the whole range of motion."
|
||||||
|
)
|
||||||
after_img = rom_deps.cam.grab_as_array()
|
after_img = rom_deps.cam.grab_as_array()
|
||||||
offset = fft_image_tracking.displacement_between_images(
|
offset = fft_image_tracking.displacement_between_images(
|
||||||
image_0=before_img,
|
image_0=before_img,
|
||||||
|
|
@ -542,7 +555,7 @@ def _axis_from_movement_dict(movement: dict[str, float], return_other: bool = Fa
|
||||||
return "y" if movement["x"] == 0 else "x"
|
return "y" if movement["x"] == 0 else "x"
|
||||||
|
|
||||||
|
|
||||||
def _detect_parasitic_motion(
|
def _parasitic_motion_detected(
|
||||||
movement: dict[str, float], offset: dict[str, float]
|
movement: dict[str, float], offset: dict[str, float]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Compare a desired movement to measured offset and error if parasitic motion is too high.
|
"""Compare a desired movement to measured offset and error if parasitic motion is too high.
|
||||||
|
|
@ -553,8 +566,4 @@ def _detect_parasitic_motion(
|
||||||
movement_axis, other_axis = _axis_from_movement_dict(movement, return_other=True)
|
movement_axis, other_axis = _axis_from_movement_dict(movement, return_other=True)
|
||||||
parasitic_motion = abs(offset[other_axis])
|
parasitic_motion = abs(offset[other_axis])
|
||||||
max_parasitic_motion = abs(movement[movement_axis] * PARASITIC_MOTION_TOL)
|
max_parasitic_motion = abs(movement[movement_axis] * PARASITIC_MOTION_TOL)
|
||||||
if parasitic_motion > max_parasitic_motion:
|
return parasitic_motion > max_parasitic_motion
|
||||||
raise ParasiticMotionError(
|
|
||||||
f"Parasitic motion detected. Detected motion of {parasitic_motion} pixels "
|
|
||||||
f"this exceeds a maximum for this move of {max_parasitic_motion} pixels."
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ def test_error_on_axis_from_movement_dict(movement):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("par_fraction", "should_error"),
|
("par_fraction", "too_high"),
|
||||||
[
|
[
|
||||||
(-0.20, True),
|
(-0.20, True),
|
||||||
(-0.11, True),
|
(-0.11, True),
|
||||||
|
|
@ -119,19 +119,17 @@ def test_error_on_axis_from_movement_dict(movement):
|
||||||
(0.20, True),
|
(0.20, True),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_parasitic_detect(par_fraction, should_error):
|
def test_parasitic_detect(par_fraction, too_high):
|
||||||
"""Check error is raised if the fraction of parastitic motion is too high."""
|
"""Check parasitic motion is detected if the fraction of parasitic motion is too high."""
|
||||||
movement = {"x": 2908, "y": 0}
|
movement = {"x": 2908, "y": 0}
|
||||||
|
|
||||||
offset = copy(movement)
|
offset = copy(movement)
|
||||||
offset["y"] = movement["x"] * par_fraction
|
offset["y"] = movement["x"] * par_fraction
|
||||||
if should_error:
|
|
||||||
with pytest.raises(stage_measure.ParasiticMotionError):
|
detected = stage_measure._parasitic_motion_detected(
|
||||||
stage_measure._detect_parasitic_motion(movement=movement, offset=offset)
|
movement=movement, offset=offset
|
||||||
else:
|
)
|
||||||
# Nothing to check here as the only job of _detect_parasitic_motion is to
|
assert detected == too_high
|
||||||
# error if there is too much motion
|
|
||||||
stage_measure._detect_parasitic_motion(movement=movement, offset=offset)
|
|
||||||
|
|
||||||
|
|
||||||
def test_error_if_no_stream_res_set_when_requesting_img_coords():
|
def test_error_if_no_stream_res_set_when_requesting_img_coords():
|
||||||
|
|
@ -163,6 +161,9 @@ def mock_rom_deps(csm_matrix, mocker) -> stage_measure.RomDeps:
|
||||||
vec = np.dot(np.array([y, x]), np.array(csm_matrix))
|
vec = np.dot(np.array([y, x]), np.array(csm_matrix))
|
||||||
return {"x": int(vec[0]), "y": int(vec[1])}
|
return {"x": int(vec[0]), "y": int(vec[1])}
|
||||||
|
|
||||||
|
mock_cam = mocker.Mock()
|
||||||
|
mock_cam.image_is_sample.return_value = (True, "Mocked not measured.")
|
||||||
|
|
||||||
mock_csm = mocker.Mock()
|
mock_csm = mocker.Mock()
|
||||||
# Set up mock csm to return a CSM matrix
|
# Set up mock csm to return a CSM matrix
|
||||||
mock_csm.image_to_stage_displacement_matrix = csm_matrix
|
mock_csm.image_to_stage_displacement_matrix = csm_matrix
|
||||||
|
|
@ -171,7 +172,7 @@ def mock_rom_deps(csm_matrix, mocker) -> stage_measure.RomDeps:
|
||||||
return stage_measure.RomDeps(
|
return stage_measure.RomDeps(
|
||||||
autofocus=mocker.Mock(),
|
autofocus=mocker.Mock(),
|
||||||
stage=mocker.Mock(),
|
stage=mocker.Mock(),
|
||||||
cam=mocker.Mock(),
|
cam=mock_cam,
|
||||||
csm=mock_csm,
|
csm=mock_csm,
|
||||||
logger=LOGGER,
|
logger=LOGGER,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue