I’m having a consistent issue with using the CO-RE 96 head immediately after initialization after a power cycle. When attempting to pick up tips at the beginning of a method in this scenario, I get the following error:
---------------------------------------------------------------------------
STARFirmwareError Traceback (most recent call last)
Cell In[11], line 1
----> 1 await lh.pick_up_tips96(lh.get_resource('tips_01'))
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/liquid_handler.py:1056, in LiquidHandler.pick_up_tips96(self, tip_rack, offset, **backend_kwargs)
1054 tip_spot.tracker.rollback()
1055 self.head96[i].rollback()
-> 1056 self._trigger_callback(
1057 "pick_up_tips96",
1058 liquid_handler=self,
1059 pickup=pickup_operation,
1060 error=error,
1061 **backend_kwargs,
1062 )
1063 else:
1064 for i, tip_spot in enumerate(tip_rack.get_all_items()):
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/liquid_handler.py:1654, in LiquidHandler._trigger_callback(self, method_name, error, *args, **kwargs)
1652 callback(self, *args, error=error, **kwargs)
1653 elif error is not None:
-> 1654 raise error
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/liquid_handler.py:1047, in LiquidHandler.pick_up_tips96(self, tip_rack, offset, **backend_kwargs)
1045 pickup_operation = PickupTipRack(resource=tip_rack, offset=offset)
1046 try:
-> 1047 await self.backend.pick_up_tips96(
1048 pickup=pickup_operation,
1049 **backend_kwargs
1050 )
1051 except Exception as error: # pylint: disable=broad-except
1052 for i, tip_spot in enumerate(tip_rack.get_all_items()):
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/STAR.py:61, in need_iswap_parked.<locals>.wrapper(self, *args, **kwargs)
58 if self.iswap_installed and not self.iswap_parked:
59 await self.park_iswap()
---> 61 result = await method(self, *args, **kwargs) # pylint: disable=not-callable
63 return result
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/STAR.py:1970, in STAR.pick_up_tips96(self, pickup, tip_pickup_method, z_deposit_position, minimum_height_command_end, minimum_traverse_height_at_beginning_of_a_command)
1967 ttti = await self.get_or_assign_tip_type_index(tip_a1)
1968 position = tip_spot_a1.get_absolute_location() + tip_spot_a1.center() + pickup.offset
-> 1970 return await self.pick_up_tips_core96(
1971 x_position=int(position.x * 10),
1972 x_direction=0,
1973 y_position=int(position.y * 10),
1974 tip_type_idx=ttti,
1975 tip_pickup_method=tip_pickup_method,
1976 z_deposit_position=z_deposit_position,
1977 minimum_height_command_end=minimum_height_command_end,
1978 minimum_traverse_height_at_beginning_of_a_command=
1979 minimum_traverse_height_at_beginning_of_a_command,
1980 )
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/STAR.py:4825, in STAR.pick_up_tips_core96(self, x_position, x_direction, y_position, tip_type_idx, tip_pickup_method, z_deposit_position, minimum_traverse_height_at_beginning_of_a_command, minimum_height_command_end)
4820 assert 0 <= minimum_traverse_height_at_beginning_of_a_command <= 3425, \
4821 "minimum_traverse_height_at_beginning_of_a_command must be between 0 and 3425"
4822 assert 0 <= minimum_height_command_end <= 3425, \
4823 "minimum_height_command_end must be between 0 and 3425"
-> 4825 return await self.send_command(
4826 module="C0",
4827 command="EP",
4828 xs=f"{x_position:05}",
4829 xd=x_direction,
4830 yh=f"{y_position:04}",
4831 tt=f"{tip_type_idx:02}",
4832 wu=tip_pickup_method,
4833 za=f"{z_deposit_position:04}",
4834 zh=f"{minimum_traverse_height_at_beginning_of_a_command:04}",
4835 ze=f"{minimum_height_command_end:04}",
4836 )
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/base.py:193, in HamiltonLiquidHandler.send_command(self, module, command, tip_pattern, write_timeout, read_timeout, wait, fmt, **kwargs)
171 """ Send a firmware command to the Hamilton machine.
172
173 Args:
(...)
188 A dictionary containing the parsed response, or None if no response was read within `timeout`.
189 """
191 cmd, id_ = self._assemble_command(module=module, command=command, tip_pattern=tip_pattern,
192 **kwargs)
--> 193 resp = await self._write_and_read_command(id_=id_, cmd=cmd, write_timeout=write_timeout,
194 read_timeout=read_timeout, wait=wait)
195 if resp is not None and fmt is not None:
196 return self._parse_response(resp, fmt)
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/base.py:220, in HamiltonLiquidHandler._write_and_read_command(self, id_, cmd, write_timeout, read_timeout, wait)
218 fut = loop.create_future()
219 self._start_reading(id_, loop, fut, cmd, read_timeout)
--> 220 result = await fut
221 return cast(str, result)
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/base.py:293, in HamiltonLiquidHandler._continuously_read(self)
291 if response_id == id_:
292 try:
--> 293 self.check_fw_string_error(resp)
294 except Exception as e: # pylint: disable=broad-exception-caught
295 loop.call_soon_threadsafe(fut.set_exception, e)
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/STAR.py:1181, in STAR.check_fw_string_error(self, resp)
1172 if error.message == "Unknown parameter":
1173 # temp. disabled until we figure out how to handle async in parse response (the
1174 # background thread does not have an event loop, and I'm not sure if it should.)
(...)
1177
1178 # pylint: disable=unnecessary-dict-index-lookup
1179 he[module_name].message += " (call lh.backend.request_name_of_last_faulty_parameter)"
-> 1181 raise he
STARFirmwareError: STARFirmwareError(errors={'CoRe 96 Head': HardwareError('Dispensing drive not initialized')}, raw_response=C0EPid0008er99/00 H002/51)
I’ve been able to work around this for the time being by plugging the robot back into the VENUS computer and running a simple method that does an initialization through VENUS itself, and then plugging back into the PLR computer.
However, this is usually (always?) followed by another issue that seems related. Essentially the head will now pick up the tips just fine, but when rising out of the tip rack, the head does not go up high enough in the z direction, so when it attempts to traverse to an aspiration it crashes into the tip rack it just picked up from. I’ve figured out I can get around this by testing a tip pickup after doing the above initialization workaround, and seeing if it rises high enough. If not, I restart the kernel + clear cells, redo the lh.setup(), and then try again. Usually this now results in the head rising the necessary height. This crash issue throws this error once it crashes, if helpful:
INFO:pylabrobot:Sent command: C0EPid0042xs01179xd0yh1458tt01wu0za2164zh2450ze2450
DEBUG:pylabrobot:Starting reading thread...
commencing cycle: 1
DEBUG:pylabrobot:Received data: bytearray(b'')
INFO:pylabrobot:Received response:
DEBUG:pylabrobot:Received data: bytearray(b'')
INFO:pylabrobot:Received response:
DEBUG:pylabrobot:Received data: bytearray(b'C0EPid0042er00/00')
INFO:pylabrobot:Received response: C0EPid0042er00/00
DEBUG:pylabrobot:Reading thread stopped.
INFO:pylabrobot:Sent command: C0EAid0043aa0xs03880xd0yh5300zh2450ze2450lz1999zt1881pp0100zm1269zv0032zq06180iw000ix0fh000af06750ag2500vt050bv00000wv00050cm0cs1bs0020wh10hv00000hc00hp000mj000hs1200cwFFFFFFFFFFFFFFFFFFFFFFFFcr000cj0cx0
DEBUG:pylabrobot:Starting reading thread...
DEBUG:pylabrobot:Received data: bytearray(b'C0EAid0043er99/00 X002/52 H002/57')
INFO:pylabrobot:Received response: C0EAid0043er99/00 X002/52 H002/57
DEBUG:pylabrobot:Reading thread stopped.
---------------------------------------------------------------------------
STARFirmwareError Traceback (most recent call last)
Cell In[35], line 2
1 ## Run it
----> 2 await growth_cycles(num_cycles)
Cell In[9], line 14, in growth_cycles(cycles)
12 if cycle == 0:
13 initial_vol = growth_vol+read_vol
---> 14 await aspirate_trough(initial_vol)
15 await initiate_growth(growth_vol)
16 await dispense_reader(read_vol)
Cell In[6], line 18, in aspirate_trough(vol)
16 async def aspirate_trough(vol):
17 await lh.pick_up_tips96(lh.get_resource('tips_01'))
---> 18 await lh.aspirate_plate(lh.get_resource('trough_plate'), volume=vol, flow_rates=aspirate_speed)
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/liquid_handler.py:1259, in LiquidHandler.aspirate_plate(self, plate, volume, flow_rate, end_delay, blow_out_air_volume, **backend_kwargs)
1257 well.tracker.rollback()
1258 channel.get_tip().tracker.rollback()
-> 1259 self._trigger_callback(
1260 "aspirate_plate",
1261 liquid_handler=self,
1262 aspiration=aspiration_plate,
1263 error=error,
1264 **backend_kwargs,
1265 )
1266 else:
1267 for channel, well in zip(self.head96.values(), plate.get_all_items()):
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/liquid_handler.py:1654, in LiquidHandler._trigger_callback(self, method_name, error, *args, **kwargs)
1652 callback(self, *args, error=error, **kwargs)
1653 elif error is not None:
-> 1654 raise error
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/liquid_handler.py:1253, in LiquidHandler.aspirate_plate(self, plate, volume, flow_rate, end_delay, blow_out_air_volume, **backend_kwargs)
1241 aspiration_plate = AspirationPlate(
1242 resource=plate,
1243 volume=volume,
(...)
1249 liquids=cast(List[List[Tuple[Optional[Liquid], float]]], all_liquids) # stupid
1250 )
1252 try:
-> 1253 await self.backend.aspirate96(aspiration=aspiration_plate, **backend_kwargs)
1254 except Exception as error: # pylint: disable=broad-except
1255 for channel, well in zip(self.head96.values(), plate.get_all_items()):
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/STAR.py:61, in need_iswap_parked.<locals>.wrapper(self, *args, **kwargs)
58 if self.iswap_installed and not self.iswap_parked:
59 await self.park_iswap()
---> 61 result = await method(self, *args, **kwargs) # pylint: disable=not-callable
63 return result
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/STAR.py:2150, in STAR.aspirate96(self, aspiration, jet, blow_out, use_lld, liquid_height, air_transport_retract_dist, hlc, aspiration_type, minimum_traverse_height_at_beginning_of_a_command, minimal_end_height, lld_search_height, maximum_immersion_depth, tube_2nd_section_height_measured_from_zm, tube_2nd_section_ratio, immersion_depth, immersion_depth_direction, liquid_surface_sink_distance_at_the_end_of_aspiration, transport_air_volume, pre_wetting_volume, gamma_lld_sensitivity, swap_speed, settling_time, homogenization_volume, homogenization_cycles, homogenization_position_from_liquid_surface, surface_following_distance_during_homogenization, speed_of_homogenization, limit_curve_index)
2134 pull_out_distance_to_take_transport_air_in_function_without_lld = \
2135 int(air_transport_retract_dist * 10)
2137 # Was this ever true? Just copied it over from pyhamilton. Could have something to do with
2138 # the liquid classes and whether blow_out mode is enabled.
2139 # # Unfortunately, `blow_out_air_volume` does not work correctly, so instead we aspirate air
(...)
2147 # aspiration_volumes=int(blow_out_air_volume * 10)
2148 # )
-> 2150 return await self.aspirate_core_96(
2151 x_position=int(position.x * 10),
2152 x_direction=0,
2153 y_positions=int(position.y * 10),
2154 aspiration_type=aspiration_type,
2155
2156 minimum_traverse_height_at_beginning_of_a_command=
2157 minimum_traverse_height_at_beginning_of_a_command,
2158 minimal_end_height=minimal_end_height,
2159 lld_search_height=lld_search_height,
2160 liquid_surface_at_function_without_lld=liquid_surface_at_function_without_lld,
2161 pull_out_distance_to_take_transport_air_in_function_without_lld=
2162 pull_out_distance_to_take_transport_air_in_function_without_lld,
2163 maximum_immersion_depth=maximum_immersion_depth,
2164 tube_2nd_section_height_measured_from_zm=tube_2nd_section_height_measured_from_zm,
2165 tube_2nd_section_ratio=tube_2nd_section_ratio,
2166 immersion_depth=immersion_depth,
2167 immersion_depth_direction=immersion_depth_direction,
2168 liquid_surface_sink_distance_at_the_end_of_aspiration=
2169 liquid_surface_sink_distance_at_the_end_of_aspiration,
2170 aspiration_volumes=aspiration_volumes,
2171 aspiration_speed=flow_rate,
2172 transport_air_volume=transport_air_volume,
2173 blow_out_air_volume=blow_out_air_volume,
2174 pre_wetting_volume=pre_wetting_volume,
2175 lld_mode=int(use_lld),
2176 gamma_lld_sensitivity=gamma_lld_sensitivity,
2177 swap_speed=swap_speed,
2178 settling_time=settling_time,
2179 homogenization_volume=homogenization_volume,
2180 homogenization_cycles=homogenization_cycles,
2181 homogenization_position_from_liquid_surface=
2182 homogenization_position_from_liquid_surface,
2183 surface_following_distance_during_homogenization=
2184 surface_following_distance_during_homogenization,
2185 speed_of_homogenization=speed_of_homogenization,
2186 channel_pattern=channel_pattern,
2187 limit_curve_index=limit_curve_index,
2188 tadm_algorithm=False,
2189 recording_mode=0,
2190 )
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/STAR.py:5026, in STAR.aspirate_core_96(self, aspiration_type, x_position, x_direction, y_positions, minimum_traverse_height_at_beginning_of_a_command, minimal_end_height, lld_search_height, liquid_surface_at_function_without_lld, pull_out_distance_to_take_transport_air_in_function_without_lld, maximum_immersion_depth, tube_2nd_section_height_measured_from_zm, tube_2nd_section_ratio, immersion_depth, immersion_depth_direction, liquid_surface_sink_distance_at_the_end_of_aspiration, aspiration_volumes, aspiration_speed, transport_air_volume, blow_out_air_volume, pre_wetting_volume, lld_mode, gamma_lld_sensitivity, swap_speed, settling_time, homogenization_volume, homogenization_cycles, homogenization_position_from_liquid_surface, surface_following_distance_during_homogenization, speed_of_homogenization, channel_pattern, limit_curve_index, tadm_algorithm, recording_mode)
5023 channel_pattern_bin_str = reversed(["1" if x else "0" for x in channel_pattern])
5024 channel_pattern_hex = hex(int("".join(channel_pattern_bin_str), 2)).upper()[2:]
-> 5026 return await self.send_command(
5027 module="C0",
5028 command="EA",
5029 aa=aspiration_type,
5030 xs=f"{x_position:05}",
5031 xd=x_direction,
5032 yh=f"{y_positions:04}",
5033 zh=f"{minimum_traverse_height_at_beginning_of_a_command:04}",
5034 ze=f"{minimal_end_height:04}",
5035 lz=f"{lld_search_height:04}",
5036 zt=f"{liquid_surface_at_function_without_lld:04}",
5037 pp=f"{pull_out_distance_to_take_transport_air_in_function_without_lld:04}",
5038 zm=f"{maximum_immersion_depth:04}",
5039 zv=f"{tube_2nd_section_height_measured_from_zm:04}",
5040 zq=f"{tube_2nd_section_ratio:05}",
5041 iw=f"{immersion_depth:03}",
5042 ix=immersion_depth_direction,
5043 fh=f"{liquid_surface_sink_distance_at_the_end_of_aspiration:03}",
5044 af=f"{aspiration_volumes:05}",
5045 ag=f"{aspiration_speed:04}",
5046 vt=f"{transport_air_volume:03}",
5047 bv=f"{blow_out_air_volume:05}",
5048 wv=f"{pre_wetting_volume:05}",
5049 cm=lld_mode,
5050 cs=gamma_lld_sensitivity,
5051 bs=f"{swap_speed:04}",
5052 wh=f"{settling_time:02}",
5053 hv=f"{homogenization_volume:05}",
5054 hc=f"{homogenization_cycles:02}",
5055 hp=f"{homogenization_position_from_liquid_surface:03}",
5056 mj=f"{surface_following_distance_during_homogenization:03}",
5057 hs=f"{speed_of_homogenization:04}",
5058 cw=channel_pattern_hex,
5059 cr=f"{limit_curve_index:03}",
5060 cj=tadm_algorithm,
5061 cx=recording_mode,
5062 )
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/base.py:193, in HamiltonLiquidHandler.send_command(self, module, command, tip_pattern, write_timeout, read_timeout, wait, fmt, **kwargs)
171 """ Send a firmware command to the Hamilton machine.
172
173 Args:
(...)
188 A dictionary containing the parsed response, or None if no response was read within `timeout`.
189 """
191 cmd, id_ = self._assemble_command(module=module, command=command, tip_pattern=tip_pattern,
192 **kwargs)
--> 193 resp = await self._write_and_read_command(id_=id_, cmd=cmd, write_timeout=write_timeout,
194 read_timeout=read_timeout, wait=wait)
195 if resp is not None and fmt is not None:
196 return self._parse_response(resp, fmt)
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/base.py:220, in HamiltonLiquidHandler._write_and_read_command(self, id_, cmd, write_timeout, read_timeout, wait)
218 fut = loop.create_future()
219 self._start_reading(id_, loop, fut, cmd, read_timeout)
--> 220 result = await fut
221 return cast(str, result)
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/base.py:293, in HamiltonLiquidHandler._continuously_read(self)
291 if response_id == id_:
292 try:
--> 293 self.check_fw_string_error(resp)
294 except Exception as e: # pylint: disable=broad-exception-caught
295 loop.call_soon_threadsafe(fut.set_exception, e)
File ~/Documents/Lab/PyLabRobot/pylabrobot/pylabrobot/liquid_handling/backends/hamilton/STAR.py:1181, in STAR.check_fw_string_error(self, resp)
1172 if error.message == "Unknown parameter":
1173 # temp. disabled until we figure out how to handle async in parse response (the
1174 # background thread does not have an event loop, and I'm not sure if it should.)
(...)
1177
1178 # pylint: disable=unnecessary-dict-index-lookup
1179 he[module_name].message += " (call lh.backend.request_name_of_last_faulty_parameter)"
-> 1181 raise he
STARFirmwareError: STARFirmwareError(errors={'X-drives': HardwareError('Unknown trace information code 52'), 'CoRe 96 Head': HardwareError('Y drive movement error')}, raw_response=C0EAid0043er99/00 X002/52 H002/57)