Skip to content

Issue with our 96 well plate semiskirted definition #321

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

ashah03
Copy link

@ashah03 ashah03 commented Dec 3, 2024

@mackenziekormann is testing this plate definition and it currently isn't working. Don't want to merge (yet) so thought a draft PR is the easiest way to share the code, let me know if there is a better way!

Reference to PLR Forum post: https://discuss.pylabrobot.org/t/using-pcr-plates-on-star/74/6

Datasheet (left side)
image

Mackenzie will add more details on the issue!

@mackenziekormann
Copy link

The issue I'm having is essentially that I can align the tips with the wells in the x-direction such that the first tip goes into A1, but subsequent tips aren't spaced properly to enter A2, A3, etc. Based on the spec it should work, so wondering if I'm missing anything in the definition, or if there's a fix other than just trial and error.

@rickwierenga
Copy link
Member

item_dy looks correct so i am not sure what the issue is.

microplates actually use the transpose of ms excel-style notation, so you would expect channels to go into B1, C1, etc. Columns have the same number, rows have the same letter, assuming the plate is in landscape orientation on the deck.

could you maybe share the code you are using to aspirate?

@mackenziekormann
Copy link

Through trial and error I figured out that the issue was that the location when assigning the plate to the adapter needs to manually be set using location=Coordinate(0, 0, 0). To get it to work with the other measurements, I set the pedestal_size_z of the carrier to 0.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a through a separate pr, #315. did you make a compared to what i have there?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not totally sure what you mean by this reference to the single channel trough

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah apologies this got mixed into the history - will clean up the PR before requesting to merge

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nw

@rickwierenga
Copy link
Member

