Adjust CSM Thing to allow calculation of stage coords from img coords and use this in stage measure
Using this stops the z-axis being flipped when the off diagonal elements of the CSM are different signs.
This commit is contained in:
parent
635375a012
commit
72c35c302c
3 changed files with 36 additions and 27 deletions
|
|
@ -268,10 +268,18 @@ class CameraStageMapper(lt.Thing):
|
|||
an image usually helps resolve any ambiguity.
|
||||
"""
|
||||
self.assert_calibrated()
|
||||
stage.move_relative(**self.convert_image_to_stage_coordinates(x=x, y=y))
|
||||
|
||||
@lt.thing_action
|
||||
def convert_image_to_stage_coordinates(
|
||||
self, x: float, y: float
|
||||
) -> Mapping[str, int]:
|
||||
"""Convert image coordinates to stage coordinates."""
|
||||
self.assert_calibrated()
|
||||
relative_move: np.ndarray = np.dot(
|
||||
np.array([y, x]), np.array(self.image_to_stage_displacement_matrix)
|
||||
)
|
||||
stage.move_relative(x=relative_move[0], y=relative_move[1])
|
||||
return {"x": int(relative_move[0]), "y": int(relative_move[1])}
|
||||
|
||||
@lt.thing_property
|
||||
def thing_state(self) -> Mapping[str, Any]:
|
||||
|
|
|
|||
|
|
@ -74,30 +74,23 @@ class RomDataTracker:
|
|||
|
||||
def predict_z_displacement(
|
||||
self,
|
||||
movement: dict[str, int],
|
||||
axis: Literal["x", "y"],
|
||||
stage_movement: dict[str, int],
|
||||
stage_position: dict[str, int],
|
||||
csm_matrix: list[list[int]],
|
||||
) -> int:
|
||||
"""Predict the z-displacement needed for a given movement.
|
||||
|
||||
:param movement: The movement to be performed in image coordinates.
|
||||
:param axis: The axis which is being measured. This must be 'x' or 'y'.
|
||||
:param stage_movement: The movement to be performed in stage coordinates.
|
||||
:param stage_position: The current stage position in stage coordinates.
|
||||
:param csm_matrix: The camera stage mapping matrix.
|
||||
:returns: The predicted relative z displacement needed to stay in focus.
|
||||
"""
|
||||
pixel_step = {
|
||||
"x": 1 / csm_matrix[0][1],
|
||||
"y": 1 / csm_matrix[1][0],
|
||||
}
|
||||
|
||||
axis = _axis_from_movement_dict(movement)
|
||||
|
||||
lateral_positions = [i[axis] for i in self.stage_coords] # x or y positions
|
||||
# x or y positions
|
||||
lateral_positions = [i[axis] for i in self.stage_coords]
|
||||
z_positions = [i["z"] for i in self.stage_coords]
|
||||
fit_params, *_others = curve_fit(quadratic, lateral_positions, z_positions)
|
||||
z_dest = quadratic(
|
||||
stage_position[axis] + (movement[axis] / pixel_step[axis]), *fit_params
|
||||
)
|
||||
z_dest = quadratic(stage_position[axis] + stage_movement[axis], *fit_params)
|
||||
|
||||
return int(z_dest - stage_position["z"])
|
||||
|
||||
|
||||
|
|
@ -248,7 +241,6 @@ class RangeofMotionThing(lt.Thing):
|
|||
self._big_z_corrected_movement(
|
||||
axis=axis, direction=direction, rom_deps=rom_deps
|
||||
)
|
||||
|
||||
# Autofocus and record position
|
||||
rom_deps.autofocus.looping_autofocus(dz=800)
|
||||
self._rom_data.stage_coords.append(rom_deps.stage.position)
|
||||
|
|
@ -334,7 +326,6 @@ class RangeofMotionThing(lt.Thing):
|
|||
)
|
||||
for _loop in range(5):
|
||||
offset = self._move_and_measure(movement=movement, rom_deps=rom_deps)
|
||||
|
||||
rom_deps.logger.info(f"Offset measured as {offset[axis]}")
|
||||
|
||||
self._rom_data.record_movement(rom_deps.stage.position, offset)
|
||||
|
|
@ -351,16 +342,19 @@ class RangeofMotionThing(lt.Thing):
|
|||
:param direction: The direction to move in.
|
||||
:param rom_deps: All dependencies that were passed to the calling Action
|
||||
"""
|
||||
big_movement = self._movement_in_img_coords(
|
||||
movement = self._movement_in_img_coords(
|
||||
fov_perc=BIG_STEP, axis=axis, direction=direction
|
||||
)
|
||||
# Convert to stage coordinates
|
||||
stage_movemenet = rom_deps.csm.convert_image_to_stage_coordinates(**movement)
|
||||
|
||||
z_disp = self._rom_data.predict_z_displacement(
|
||||
movement=big_movement,
|
||||
axis=axis,
|
||||
stage_movement=stage_movemenet,
|
||||
stage_position=rom_deps.stage.position,
|
||||
csm_matrix=rom_deps.csm.image_to_stage_displacement_matrix,
|
||||
)
|
||||
rom_deps.stage.move_relative(z=z_disp)
|
||||
rom_deps.csm.move_in_image_coordinates(**big_movement)
|
||||
rom_deps.csm.move_in_image_coordinates(**movement)
|
||||
|
||||
def _stage_still_moves(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ def increasing_xyz_dict_generator(*_args, **_kwargs):
|
|||
def csm_matrix():
|
||||
"""Return an example CSM matrix."""
|
||||
return [
|
||||
[0.03061156624485296, 1.8031242270940833],
|
||||
[0.03061156624485296, -1.8031242270940833],
|
||||
[1.773236372778601, 0.006660431608601435],
|
||||
]
|
||||
|
||||
|
|
@ -70,12 +70,12 @@ def example_rom_data():
|
|||
return rom_data
|
||||
|
||||
|
||||
def test_predict_z(csm_matrix, example_rom_data):
|
||||
def test_predict_z(example_rom_data):
|
||||
"""Check that the prediction for the next z position is correct."""
|
||||
mock_z_diff = example_rom_data.predict_z_displacement(
|
||||
movement={"x": 2908, "y": 0},
|
||||
axis="x",
|
||||
stage_movement={"x": 5243, "y": 0},
|
||||
stage_position={"x": 3635, "y": 10, "z": 617},
|
||||
csm_matrix=csm_matrix,
|
||||
)
|
||||
expected_z_diff = 1343
|
||||
assert mock_z_diff == expected_z_diff
|
||||
|
|
@ -157,9 +157,16 @@ def rom_thing(example_rom_data) -> stage_measure.RangeofMotionThing:
|
|||
@pytest.fixture
|
||||
def mock_rom_deps(csm_matrix, mocker) -> stage_measure.RomDeps:
|
||||
"""Return a RomDeps object full of mocks, except the logger which is LOGGER."""
|
||||
|
||||
def apply_csm(x: float, y: float) -> dict[str, int]:
|
||||
"""Convert image coordinates to stage coordinates."""
|
||||
vec = np.dot(np.array([y, x]), np.array(csm_matrix))
|
||||
return {"x": int(vec[0]), "y": int(vec[1])}
|
||||
|
||||
mock_csm = mocker.Mock()
|
||||
# Set up mock csm to return a CSM matrix
|
||||
mock_csm.image_to_stage_displacement_matrix = csm_matrix
|
||||
mock_csm.convert_image_to_stage_coordinates.side_effect = apply_csm
|
||||
|
||||
return stage_measure.RomDeps(
|
||||
autofocus=mocker.Mock(),
|
||||
|
|
@ -367,7 +374,7 @@ def test_big_z_corrected_movement(rom_thing, mock_rom_deps):
|
|||
assert "x" not in move_kwargs
|
||||
assert "y" not in move_kwargs
|
||||
assert "z" in move_kwargs
|
||||
move_kwargs["z"] = 1162
|
||||
assert move_kwargs["z"] == 1148
|
||||
|
||||
# And one move in image coordinates
|
||||
assert mock_rom_deps.csm.move_in_image_coordinates.call_count == 1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue