Ruff formatting stuff
This commit is contained in:
parent
6579a65f84
commit
d6c47bd43e
2 changed files with 142 additions and 108 deletions
|
|
@ -35,11 +35,9 @@ CSMDep = lt.deps.direct_thing_client_dependency(
|
||||||
AutofocusDep = lt.deps.direct_thing_client_dependency(AutofocusThing, "/autofocus/")
|
AutofocusDep = lt.deps.direct_thing_client_dependency(AutofocusThing, "/autofocus/")
|
||||||
|
|
||||||
|
|
||||||
def generate_move_dicts(fov_perc: int,
|
def generate_move_dicts(
|
||||||
stream_resolution: list[int],
|
fov_perc: int, stream_resolution: list[int], direction: int, factor: float = 1
|
||||||
direction: int,
|
) -> dict[str, float]:
|
||||||
factor: float = 1
|
|
||||||
) -> dict[str, float]:
|
|
||||||
"""Create a single dictionary of x and y moves for moving in image coordinates.
|
"""Create a single dictionary of x and y moves for moving in image coordinates.
|
||||||
|
|
||||||
:param fov_perc: The percentage of field of view the stage should move by.
|
:param fov_perc: The percentage of field of view the stage should move by.
|
||||||
|
|
@ -54,12 +52,9 @@ def generate_move_dicts(fov_perc: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def predict_z(positions: list,
|
def predict_z(
|
||||||
axis: str,
|
positions: list, axis: str, relative_move: float, stage: StageDep, csm: CSMDep
|
||||||
relative_move: float,
|
) -> float:
|
||||||
stage: StageDep,
|
|
||||||
csm: CSMDep
|
|
||||||
) -> float:
|
|
||||||
"""Predict the next z position for a move using previous positions.
|
"""Predict the next z position for a move using previous positions.
|
||||||
|
|
||||||
:params positions: The list of positions used for predicting z.
|
:params positions: The list of positions used for predicting z.
|
||||||
|
|
@ -70,26 +65,30 @@ def predict_z(positions: list,
|
||||||
:return: A number of pixels the stage needs to move in z.
|
:return: A number of pixels the stage needs to move in z.
|
||||||
"""
|
"""
|
||||||
pixel_step = {
|
pixel_step = {
|
||||||
'x':1/csm.image_to_stage_displacement_matrix[0][1],
|
"x": 1 / csm.image_to_stage_displacement_matrix[0][1],
|
||||||
'y':1/csm.image_to_stage_displacement_matrix[1][0]
|
"y": 1 / csm.image_to_stage_displacement_matrix[1][0],
|
||||||
}
|
}
|
||||||
|
|
||||||
lateral_positions = [i[axis] for i in positions]
|
lateral_positions = [i[axis] for i in positions]
|
||||||
z_positions = [i['z'] for i in positions]
|
z_positions = [i["z"] for i in positions]
|
||||||
parameters, _ = curve_fit(quadratic, lateral_positions, z_positions)
|
parameters, _ = curve_fit(quadratic, lateral_positions, z_positions)
|
||||||
z_dest = quadratic(stage.position[axis] + (relative_move/pixel_step[axis]), *parameters)
|
z_dest = quadratic(
|
||||||
|
stage.position[axis] + (relative_move / pixel_step[axis]), *parameters
|
||||||
|
)
|
||||||
|
|
||||||
return z_dest - stage.position["z"]
|
return z_dest - stage.position["z"]
|
||||||
|
|
||||||
|
|
||||||
def move_and_measure(
|
def move_and_measure(
|
||||||
step_size: dict[str, float],
|
step_size: dict[str, float],
|
||||||
axis: str,
|
axis: str,
|
||||||
data,
|
data,
|
||||||
image1,
|
image1,
|
||||||
autofocus_proc: bool,
|
autofocus_proc: bool,
|
||||||
csm: CSMDep,
|
csm: CSMDep,
|
||||||
autofocus: AutofocusDep,
|
autofocus: AutofocusDep,
|
||||||
cam:CamDep) -> tuple:
|
cam: CamDep,
|
||||||
|
) -> tuple:
|
||||||
"""Move the stage and measure the offset between the two positions.
|
"""Move the stage and measure the offset between the two positions.
|
||||||
|
|
||||||
:params step_size: A dictionary with keys 'x' and 'y' with pixel distances.
|
:params step_size: A dictionary with keys 'x' and 'y' with pixel distances.
|
||||||
|
|
@ -100,26 +99,29 @@ def move_and_measure(
|
||||||
:return: All required data for the next move. This includes the updated delta value and offset.
|
:return: All required data for the next move. This includes the updated delta value and offset.
|
||||||
Also returns what wrong_axis is i.e. if the direction is 'x', wrong_axis = 'y'.
|
Also returns what wrong_axis is i.e. if the direction is 'x', wrong_axis = 'y'.
|
||||||
"""
|
"""
|
||||||
if axis == 'x':
|
if axis == "x":
|
||||||
csm.move_in_image_coordinates(x = step_size['x'], y = 0)
|
csm.move_in_image_coordinates(x=step_size["x"], y=0)
|
||||||
wrong_axis = 'y'
|
wrong_axis = "y"
|
||||||
else:
|
else:
|
||||||
csm.move_in_image_coordinates(x = 0, y = step_size['y'])
|
csm.move_in_image_coordinates(x=0, y=step_size["y"])
|
||||||
wrong_axis = 'x'
|
wrong_axis = "x"
|
||||||
if autofocus_proc:
|
if autofocus_proc:
|
||||||
autofocus.looping_autofocus(dz = 800)
|
autofocus.looping_autofocus(dz=800)
|
||||||
image2 = cv2.resize(np.array(Image.open(cam.grab_jpeg().open())), dsize=(0,0), fx= 1, fy= 1)
|
image2 = cv2.resize(
|
||||||
offset = [x * 1 for x in fft_image_tracking.displacement_between_images(
|
np.array(Image.open(cam.grab_jpeg().open())), dsize=(0, 0), fx=1, fy=1
|
||||||
image_0 = image1,
|
)
|
||||||
image_1 = image2,
|
offset = [
|
||||||
sigma=10,
|
x * 1
|
||||||
fractional_threshold=0.1,
|
for x in fft_image_tracking.displacement_between_images(
|
||||||
pad=True)] # Units is pixels
|
image_0=image1, image_1=image2, sigma=10, fractional_threshold=0.1, pad=True
|
||||||
data.delta['x'] = int(offset[1])
|
)
|
||||||
data.delta['y'] = int(offset[0])
|
] # Units is pixels
|
||||||
|
data.delta["x"] = int(offset[1])
|
||||||
|
data.delta["y"] = int(offset[0])
|
||||||
|
|
||||||
return offset, wrong_axis
|
return offset, wrong_axis
|
||||||
|
|
||||||
|
|
||||||
def acquire_z_predict_points(
|
def acquire_z_predict_points(
|
||||||
stream_resolution: list[int],
|
stream_resolution: list[int],
|
||||||
direction: int,
|
direction: int,
|
||||||
|
|
@ -162,7 +164,10 @@ def acquire_z_predict_points(
|
||||||
|
|
||||||
data.measure(stage.position, offset)
|
data.measure(stage.position, offset)
|
||||||
|
|
||||||
assert(np.abs(data.delta[wrong_axis]) < np.abs(wrong_axis_max_medium[wrong_axis]))
|
assert np.abs(data.delta[wrong_axis]) < np.abs(
|
||||||
|
wrong_axis_max_medium[wrong_axis]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def check_stage_operation(
|
def check_stage_operation(
|
||||||
small_step: int,
|
small_step: int,
|
||||||
|
|
@ -191,10 +196,8 @@ def check_stage_operation(
|
||||||
"""
|
"""
|
||||||
failure_count = 0
|
failure_count = 0
|
||||||
wrong_axis_max_small = generate_move_dicts(
|
wrong_axis_max_small = generate_move_dicts(
|
||||||
small_step,
|
small_step, stream_resolution, direction, factor=0.1
|
||||||
stream_resolution,
|
)
|
||||||
direction,
|
|
||||||
factor=0.1)
|
|
||||||
|
|
||||||
for _loop in range(3):
|
for _loop in range(3):
|
||||||
image1 = cv2.resize(
|
image1 = cv2.resize(
|
||||||
|
|
@ -250,6 +253,7 @@ def check_stage_operation(
|
||||||
logger.info("Edge has been found.")
|
logger.info("Edge has been found.")
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def motion_detection(
|
def motion_detection(
|
||||||
axis: str,
|
axis: str,
|
||||||
direction: int,
|
direction: int,
|
||||||
|
|
@ -265,39 +269,46 @@ def motion_detection(
|
||||||
previous to motion detection being used.
|
previous to motion detection being used.
|
||||||
:return: The stage coordinates where motion was detected.
|
:return: The stage coordinates where motion was detected.
|
||||||
"""
|
"""
|
||||||
displacements = [1,2,4,8,16,32,64,128,256,512] # Array of increasing step sizes
|
displacements = [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
8,
|
||||||
|
16,
|
||||||
|
32,
|
||||||
|
64,
|
||||||
|
128,
|
||||||
|
256,
|
||||||
|
512,
|
||||||
|
] # Array of increasing step sizes
|
||||||
motion_minimum = 20 # minimum number of pixels for motion to be detected
|
motion_minimum = 20 # minimum number of pixels for motion to be detected
|
||||||
|
|
||||||
this_motion_step = {
|
this_motion_step = {"x": 0, "y": 0}
|
||||||
'x': 0,
|
|
||||||
'y': 0
|
|
||||||
}
|
|
||||||
|
|
||||||
delta = {
|
delta = {"x": 0, "y": 0}
|
||||||
'x': 0,
|
|
||||||
'y': 0
|
|
||||||
}
|
|
||||||
|
|
||||||
for loop in range(np.shape(displacements)[0]):
|
for loop in range(np.shape(displacements)[0]):
|
||||||
this_motion_step[axis] = displacements[loop] * direction * -1
|
this_motion_step[axis] = displacements[loop] * direction * -1
|
||||||
logger.info(f"Testing with step size {this_motion_step[axis]}")
|
logger.info(f"Testing with step size {this_motion_step[axis]}")
|
||||||
image1 = cv2.resize(np.array(Image.open(cam.grab_jpeg().open())),
|
image1 = cv2.resize(
|
||||||
dsize=(0,0),
|
np.array(Image.open(cam.grab_jpeg().open())), dsize=(0, 0), fx=1, fy=1
|
||||||
fx= 1,
|
)
|
||||||
fy= 1)
|
csm.move_in_image_coordinates(x=this_motion_step["x"], y=this_motion_step["y"])
|
||||||
csm.move_in_image_coordinates(x = this_motion_step['x'], y = this_motion_step['y'])
|
image2 = cv2.resize(
|
||||||
image2 = cv2.resize(np.array(Image.open(cam.grab_jpeg().open())),
|
np.array(Image.open(cam.grab_jpeg().open())), dsize=(0, 0), fx=1, fy=1
|
||||||
dsize=(0,0),
|
)
|
||||||
fx= 1,
|
offset = [
|
||||||
fy= 1)
|
x * 1
|
||||||
offset = [x * 1 for x in fft_image_tracking.displacement_between_images(
|
for x in fft_image_tracking.displacement_between_images(
|
||||||
image_0 = image1,
|
image_0=image1,
|
||||||
image_1 = image2,
|
image_1=image2,
|
||||||
sigma=10,
|
sigma=10,
|
||||||
fractional_threshold=0.1,
|
fractional_threshold=0.1,
|
||||||
pad=True)]
|
pad=True,
|
||||||
delta['x'] = int(offset[1])
|
)
|
||||||
delta['y'] = int(offset[0])
|
]
|
||||||
|
delta["x"] = int(offset[1])
|
||||||
|
delta["y"] = int(offset[0])
|
||||||
logger.info(f"Offset measured as {np.abs(delta[axis])}")
|
logger.info(f"Offset measured as {np.abs(delta[axis])}")
|
||||||
if np.abs(delta[axis]) > motion_minimum:
|
if np.abs(delta[axis]) > motion_minimum:
|
||||||
logger.info("Motion detected.")
|
logger.info("Motion detected.")
|
||||||
|
|
@ -305,10 +316,16 @@ def motion_detection(
|
||||||
|
|
||||||
return stage.position
|
return stage.position
|
||||||
|
|
||||||
class RomDataTracker():
|
|
||||||
|
class RomDataTracker:
|
||||||
"""Class for tracking range of motion data."""
|
"""Class for tracking range of motion data."""
|
||||||
|
|
||||||
def __init__(self, stage_coords: list[dict[str, int]] = [], cor_lat_steps: list[list[float]] = [], delta: dict = {'x':0, 'y':0}):
|
def __init__(
|
||||||
|
self,
|
||||||
|
stage_coords: list[dict[str, int]] = [],
|
||||||
|
cor_lat_steps: list[list[float]] = [],
|
||||||
|
delta: dict = {"x": 0, "y": 0},
|
||||||
|
):
|
||||||
"""Define useful data tracked throughout test."""
|
"""Define useful data tracked throughout test."""
|
||||||
self.stage_coords = stage_coords
|
self.stage_coords = stage_coords
|
||||||
self.cor_lat_steps = cor_lat_steps
|
self.cor_lat_steps = cor_lat_steps
|
||||||
|
|
@ -319,6 +336,7 @@ class RomDataTracker():
|
||||||
self.stage_coords.append(current_pos)
|
self.stage_coords.append(current_pos)
|
||||||
self.cor_lat_steps.append(cor)
|
self.cor_lat_steps.append(cor)
|
||||||
|
|
||||||
|
|
||||||
class RangeofMotionThing(lt.Thing):
|
class RangeofMotionThing(lt.Thing):
|
||||||
"""A class used to measure the range of motion of the stage in X and Y."""
|
"""A class used to measure the range of motion of the stage in X and Y."""
|
||||||
|
|
||||||
|
|
@ -339,7 +357,7 @@ class RangeofMotionThing(lt.Thing):
|
||||||
:return: Results dictionary containing stage positions,
|
:return: Results dictionary containing stage positions,
|
||||||
correlations and the final position.
|
correlations and the final position.
|
||||||
"""
|
"""
|
||||||
autofocus.looping_autofocus(dz = 1000)
|
autofocus.looping_autofocus(dz=1000)
|
||||||
|
|
||||||
starting_position = list(stage.position.values())
|
starting_position = list(stage.position.values())
|
||||||
|
|
||||||
|
|
@ -352,7 +370,7 @@ class RangeofMotionThing(lt.Thing):
|
||||||
logger.info(f"Beginning the {axis}-axis in the {dir_word} direction")
|
logger.info(f"Beginning the {axis}-axis in the {dir_word} direction")
|
||||||
|
|
||||||
# Generate required dictionaries for step sizes and minimum offsets
|
# Generate required dictionaries for step sizes and minimum offsets
|
||||||
stream_resolution = [820,616]
|
stream_resolution = [820, 616]
|
||||||
big_step = 200
|
big_step = 200
|
||||||
small_step = 20
|
small_step = 20
|
||||||
step_sizes_big = generate_move_dicts(big_step, stream_resolution, direction)
|
step_sizes_big = generate_move_dicts(big_step, stream_resolution, direction)
|
||||||
|
|
@ -380,23 +398,24 @@ class RangeofMotionThing(lt.Thing):
|
||||||
|
|
||||||
while np.abs(rom_data.delta[axis]) > np.abs(minimum_offset_small[axis]):
|
while np.abs(rom_data.delta[axis]) > np.abs(minimum_offset_small[axis]):
|
||||||
z_diff = predict_z(
|
z_diff = predict_z(
|
||||||
positions = rom_data.stage_coords,
|
positions=rom_data.stage_coords,
|
||||||
axis = axis,
|
axis=axis,
|
||||||
relative_move = step_sizes_big[axis],
|
relative_move=step_sizes_big[axis],
|
||||||
stage = stage,
|
stage=stage,
|
||||||
csm = csm)
|
csm=csm,
|
||||||
|
)
|
||||||
|
|
||||||
logger.info("Z calibration complete.")
|
logger.info("Z calibration complete.")
|
||||||
stage.move_relative(z = z_diff)
|
stage.move_relative(z=z_diff)
|
||||||
logger.info(f"Moved in z by {z_diff}")
|
logger.info(f"Moved in z by {z_diff}")
|
||||||
|
|
||||||
# Big step
|
# Big step
|
||||||
if axis == 'x':
|
if axis == "x":
|
||||||
csm.move_in_image_coordinates(x = step_sizes_big['x'], y = 0)
|
csm.move_in_image_coordinates(x=step_sizes_big["x"], y=0)
|
||||||
else:
|
else:
|
||||||
csm.move_in_image_coordinates(x = 0, y = step_sizes_big['y'])
|
csm.move_in_image_coordinates(x=0, y=step_sizes_big["y"])
|
||||||
|
|
||||||
autofocus.looping_autofocus(dz = 800)
|
autofocus.looping_autofocus(dz=800)
|
||||||
rom_data.stage_coords.append(stage.position)
|
rom_data.stage_coords.append(stage.position)
|
||||||
|
|
||||||
check_stage_operation(
|
check_stage_operation(
|
||||||
|
|
@ -404,7 +423,7 @@ class RangeofMotionThing(lt.Thing):
|
||||||
stream_resolution=stream_resolution,
|
stream_resolution=stream_resolution,
|
||||||
direction=direction,
|
direction=direction,
|
||||||
axis=axis,
|
axis=axis,
|
||||||
data = rom_data,
|
data=rom_data,
|
||||||
minimum_offset_small=minimum_offset_small,
|
minimum_offset_small=minimum_offset_small,
|
||||||
csm=csm,
|
csm=csm,
|
||||||
cam=cam,
|
cam=cam,
|
||||||
|
|
@ -415,22 +434,32 @@ class RangeofMotionThing(lt.Thing):
|
||||||
|
|
||||||
# Motion detection
|
# Motion detection
|
||||||
logger.info("Running motion detection")
|
logger.info("Running motion detection")
|
||||||
final_pos = motion_detection(axis = axis, direction = direction, csm = csm, stage = stage, cam = cam, logger = logger)
|
final_pos = motion_detection(
|
||||||
rom_data.stage_coords[np.shape(np.array(rom_data.stage_coords))[0] - 1] = final_pos
|
axis=axis,
|
||||||
|
direction=direction,
|
||||||
|
csm=csm,
|
||||||
|
stage=stage,
|
||||||
|
cam=cam,
|
||||||
|
logger=logger,
|
||||||
|
)
|
||||||
|
rom_data.stage_coords[np.shape(np.array(rom_data.stage_coords))[0] - 1] = (
|
||||||
|
final_pos
|
||||||
|
)
|
||||||
|
|
||||||
axis_results = {
|
axis_results = {
|
||||||
"correlation_lateral_steps": rom_data.cor_lat_steps,
|
"correlation_lateral_steps": rom_data.cor_lat_steps,
|
||||||
"stage_positions": rom_data.stage_coords,
|
"stage_positions": rom_data.stage_coords,
|
||||||
"final_position": final_pos
|
"final_position": final_pos,
|
||||||
}
|
}
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
logger.info("Parasitic motion detected.")
|
logger.info("Parasitic motion detected.")
|
||||||
finally:
|
finally:
|
||||||
stage.move_absolute(
|
stage.move_absolute(
|
||||||
x = starting_position[0],
|
x=starting_position[0],
|
||||||
y = starting_position[1],
|
y=starting_position[1],
|
||||||
z = starting_position[2],
|
z=starting_position[2],
|
||||||
block_cancellation=True)
|
block_cancellation=True,
|
||||||
|
)
|
||||||
|
|
||||||
return axis_results
|
return axis_results
|
||||||
|
|
||||||
|
|
@ -447,30 +476,36 @@ class RangeofMotionThing(lt.Thing):
|
||||||
|
|
||||||
:return: Results dictionary separated into keys of each axis and direction.
|
:return: Results dictionary separated into keys of each axis and direction.
|
||||||
"""
|
"""
|
||||||
logger.info("Using the stage to measure the Range of Motion.\
|
logger.info(
|
||||||
Please ensure you are using a big enough sample.")
|
"Using the stage to measure the Range of Motion.\
|
||||||
|
Please ensure you are using a big enough sample."
|
||||||
|
)
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
rom_results = {}
|
rom_results = {}
|
||||||
|
|
||||||
for axis_dir in [['x', 1],['x', -1],['y', 1],['y', -1]]:
|
for axis_dir in [["x", 1], ["x", -1], ["y", 1], ["y", -1]]:
|
||||||
axis_dir_results = self.rom_axis(
|
axis_dir_results = self.rom_axis(
|
||||||
autofocus,
|
autofocus,
|
||||||
stage,
|
stage,
|
||||||
cam,
|
cam,
|
||||||
csm,
|
csm,
|
||||||
logger,
|
logger,
|
||||||
axis = axis_dir[0],
|
axis=axis_dir[0],
|
||||||
direction = axis_dir[1]
|
direction=axis_dir[1],
|
||||||
)
|
)
|
||||||
rom_results[f"{axis_dir}"] = axis_dir_results
|
rom_results[f"{axis_dir}"] = axis_dir_results
|
||||||
|
|
||||||
end_time = time.time()
|
end_time = time.time()
|
||||||
total_time = (end_time - start_time)/60
|
total_time = (end_time - start_time) / 60
|
||||||
|
|
||||||
x_range = abs(rom_results["['x', 1]"]["final_position"]["x"] -
|
x_range = abs(
|
||||||
rom_results["['x', -1]"]["final_position"]["x"])
|
rom_results["['x', 1]"]["final_position"]["x"]
|
||||||
y_range = abs(rom_results["['y', 1]"]["final_position"]["y"] -
|
- rom_results["['x', -1]"]["final_position"]["x"]
|
||||||
rom_results["['y', -1]"]["final_position"]["y"])
|
)
|
||||||
|
y_range = abs(
|
||||||
|
rom_results["['y', 1]"]["final_position"]["y"]
|
||||||
|
- rom_results["['y', -1]"]["final_position"]["y"]
|
||||||
|
)
|
||||||
step_range = [x_range, y_range]
|
step_range = [x_range, y_range]
|
||||||
logger.info(f"Range of motion is {x_range} X {y_range}")
|
logger.info(f"Range of motion is {x_range} X {y_range}")
|
||||||
|
|
||||||
|
|
@ -480,11 +515,9 @@ class RangeofMotionThing(lt.Thing):
|
||||||
|
|
||||||
self.last_calibration = DenumpifyingDict(rom_results).model_dump()
|
self.last_calibration = DenumpifyingDict(rom_results).model_dump()
|
||||||
|
|
||||||
with open("/var/openflexure/ROM_Test_Results.json", 'w') as file_object:
|
with open("/var/openflexure/ROM_Test_Results.json", "w") as file_object:
|
||||||
json.dump(rom_results, file_object, indent = 3)
|
json.dump(rom_results, file_object, indent=3)
|
||||||
|
|
||||||
return rom_results
|
return rom_results
|
||||||
|
|
||||||
last_calibration = lt.ThingSetting(
|
last_calibration = lt.ThingSetting(initial_value=None, model=dict, readonly=True)
|
||||||
initial_value=None, model=dict, readonly=True
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -328,6 +328,7 @@ def _get_version_from_toml(toml_path: str) -> str:
|
||||||
LOGGER.error("Problem opening pyproject.toml")
|
LOGGER.error("Problem opening pyproject.toml")
|
||||||
return "Undefined"
|
return "Undefined"
|
||||||
|
|
||||||
|
|
||||||
def quadratic(x, a, b, c):
|
def quadratic(x, a, b, c):
|
||||||
"""Quadratic function. Used for predicting z.
|
"""Quadratic function. Used for predicting z.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue