From 814c84233ba7833cd60b715440a6fe3d57bd1c1e Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Mon, 29 Dec 2025 12:02:20 +0000 Subject: [PATCH 1/9] Tip-aware cLLD probing `start_pos_search` and safety validation --- .../backends/hamilton/STAR_backend.py | 68 ++++++++++++++++--- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py index 521fa5adaa..e320174607 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py @@ -1723,10 +1723,7 @@ async def probe_liquid_heights( lowest_immers_pos=container.get_absolute_location("c", "c", "cavity_bottom").z + tip.total_tip_length - tip.fitting_depth, - start_pos_search=container.get_absolute_location("c", "c", "t").z - + tip.total_tip_length - - tip.fitting_depth - + 5, + start_pos_search=container.get_absolute_location("c", "c", "t").z + 5, ) for channel, container, tip in zip(use_channels, containers, tips) ] @@ -8977,7 +8974,7 @@ async def move_z_drive_to_liquid_surface_using_clld( self, channel_idx: int, # 0-based indexing of channels! lowest_immers_pos: float = 99.98, # mm - start_pos_search: float = 330.0, # mm + start_pos_search: Optional[float] = None, # mm channel_speed: float = 10.0, # mm channel_acceleration: float = 800.0, # mm/sec**2 detection_edge: int = 10, @@ -8985,8 +8982,61 @@ async def move_z_drive_to_liquid_surface_using_clld( post_detection_trajectory: Literal[0, 1] = 1, post_detection_dist: float = 2.0, # mm ): + """Move the tip on a channel to the liquid surface using capacitive LLD (cLLD). + + Runs a downward capacitive liquid-level detection (cLLD) search on the specified + 0-indexed channel. Requires a tip to be mounted; tip length is queried and used + (with a fixed fitting depth of 8 mm) to convert the provided positions into the + Z-drive coordinates expected by the instrument. If start_pos_search is None, a + safe start height is computed from the tip length. The search will not go below + lowest_immers_pos. After detection, the channel performs the configured + post-detection move (by default retracting 2.0 mm). + + Args: + channel_idx: Channel index (0-based). + lowest_immers_pos: Lowest allowed search position in mm (hard stop). Defaults to 99.98. + start_pos_search: Search start position in mm. If None, computed from tip length. + channel_speed: Search speed in mm/s. Defaults to 10.0. + channel_acceleration: Search acceleration in mm/s^2. Defaults to 800.0. + detection_edge: Edge steepness threshold for cLLD detection (0-1023). Defaults to 10. + detection_drop: Offset applied after cLLD edge detection (0-1023). Defaults to 2. + post_detection_trajectory: Instrument post-detection move mode (0 or 1). Defaults to 1. + post_detection_dist: Distance in mm to move after detection (interpreted per trajectory). + Defaults to 2.0. + + Raises: + ValueError: If channel_idx is out of range. + RuntimeError: If no tip is mounted on channel_idx. + AssertionError: If any parameter is outside the instrument-supported range. + + Returns: + None + """ + + # Preconditions checks + # Ensure tip is mounted + if not (await self.request_tip_presence())[channel_idx]: + raise RuntimeError(f"No tip mounted on channel {channel_idx}") + + # Ensure valid channel index + if (not 0 <= channel_idx <= self.num_channels - 1) and isinstance(channel_idx, int): + raise ValueError(f"channel_idx must be in [0, {self.num_channels - 1}], is {channel_idx}") + + # Correct for tip length + fitting depth + tip_len = await self.request_tip_len_on_channel(channel_idx) + + fitting_depth = 8 # mm, for 10, 50, 300, 1000 ul Hamilton tips + safe_head_top_z_pos = 334.7 + + if start_pos_search is None: + start_pos_search = 334.7 - tip_len + fitting_depth + + channel_head_start_pos = start_pos_search + tip_len - fitting_depth + safe_head_bottom_z_pos = 99.98 + tip_len - fitting_depth + 0.5 # add 0.5 mm safety margin + + # Conversions & machine-compatibility check of parameters lowest_immers_pos_increments = STARBackend.mm_to_z_drive_increment(lowest_immers_pos) - start_pos_search_increments = STARBackend.mm_to_z_drive_increment(start_pos_search) + start_pos_search_increments = STARBackend.mm_to_z_drive_increment(channel_head_start_pos) channel_speed_increments = STARBackend.mm_to_z_drive_increment(channel_speed) channel_acceleration_thousand_increments = STARBackend.mm_to_z_drive_increment( channel_acceleration / 1000 @@ -8997,9 +9047,9 @@ async def move_z_drive_to_liquid_surface_using_clld( f"Lowest immersion position must be between \n{STARBackend.z_drive_increment_to_mm(9_320)}" + f" and {STARBackend.z_drive_increment_to_mm(31_200)} mm, is {lowest_immers_pos} mm" ) - assert 9_320 <= start_pos_search_increments <= 31_200, ( - f"Start position of LLD search must be between \n{STARBackend.z_drive_increment_to_mm(9_320)}" - + f" and {STARBackend.z_drive_increment_to_mm(31_200)} mm, is {start_pos_search} mm" + assert safe_head_bottom_z_pos <= channel_head_start_pos <= safe_head_top_z_pos, ( + f"Start position of LLD search must be between \n{safe_head_bottom_z_pos}" + + f" and {safe_head_top_z_pos} mm, is {channel_head_start_pos} mm" ) assert 20 <= channel_speed_increments <= 15_000, ( f"LLD search speed must be between \n{STARBackend.z_drive_increment_to_mm(20)}" From da5a97af814c5a4e1e4a3e63945cda8da07a0f16 Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Mon, 29 Dec 2025 12:03:38 +0000 Subject: [PATCH 2/9] `make format` --- pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py index e320174607..81da40ba15 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py @@ -9032,7 +9032,7 @@ async def move_z_drive_to_liquid_surface_using_clld( start_pos_search = 334.7 - tip_len + fitting_depth channel_head_start_pos = start_pos_search + tip_len - fitting_depth - safe_head_bottom_z_pos = 99.98 + tip_len - fitting_depth + 0.5 # add 0.5 mm safety margin + safe_head_bottom_z_pos = 99.98 + tip_len - fitting_depth + 0.5 # add 0.5 mm safety margin # Conversions & machine-compatibility check of parameters lowest_immers_pos_increments = STARBackend.mm_to_z_drive_increment(lowest_immers_pos) From 7d7fc8736e6d4ea5e784c6692dc8eea742e05e89 Mon Sep 17 00:00:00 2001 From: Camillo Moschner <122165124+BioCam@users.noreply.github.com> Date: Mon, 29 Dec 2025 12:05:46 +0000 Subject: [PATCH 3/9] Update pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py index 81da40ba15..036f561138 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py @@ -9019,7 +9019,7 @@ async def move_z_drive_to_liquid_surface_using_clld( raise RuntimeError(f"No tip mounted on channel {channel_idx}") # Ensure valid channel index - if (not 0 <= channel_idx <= self.num_channels - 1) and isinstance(channel_idx, int): + if not isinstance(channel_idx, int) or not (0 <= channel_idx <= self.num_channels - 1): raise ValueError(f"channel_idx must be in [0, {self.num_channels - 1}], is {channel_idx}") # Correct for tip length + fitting depth From 4bcfdbdd86315925605db17e6fede4c7d9eca4b1 Mon Sep 17 00:00:00 2001 From: Camillo Moschner <122165124+BioCam@users.noreply.github.com> Date: Mon, 29 Dec 2025 12:06:14 +0000 Subject: [PATCH 4/9] Update pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../liquid_handling/backends/hamilton/STAR_backend.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py index 036f561138..d8a21221fa 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py @@ -9014,14 +9014,14 @@ async def move_z_drive_to_liquid_surface_using_clld( """ # Preconditions checks - # Ensure tip is mounted - if not (await self.request_tip_presence())[channel_idx]: - raise RuntimeError(f"No tip mounted on channel {channel_idx}") - # Ensure valid channel index if not isinstance(channel_idx, int) or not (0 <= channel_idx <= self.num_channels - 1): raise ValueError(f"channel_idx must be in [0, {self.num_channels - 1}], is {channel_idx}") + # Ensure tip is mounted + tip_presence = await self.request_tip_presence() + if not tip_presence[channel_idx]: + raise RuntimeError(f"No tip mounted on channel {channel_idx}") # Correct for tip length + fitting depth tip_len = await self.request_tip_len_on_channel(channel_idx) From c09639d54709cc015ff1e4c334a280b738ce74a5 Mon Sep 17 00:00:00 2001 From: Camillo Moschner <122165124+BioCam@users.noreply.github.com> Date: Mon, 29 Dec 2025 12:06:30 +0000 Subject: [PATCH 5/9] Update pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py index d8a21221fa..697e253f4b 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py @@ -9029,7 +9029,7 @@ async def move_z_drive_to_liquid_surface_using_clld( safe_head_top_z_pos = 334.7 if start_pos_search is None: - start_pos_search = 334.7 - tip_len + fitting_depth + start_pos_search = safe_head_top_z_pos - tip_len + fitting_depth channel_head_start_pos = start_pos_search + tip_len - fitting_depth safe_head_bottom_z_pos = 99.98 + tip_len - fitting_depth + 0.5 # add 0.5 mm safety margin From 3809027563b808366c3ce4f887cacd9d601ea7f8 Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Tue, 30 Dec 2025 13:00:17 +0000 Subject: [PATCH 6/9] transfer `C0` dependency into `clld_probe_z_height_using_channel` --- .../backends/hamilton/STAR_backend.py | 114 +++++++++++------- 1 file changed, 73 insertions(+), 41 deletions(-) diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py index 697e253f4b..7bd71d7519 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py @@ -1718,12 +1718,15 @@ async def probe_liquid_heights( # detect liquid heights current_absolute_liquid_heights = await asyncio.gather( *[ - self.move_z_drive_to_liquid_surface_using_clld( + self._move_z_drive_to_liquid_surface_using_clld( channel_idx=channel, lowest_immers_pos=container.get_absolute_location("c", "c", "cavity_bottom").z + tip.total_tip_length - tip.fitting_depth, - start_pos_search=container.get_absolute_location("c", "c", "t").z + 5, + start_pos_search=container.get_absolute_location("c", "c", "t").z + + 5 + + tip.total_tip_length + - tip.fitting_depth, ) for channel, container, tip in zip(use_channels, containers, tips) ] @@ -8970,7 +8973,7 @@ async def clld_probe_y_position_using_channel( return round(material_y_pos, 1) - async def move_z_drive_to_liquid_surface_using_clld( + async def _move_z_drive_to_liquid_surface_using_clld( self, channel_idx: int, # 0-based indexing of channels! lowest_immers_pos: float = 99.98, # mm @@ -9018,25 +9021,9 @@ async def move_z_drive_to_liquid_surface_using_clld( if not isinstance(channel_idx, int) or not (0 <= channel_idx <= self.num_channels - 1): raise ValueError(f"channel_idx must be in [0, {self.num_channels - 1}], is {channel_idx}") - # Ensure tip is mounted - tip_presence = await self.request_tip_presence() - if not tip_presence[channel_idx]: - raise RuntimeError(f"No tip mounted on channel {channel_idx}") - # Correct for tip length + fitting depth - tip_len = await self.request_tip_len_on_channel(channel_idx) - - fitting_depth = 8 # mm, for 10, 50, 300, 1000 ul Hamilton tips - safe_head_top_z_pos = 334.7 - - if start_pos_search is None: - start_pos_search = safe_head_top_z_pos - tip_len + fitting_depth - - channel_head_start_pos = start_pos_search + tip_len - fitting_depth - safe_head_bottom_z_pos = 99.98 + tip_len - fitting_depth + 0.5 # add 0.5 mm safety margin - # Conversions & machine-compatibility check of parameters lowest_immers_pos_increments = STARBackend.mm_to_z_drive_increment(lowest_immers_pos) - start_pos_search_increments = STARBackend.mm_to_z_drive_increment(channel_head_start_pos) + start_pos_search_increments = STARBackend.mm_to_z_drive_increment(start_pos_search) channel_speed_increments = STARBackend.mm_to_z_drive_increment(channel_speed) channel_acceleration_thousand_increments = STARBackend.mm_to_z_drive_increment( channel_acceleration / 1000 @@ -9047,10 +9034,6 @@ async def move_z_drive_to_liquid_surface_using_clld( f"Lowest immersion position must be between \n{STARBackend.z_drive_increment_to_mm(9_320)}" + f" and {STARBackend.z_drive_increment_to_mm(31_200)} mm, is {lowest_immers_pos} mm" ) - assert safe_head_bottom_z_pos <= channel_head_start_pos <= safe_head_top_z_pos, ( - f"Start position of LLD search must be between \n{safe_head_bottom_z_pos}" - + f" and {safe_head_top_z_pos} mm, is {channel_head_start_pos} mm" - ) assert 20 <= channel_speed_increments <= 15_000, ( f"LLD search speed must be between \n{STARBackend.z_drive_increment_to_mm(20)}" + f"and {STARBackend.z_drive_increment_to_mm(15_000)} mm/sec, is {channel_speed} mm/sec" @@ -9096,31 +9079,80 @@ async def clld_probe_z_height_using_channel( post_detection_dist: float = 2.0, # mm move_channels_to_save_pos_after: bool = False, ) -> float: - """Probes the Z-height below the specified channel on a Hamilton STAR liquid handling machine - using the channels 'capacitive Liquid Level Detection' (cLLD) capabilities. - N.B.: this means only conductive materials can be probed! + """Probe the liquid surface Z-height using a channel's capacitive LLD (cLLD). + + Uses the specified channel to perform a downward cLLD search and returns the + last liquid level detected by the instrument for that channel. + + This helper is responsible for: + - Ensuring a tip is mounted on the chosen channel. + - Reading the mounted tip length and applying the fixed fitting depth (8 mm) + to convert *tip-referenced* Z positions (C0-style coordinates) into the + channel Z-drive coordinates required by the firmware `ZL` cLLD command. + - Optionally moving channels to a Z-safe position after probing. + + Note: + cLLD requires a conductive target (e.g., conductive liquid / surface). Args: - channel_idx: The index of the channel to use for probing. Backmost channel = 0. - lowest_immers_pos: The lowest immersion position in mm. This is the position of the channel, NOT including the tip length (as C0 commands do). So you have to add the total_tip_length - fitting_depth. - start_pos_lld_search: The start position for z-touch search in mm. This is the position of the channel, NOT including the tip length (as C0 commands do). So you have to add the total_tip_length - fitting_depth. - channel_speed: The speed of channel movement in mm/sec. - channel_acceleration: The acceleration of the channel in mm/sec**2. - detection_edge: The edge steepness at capacitive LLD detection. - detection_drop: The offset after capacitive LLD edge detection. - post_detection_trajectory (0, 1): Movement of the channel up (1) or down (0) after contacting the surface. - post_detection_dist: Distance to move into the trajectory after detection in mm. - move_channels_to_save_pos_after: Flag to move channels to a safe position after operation. + channel_idx: Channel index to probe with (0-based; backmost channel = 0). + lowest_immers_pos: Lowest allowed search position in mm, expressed in the + *tip-referenced* coordinate system (i.e., the position you would use for + commands that include tip length). Internally converted to channel Z-drive + coordinates before issuing `ZL`. + start_pos_search: Start position for the cLLD search in mm, expressed in the + *tip-referenced* coordinate system. Internally converted to channel Z-drive + coordinates before issuing `ZL`. + channel_speed: Search speed in mm/s. Defaults to 10.0. + channel_acceleration: Search acceleration in mm/s^2. Defaults to 800.0. + detection_edge: Edge steepness threshold for cLLD detection (0–1023). Defaults to 10. + detection_drop: Offset applied after cLLD edge detection (0–1023). Defaults to 2. + post_detection_trajectory: Firmware post-detection move mode (0 or 1). Defaults to 1. + post_detection_dist: Distance in mm to move after detection (interpreted per trajectory). + Defaults to 2.0. + move_channels_to_save_pos_after: If True, moves all channels to a Z-safe position + after the probing sequence completes. + + Raises: + RuntimeError: If no tip is mounted on `channel_idx`. + AssertionError: If the computed start position is outside the allowed safe range. + STARFirmwareError: If the firmware reports an error during cLLD (channels are moved + to Z-safe before re-raising). Returns: - The detected Z-height in mm. + The detected liquid surface Z-height in mm as reported by `request_pip_height_last_lld()` + for `channel_idx`. """ + # Ensure tip is mounted + tip_presence = await self.request_tip_presence() + if not tip_presence[channel_idx]: + raise RuntimeError(f"No tip mounted on channel {channel_idx}") + # Correct for tip length + fitting depth + tip_len = await self.request_tip_len_on_channel(channel_idx) + + fitting_depth = 8 # mm, for 10, 50, 300, 1000 ul Hamilton tips + safe_head_top_z_pos = 334.7 + + if start_pos_search is None: + start_pos_search = safe_head_top_z_pos - tip_len + fitting_depth + + channel_head_start_pos = round(start_pos_search + tip_len - fitting_depth, 2) + safe_head_bottom_z_pos = round(99.98 + tip_len - fitting_depth, 2) + lowest_immers_pos_corrected = round(lowest_immers_pos + tip_len - fitting_depth, 2) + + assert safe_head_bottom_z_pos <= channel_head_start_pos <= safe_head_top_z_pos, ( + f"Start position of LLD search must be between \n{safe_head_bottom_z_pos}" + + f" and {safe_head_top_z_pos} mm, is {channel_head_start_pos} mm\n" + + f" because tip_lenght-fitting_depth = {tip_len - fitting_depth} mm +" + + f" start_pos_search = {start_pos_search} mm" + ) + try: - await self.move_z_drive_to_liquid_surface_using_clld( + await self._move_z_drive_to_liquid_surface_using_clld( channel_idx=channel_idx, - lowest_immers_pos=lowest_immers_pos, - start_pos_search=start_pos_search, + lowest_immers_pos=lowest_immers_pos_corrected, + start_pos_search=channel_head_start_pos, channel_speed=channel_speed, channel_acceleration=channel_acceleration, detection_edge=detection_edge, From 52919d7db46dfe35313a2976b29fbd117a7a27c1 Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Tue, 30 Dec 2025 13:03:07 +0000 Subject: [PATCH 7/9] fix typo --- pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py index 7bd71d7519..12f0db88b3 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py @@ -8977,7 +8977,7 @@ async def _move_z_drive_to_liquid_surface_using_clld( self, channel_idx: int, # 0-based indexing of channels! lowest_immers_pos: float = 99.98, # mm - start_pos_search: Optional[float] = None, # mm + start_pos_search: float = 334.7, # mm channel_speed: float = 10.0, # mm channel_acceleration: float = 800.0, # mm/sec**2 detection_edge: int = 10, @@ -9144,7 +9144,7 @@ async def clld_probe_z_height_using_channel( assert safe_head_bottom_z_pos <= channel_head_start_pos <= safe_head_top_z_pos, ( f"Start position of LLD search must be between \n{safe_head_bottom_z_pos}" + f" and {safe_head_top_z_pos} mm, is {channel_head_start_pos} mm\n" - + f" because tip_lenght-fitting_depth = {tip_len - fitting_depth} mm +" + + f" because tip_length-fitting_depth = {tip_len - fitting_depth} mm +" + f" start_pos_search = {start_pos_search} mm" ) From a1201fb451f0ecbc41c5457b40a26ef28a68a467 Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Tue, 30 Dec 2025 13:23:42 +0000 Subject: [PATCH 8/9] provide more informative error message --- .../liquid_handling/backends/hamilton/STAR_backend.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py index 12f0db88b3..91baea8691 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py @@ -9133,19 +9133,20 @@ async def clld_probe_z_height_using_channel( fitting_depth = 8 # mm, for 10, 50, 300, 1000 ul Hamilton tips safe_head_top_z_pos = 334.7 + safe_tip_bottom_z_pos = 99.98 + safet_tip_top_z_pos = safe_head_top_z_pos - tip_len + fitting_depth if start_pos_search is None: start_pos_search = safe_head_top_z_pos - tip_len + fitting_depth channel_head_start_pos = round(start_pos_search + tip_len - fitting_depth, 2) - safe_head_bottom_z_pos = round(99.98 + tip_len - fitting_depth, 2) + safe_head_bottom_z_pos = round(safe_tip_bottom_z_pos + tip_len - fitting_depth, 2) lowest_immers_pos_corrected = round(lowest_immers_pos + tip_len - fitting_depth, 2) assert safe_head_bottom_z_pos <= channel_head_start_pos <= safe_head_top_z_pos, ( - f"Start position of LLD search must be between \n{safe_head_bottom_z_pos}" - + f" and {safe_head_top_z_pos} mm, is {channel_head_start_pos} mm\n" - + f" because tip_length-fitting_depth = {tip_len - fitting_depth} mm +" - + f" start_pos_search = {start_pos_search} mm" + f"Start position of LLD search must be between \n{safe_tip_bottom_z_pos}" + + f" and {safet_tip_top_z_pos} mm, is {start_pos_search} mm " + + f" ({safe_head_top_z_pos=}mm, {tip_len=} mm)" ) try: From 4c132a0d83fa5bf31207c148b576ccd92077f8ba Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Tue, 30 Dec 2025 14:14:25 +0000 Subject: [PATCH 9/9] minimise diff --- .../liquid_handling/backends/hamilton/STAR_backend.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py index 91baea8691..3459a8b0bf 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py @@ -1724,9 +1724,9 @@ async def probe_liquid_heights( + tip.total_tip_length - tip.fitting_depth, start_pos_search=container.get_absolute_location("c", "c", "t").z - + 5 + tip.total_tip_length - - tip.fitting_depth, + - tip.fitting_depth + + 5, ) for channel, container, tip in zip(use_channels, containers, tips) ] @@ -9034,6 +9034,10 @@ async def _move_z_drive_to_liquid_surface_using_clld( f"Lowest immersion position must be between \n{STARBackend.z_drive_increment_to_mm(9_320)}" + f" and {STARBackend.z_drive_increment_to_mm(31_200)} mm, is {lowest_immers_pos} mm" ) + assert 9_320 <= start_pos_search_increments <= 31_200, ( + f"Start position of LLD search must be between \n{STARBackend.z_drive_increment_to_mm(9_320)}" + + f" and {STARBackend.z_drive_increment_to_mm(31_200)} mm, is {start_pos_search} mm" + ) assert 20 <= channel_speed_increments <= 15_000, ( f"LLD search speed must be between \n{STARBackend.z_drive_increment_to_mm(20)}" + f"and {STARBackend.z_drive_increment_to_mm(15_000)} mm/sec, is {channel_speed} mm/sec"