why do you need to set the location to Coordinate(0, 0, 0)? what is the location without this manual update? should just work using the default value (None means the parent will compute the child's location, which in this case is apparently done incorrectly)

@mackenziekormann
Copy link

Without setting the location that way the tips hit on the left edge of the plate where the edge of the adapter is rather than in the wells. Not sure why this is happening, just something I noticed in testing.

@ashah03
Copy link
Author

ashah03 commented Dec 4, 2024

@mackenziekormann can you post the code with how you configured the plate with the plate adapter and carrier? I imagine it has something to do with that.

And maybe there is some way to specify what the default location is? @rickwierenga how/where is the location computed, and how can we check what it is without the manual update?

@rickwierenga
Copy link
Member

Without setting the location that way the tips hit on the left edge of the plate where the edge of the adapter is rather than in the wells. Not sure why this is happening, just something I noticed in testing.

could you get me the plate.location?

@rickwierenga
Copy link
Member

or just the code also works

@mackenziekormann
Copy link

mackenziekormann commented Dec 4, 2024

`pcr_plt_car = PLT_CAR_L5PCR(name='pcr_plate_carrier')
pcr_plt_car[0] = pcr_adapter_0 = Hamilton_96_adapter_188182('pcr_adapter_0')
pcr_plt_car[1] = pcr_adapter_1 = Hamilton_96_adapter_188182('pcr_adapter_1')

lh.deck.assign_child_resource(pcr_plt_car, rails=19)

pcr_plate_0 = Eppendorf_96_wellplate_250ul_Vb_semiskirted('pcr_plate_0')
pcr_plate_1 = Eppendorf_96_wellplate_250ul_Vb_semiskirted('pcr_plate_1')

pcr_adapter_0.assign_child_resource(pcr_plate_0, location=Coordinate(0, 0, 0))
pcr_adapter_1.assign_child_resource(pcr_plate_1, location=Coordinate(0, 0, 0))
`
There may be a more efficient way to do this, but this was the first way that worked for me.

@rickwierenga
Copy link
Member

@rickwierenga how/where is the location computed, and how can we check what it is without the manual update?

PlateHolder.assign_child_resource or PlateAdapter.assign_child_resource. the default location is computed (if the arg is None), which can be overridden by user specification of a location

@ashah03
Copy link
Author

ashah03 commented Dec 4, 2024

@rickwierenga how/where is the location computed, and how can we check what it is without the manual update?

PlateHolder.assign_child_resource or PlateAdapter.assign_child_resource. the default location is computed (if the arg is None), which can be overridden by user specification of a location

Got it, so my understanding is that in this case based on Mackenzie's code above, the plate adapter is computing the location wrong for some reason.

@rickwierenga
Copy link
Member

the computed offsets are Coordinate(x=-10.44, y=-6.44, z=2.8).

does it work perfectly when you specify it to be zero, does the tip reach the center bottom? or is it in the well but not quite perfect?

if it is perfect, that would make it likely that the logic of putting plates into an adapter is wrong.

if it close, the location of the holder wrt the carrier might be wrong. since this carrier has "adapters" rather than simple plateaus for taking plates, it is possible the location i parsed from venus is incorrect. These are the locations of the ResourceHolders, set at Coordinate(4.0, 8.5+96*site_index, 107.5).

@mackenziekormann
Copy link

It seems to be pretty close to perfect from what I'm looking at. I've attached an image of the output of lh.summary when I don't set the coordinates to (0, 0, 0) and when I do.
image
image

@rickwierenga
Copy link
Member

yes, i also got that. i am curious about what physically happens on the robot. is Coordinate(x=498.56, y=65.06, z=210.3) perfect?

@mackenziekormann
Copy link

I'll upload videos of both situations

@mackenziekormann
Copy link

mackenziekormann commented Dec 4, 2024

This is the video of the location not set
https://youtube.com/shorts/VOpDDW2X_G0?feature=share
This is the video of the location set to (0, 0, 0)
https://youtube.com/shorts/8imk3w-munQ?feature=share

(sorry about the YouTube links, the files were too big to directly upload)

@rickwierenga
Copy link
Member

i think the location of the plate adapter on the carrier is wrong.

The carrier is 135mm wide (standard width), and the adapter is 110mm wide (confirmed by Camillo). According to the definition i got from venus, the adapter is 4mm wrt the carrier on the left hand side, which would leave 135-110-4=21mm on the other side. This is wrong, because the adapter is in the middle of the carrier. The location should probably be (135-110)/2=12.5 so it's in the middle.

With your location of the plate wrt the adapter explicitly set to 0, the location of the plate wrt the carrier in the x dimension is 4 (equal to the location of the adapter wrt the carrier). If I correct the site location to 12.5, the location of the plate wrt the carrier would be 12.5-10.44=2.06 (10.44 = computed offset), 1.96mm further left than currently. I hope this works, although the 2nd video you shared looks pretty close, but it's hard to see exactly on video.

What probably happens is: in venus, the location of the site already factors in the correction of the PlateAdapter whereas we compute it dynamically in PlateAdapter. In other words, the location of the adapter in venus is actually the location of where the plate would go (so that plate wrt adapter can be 0). It's yet another example of a hack in venus to make things work but does not accurately model reality.

I will measure this tonight when i am back in the lab to confirm. If you get the chance, could you confirm that setting the location to x=-1.96 works for aspiration?

@mackenziekormann
Copy link

Setting the location that way (x=-1.96) is pretty accurate. Looks to me as though the dispensing may be ever so slightly offset to the left in the well, but I'm not sure if it's actually off or not.

@rickwierenga
Copy link
Member

based on my computation it looks accurate, but obviously physical reality is what counts. i will confirm when back in lab

@mackenziekormann
Copy link

Wanted to add that the tips are currently going too deep into the wells and causing some lifting on the plate/ overflow when I dispense 250 ul due to displacement from the tip. Tried plate_z_offset:

pcr_plt_car_0 = PLT_CAR_L5PCR(name='pcr_plate_carrier_0')
pcr_plt_car_0[0] = pcr_adapter_00 = Hamilton_96_adapter_188182('pcr_adapter_00')
pcr_adapter_00.plate_z_offset=3.2

Also tried to set the 3.2 mm value when assigning the plate to the adapter (which is not the correct way to go about it, as it's setting the entire plate above the adapter, but wanted to see if this hack worked)

pcr_plate_00 = Eppendorf_96_wellplate_250ul_Vb_semiskirted('pcr_plate_00')
pcr_adapter_00.assign_child_resource(pcr_plate_00, location=Coordinate(0, 0, 3.2))

The only other things I can think to do are to change immersion_depth in the dispense method or to change the Well's size_z, but neither of those get to the root of the problem and don't mirror the actual reality conditions.

@rickwierenga
Copy link
Member

(which is not the correct way to go about it, as it's setting the entire plate above the adapter, but wanted to see if this hack worked)

did it? i saw @BioCam left some todo-comments in the plate adapter for z handling, so possibly a z-adjustment there is the way to go.

i agree that changing immersion_depth etc is not preferred for the reason you name

@mackenziekormann
Copy link

Changing the location using location=Coordinate(x, y, z) didn't seem to work for me. I tried setting it pretty large (up to 10 mm) and it didn't have an effect on the tip depth. I'll check out the comments on the plate adapter.

@rickwierenga
Copy link
Member

the minimum location reached is determined by the well bottom. it should change when you change the location of the plate. have you verified with get_absolute_location() that things are actually changing? i'm a bit skeptical since you also say the location of 96-head tip discard on initialization doesn't change when you update the location of the 96 head trash

@mackenziekormann
Copy link

I tried checking the absolute location using get_absolute_location() as specified here and was shown that location did actually change. when location=Coordinate(0, 0, 0), output is Coordinate(x=509.0, y=71.5, z=207.5). when location=Coordinate(0, 0, 6), output is Coordinate(x=509.0, y=71.5, z=213.5)

@rickwierenga
Copy link
Member

can you check if the firmware log is changing? (use logging, docs here: https://docs.pylabrobot.org/user_guide/configuration.html)

@ashah03
Copy link
Author

ashah03 commented Dec 6, 2024

I will measure this tonight when i am back in the lab to confirm. If you get the chance, could you confirm that setting the location to x=-1.96 works for aspiration?

Did you have a chance to check this?

@rickwierenga
Copy link
Member

oh, yes, i did. the location is 11.8 mm on both sides, as suspected. the carrier is 134mm wide instead of 135. i will update in a separate PR

@BioCam
Copy link
Contributor

BioCam commented Dec 6, 2024

Thanks @rickwierenga for tagging me, I haven't seen this discussion yet:

Yes, there are a couple of issues I can see:

pcr_plt_car = PLT_CAR_L5PCR(name='pcr_plate_carrier')
pcr_plt_car[0] = pcr_adapter_0 = Hamilton_96_adapter_188182('pcr_adapter_0') # <===== THIS

...is wrong because...

Screenshot 2024-12-06 at 13 28 34

...the definition for PLT_CAR_L5PCR is wrong by Hamilton definition, not PLRdefinition (because there is no PLR definition yet) :)

What does "wrong" mean here? Wrong means that both carriers are modelled identically by VENUS/Hamilton in a classic "VENUS is oversimplifying reality and makes figuring out what is actually happening dependent on the individual who sets up the rigid automation" way.
Because we haven't fixed this yet it is up to us now to fix it:

Both carriers are believed to have a "plate_holder/plate_site" in the identical locations, that is wrong.

@BioCam
Copy link
Contributor

BioCam commented Dec 6, 2024

The carrier is 135mm wide (standard width), and the adapter is 110mm wide (confirmed by Camillo). According to the definition i got from venus, the adapter is 4mm wrt the carrier on the left hand side, which would leave 135-110-4=21mm on the other side. This is wrong, because the adapter is in the middle of the carrier. The location should probably be (135-110)/2=12.5 so it's in the middle.

Everything mentioned by Rick here is correct. The adapter is not in the same location as the "normal" plate_holder/site. And would need to be changed.

(which is not the correct way to go about it, as it's setting the entire plate above the adapter, but wanted to see if this hack worked)

did it? i saw @BioCam left some todo-comments in the plate adapter for z handling, so possibly a z-adjustment there is the way to go.

i agree that changing immersion_depth etc is not preferred for the reason you name

Yes, I would also not recommend modifying the immersion_depth.

Instead I gave the PlateAdapter a unique attribute to deal with this exact situation -> plate_z_offset.

The issue with my #TODO that I noticed really quickly is that every plate_adapter-plate combination will likely require a different z-offset for the plate!
This z-offset is not simply defined by how deep the plate_adapter holes are, nor by how deep wells are, but by both of these + the actual geometry of the holes and the wells 🙈

E.g. imagine placing a round-bottom plate onto a plate_adapter with V-shaped holes:
It is doable but the round-well-bottom will not allow the full well to immerse itself into the hole.

Since we cannot model all geometries of all possible well types and all possible plate_adapter hole types, I deemed the idea of automatic z-offset calculation as too complicated.

Instead:

  1. Measure the height of the carrier before adding the plate_adapter, measurement_1.
  2. Add plate_adapter + plate of interest on top of it
  3. Measure the bottom of a well that sits on top of the plate_adapter, measurement_2. (which we can now finally do, thanks to the zprobing_functions I created in PR#260 :) )
  4. Set plate_adapter.plate_z_offset = measurement_2 - measurement_1

You can take these measurements for any plate that you want to place onto your plate_adapter, store these values and update plate_adapter.plate_z_offset at runtime.
This means you can for the first time in Hamilton-history (to my knowledge), use different plates on the same plate_adapter in the same run.

Mackenzie Kormann and others added 2 commits January 7, 2025 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants