Liquid level detection error on EVO with fixed tips

I am trying to aspirate using a Tecan EVO200 with fixed tips.

The tips is clearly submerged into the liquid so i think it is a software error.

this error (error 9) was almost certainly returned by the machine. can you share the full traceback (a large part is missing in the screenshot) so we can see which method caused this error?

Full error:


TecanError Traceback (most recent call last)
Cell In[12], line 2
1 offset = [Coordinate(0, 0, 0)] #2
----> 2 await lh.aspirate(source1[“A1”], vols=[20.0], offsets=offset) # , swap_speed=100, homogenization_speed=1000
4 # await lh.aspirate(
5 # ops=[
6 # Aspiration(
(…)
17
18 # await lh.dispense(dist1[“A1”], vols=[20.0]) # swap_speed=100, homogenization_speed=1000, mix_speed=1000

File ~\OneDrive\Dokumenter\GitHub\pylabrobot_DALSA\pylabrobot\machines\machine.py:23, in need_setup_finished..wrapper(self, *args, **kwargs)
21 if not self.setup_finished:
22 raise RuntimeError(“The setup has not finished. See setup.”)
—> 23 return await func(self, *args, **kwargs)

File ~\OneDrive\Dokumenter\GitHub\pylabrobot_DALSA\pylabrobot\liquid_handling\liquid_handler.py:860, in LiquidHandler.aspirate(self, resources, vols, use_channels, flow_rates, offsets, liquid_height, blow_out_air_volume, **backend_kwargs)
857 (self.head[channel].get_tip().tracker.commit if success else self.head[channel].rollback)()
859 # trigger callback
→ 860 self._trigger_callback(
861 “aspirate”,
862 liquid_handler=self,
863 operations=aspirations,
864 use_channels=use_channels,
865 error=error,
866 **backend_kwargs,
867 )

File ~\OneDrive\Dokumenter\GitHub\pylabrobot_DALSA\pylabrobot\liquid_handling\liquid_handler.py:1976, in LiquidHandler._trigger_callback(self, method_name, error, *args, **kwargs)
1974 callback(self, *args, error=error, **kwargs)
1975 elif error is not None:
→ 1976 raise error

File ~\OneDrive\Dokumenter\GitHub\pylabrobot_DALSA\pylabrobot\liquid_handling\liquid_handler.py:843, in LiquidHandler.aspirate(self, resources, vols, use_channels, flow_rates, offsets, liquid_height, blow_out_air_volume, **backend_kwargs)
841 error: Optional[Exception] = None
842 try:
→ 843 await self.backend.aspirate(ops=aspirations, use_channels=use_channels, **backend_kwargs)
844 except Exception as e:
845 error = e

File ~\OneDrive\Dokumenter\GitHub\pylabrobot_DALSA\pylabrobot\liquid_handling\backends\tecan\EVO.py:386, in EVO.aspirate(self, ops, use_channels)
384 shz = [min(z for z in z_positions[“travel”] if z)] * self.num_channels
385 await self.liha.set_z_travel_height(shz)
→ 386 await self.liha.move_detect_liquid(self._bin_use_channels(use_channels), zadd)
387 await self.liha.set_z_travel_height([self._z_range] * self.num_channels)
389 # aspirate + retract
390 # SSZ: z_add / (vol / asp_speed)

File ~\OneDrive\Dokumenter\GitHub\pylabrobot_DALSA\pylabrobot\liquid_handling\backends\tecan\EVO.py:1029, in LiHa.move_detect_liquid(self, channels, zadd)
1020 async def move_detect_liquid(self, channels: int, zadd: List[Optional[int]]):
1021 “”“Move tip, detect liquid, submerge.
1022
1023 Args:
(…)
1026 must be between 0 and z-start - z-max
1027 “””
→ 1029 await self.backend.send_command(
1030 module=self.module,
1031 command=“MDT”,
1032 params=[channels] + [None] * 3 + zadd,
1033 )

File ~\OneDrive\Dokumenter\GitHub\pylabrobot_DALSA\pylabrobot\liquid_handling\backends\tecan\EVO.py:157, in TecanLiquidHandler.send_command(self, module, command, params, write_timeout, read_timeout, wait)
154 return None
156 resp = self.read(timeout=read_timeout)
→ 157 return self.parse_response(resp)

File ~\OneDrive\Dokumenter\GitHub\pylabrobot_DALSA\pylabrobot\liquid_handling\backends\tecan\EVO.py:115, in TecanLiquidHandler.parse_response(self, resp)
113 ret = int(resp[3]) ^ (1 << 7)
114 if ret != 0:
→ 115 raise error_code_to_exception(module, ret)
117 data: List[int] = [int(x) for x in s[3:-1].split(“,”) if x]
118 return {“module”: module, “data”: data}

TecanError: (‘No liquid detected’, ‘C5’, 9)

it fails on await self.liha.move_detect_liquid(self._bin_use_channels(use_channels), zadd). this suggests the software is functioning as intended - the robot is just reporting there is no liquid. one thing i can think of: is the tip already submerged when this method is called, or is it still above the liquid?

does this issue also occur in evoware?

Hello, we have the same problem. We can confirm that the tip is immersed in the liquid, but the error ‘not detected liquid’ still appears. What is the reason? We guess that the robot’s sensor is faulty.

i don’t know what the reason is. likely a faulty sensor. does it also occur in evoware?

i suppose in plr we could simply not use liquid level detection

1 Like

Hey Rick :slight_smile:
@Lydia7 is working with me on the same EVO as i have been testing on.

The problem does not occure in EvoWare.
We are using fixed tips. This may be the thing causing the problems as i think Willson was using DITI cones and not fixed tips.

I think there may be a bug in PLR for using fixed tips.

1 Like

Hey Rick
Do you know how to turn off liquid level detection on an Evo?

There is this code in evo.py:


    # perform liquid level detection
    # TODO: verify for other liquid detection modes
    if any(tlc.aspirate_lld if tlc is not None else None for tlc in tecan_liquid_classes):
      tlc, _ = self._first_valid(tecan_liquid_classes)
      assert tlc is not None
      detproc = tlc.lld_mode  # must be same for all channels?
      sense = tlc.lld_conductivity
      await self.liha.set_detection_mode(detproc, sense)
      ssl, sdl, sbl = self._liquid_detection(use_channels, tecan_liquid_classes)
      await self.liha.set_search_speed(ssl)
      await self.liha.set_search_retract_distance(sdl)
      await self.liha.set_search_z_start(z_positions["start"])
      await self.liha.set_search_z_max(list(z if z else self._z_range for z in z_positions["max"]))
      await self.liha.set_search_submerge(sbl)
      shz = [min(z for z in z_positions["travel"] if z)] * self.num_channels
      await self.liha.set_z_travel_height(shz)
      await self.liha.move_detect_liquid(self._bin_use_channels(use_channels), zadd)
      await self.liha.set_z_travel_height([self._z_range] * self.num_channels)

Currently, we infer liquid classes (from evoware). This is dumb and needs to be removed in a future version (we need the user to explicitly specify the liquid class they are using). That is the bigger fix.

In the meantime, I think it’s worth adding a single parameter liquid_level_detection: bool that users can use (via backend_kwargs) to optionally disable/enable LLD. It would override whatever the automatically inferred liquid class is (because we will remove that at some point).

1 Like