Tecan Evo Coordinate Layout, PnP Implementation, Arm Configuration, and Resource Pickup

Hello all! I’ve been working on my lab’s implementation of PLR on our Tecan Evo 200 decks. We have essentially 2 identical layouts that seem to rather customized compared to the standard Evo setup. PLR has turned out to be a wonderful repository for interfacing with these machines, and I am making this post in hopes to get some clarity regarding a few subjects so that i may contribute back some of what I’ve implemented. Apologies if these topics should be separated into several posts.

Deck Coordinate Layout and Mapping
I’ve noticed that in the default coordinate mapping, the origin (0,0) is by default the bottom left corner. I’m sure this is standard across many machines, however when referencing coordinates to send to the arms, our machine is setup to where x=0 is still the left side of the deck, but y=0 is roughly the middle of the deck, and approx equal to y=345 in the PLR Evo_backend implementation. In addition, larger positive y values bring the arms down (closer to user), and so essentially the y-axis is inverted. What would you suggest to be a feasible solution so as to simplify development and keep our contributions in line with PLR?

PnP Implementation
My colleague and I have been able to get a working demo of the PnP (tube gripper) using the send_command() afforded by the Evo_backend module. At the moment, its currently moving to arbitrary coordinates we provide as parameters (lots of guess and test), and I am working on adapting it to the existing style that PLR uses. Hoping to have a pull request in the near future.

Arm Configuration
From my understanding, the current arm setup is hardcoded, and I believe I came across another post suggesting custom arm configuration ordering upon initializing. I can also make an attempt at this as I believe i have a pretty good idea to approach it. Currently our arms are PnP, LiHa, RoMa in order from left to right.

Resource Pickup
What is the current method to determine which module/arm picks up and drops a resource? For example, tips are picked up by LiHa, carriers are picked up by RoMa, and tubes are picked up by PnP. Is the arm that moves the resource inferred by the type of resource thats being picked up? Or should it be explicitly declared that the PnP is the module used to pick up a tube when calling pickup_resource()?

Happy to provide any additional context as needed! Cheers and thanks in advance.

2 Likes

wow, that’s a lot of great work! thank you so much for sharing!

did you change the coordinate system at all? i know this is something that’s actually configurable on the firmware level.

on the PLR front end (LiquidHandler), we should standardize the coordinate system. X is from left (smaller) to right, y is from front to back, z is from left to right. All in mm.

this is what the source model is built on. wells with higher y wrt plate are further back (e.g. y of row A > y of row H)

similar to how the EVO backend (and every other backend) converts the universal PLR commands (e.g. aspirate) to specific backend commands (e.g. tecan firmware commands), the backend should also translate from the universal PLR coordinate system to the machine specific coordinate system.

all that should matter on the user-layer is the universal coordinate system. we should be converting/transforming it into the machine coordinate system just before sending those commands.

the zero point is largely arbitrary (relative positions matter most), but we should aim to have a single origin per machine (like the same for all EVO).

Let’s take your EVO’s coordinate system as an example. First, y is flipped so we should multiple all y numbers by -1 in the backend. Second, the y is offset by 345mm to the middle. Since y is bigger towards the front, we should add 345 to translate.

Awesome.

That would be great! See proposal here: Tecan evo supporting different arm configurations. It’s just a draft. I like it, but happy to discuss.

I believe only roma is currently supported for resource movement.

pick_up_tips is used for picking up tips (liha). In PLR, you would never pass a tip to pick_up_resource (we should actually add a check for this on the LiquidHandler level) since pick_up_resource is meant for resource movement, essentially everything but tips/liquid handling.

i think inferring from resource type (tube = pnp) on evo backend is an interesting approach. we should probably do that, in addition to an use_arm: Literal["roma", "pnp"] parameter (or pointing to the specific arm that is configured if we have that ability :thinking:) to explicitly set it.

(the analog is the use_arm: Literal["iswap", "core"] = "iswap", parameter of STARBackend.pick_up_resource)

Thanks Rick for the reply.

We have left the coordinate system untouched. My understanding is that the lab I’m working out of had Tecan build the setup to spec and made a series of changes that would otherwise differ from the standard out-of-the-box setup.

I figured this as well, but given that the existing EVO implementation in PLR is zero’d to the bottom left, I’m guessing my options are to either convert universal coordinates before sending the commands for my use case, or add some customizability to the existing code to add the ability to change the origin. I was leaning towards the former, however, I also wanted to make sure that calling lh.summary() gives the same coordinates that our EVO uses. Ideally i’d like to do so without changing the coordinate system the EVO is using, but if need be, I’ll go that direction.

Great! I’ll take a peak.

Good to know! I’ll keep that in mind. My initial thought was that the resources could contain a reference of the type of arm used to manipulate it. That way Evo_backend.pick_up_resource(tube_0) would infer that it must use the PnP. However, explicity stating use_arm: Literal["roma", "pnp"] might be a better approach for user clarity and potentially allows for future extensions if need be.

1 Like

definitely

i kind of see where you are coming from, however lh is not evo-specific. it is “liquid handler”. things like labware definitions will get messed up when you have axis scaling/permutations (plain translation is fine, although confusing)

i would recommend treating everything as universal PLR coordinates and only seeing the evo ones as an implementation ‘hack’. working with two systems will get confusing.

this would make resources evo-specific. i think a check in the evo backend

if use_arm is None:
  if instance(pickup.resource, Tube):
    use_arm = "pnp"
  else:
    use_arm = "roma"

would be great!

(although as said above, this could even be extended to:

use_arm: EVOArm,

...

if not use_arm in self.arms:
  raise ValueError

to support robots with multiple roma/pnp arms)

Right ok that makes sense.

I get what you mean. I’ll adapt to the PLR coordinate system.

Fair enough, that should be pretty straight forward I would think. I’ll give it a go!

Cheers!

3 Likes