Offset calibration for Opentrons

Hello,

I’m using PyLabRobot to control the OT-2 Robot from Opentrons, and I would like to express my appreciation to the developers for maintaining this excellent library.

I’ve been testing the robot’s basic functionalities with PyLabRobot, and I was able to successfully run the following tasks.

  • Pick up a tip
  • Aspirate 100 uL from the plate
  • Dispense 100 uL to the plate
  • Discard the tip
  • Return to the home position

Below is the code I used for the test:

import asyncio

from pylabrobot.liquid_handling import LiquidHandler
from pylabrobot.liquid_handling.backends import OpentronsBackend
from pylabrobot.resources.opentrons import (
  OTDeck,
  opentrons_96_tiprack_1000ul,
  corning_12_wellplate_6point9ml_flat,
)
from pylabrobot.resources import (
  Coordinate,
  Trash
)

async def main():
    lh = LiquidHandler(backend=OpentronsBackend(host="xxx.xxx.xxx.xxx"), deck=OTDeck())
    await lh.setup()

    tip_rack = opentrons_96_tiprack_1000ul(name="tip_rack")
    lh.deck.assign_child_at_slot(tip_rack, slot=10)

    plate = corning_12_wellplate_6point9ml_flat(name="plate")
    lh.deck.assign_child_at_slot(plate, slot=11)

    await lh.pick_up_tips(tip_rack["A2"],
                          offsets=[Coordinate(0.6, -2.0, 41.0)])
    await lh.aspirate(plate["A1"], vols=[100],
                      offsets=[Coordinate(1.0, 0.0, -11.0)])
    await lh.dispense(plate["A2"], vols=[100],
                      offsets=[Coordinate(1.0, 0.0, -1.0)])
    await lh.discard_tips()
    await lh.backend.home()


if __name__ == "__main__":
    asyncio.run(main())

I have two questions related to offset calibration:

  1. Is there an efficient method for calibrating the offset? I spent a significant amount of time manually adjusting the offset by running the same code with different offset values. It would be great if there were a way to calibrate offsets by moving the robot incrementally, similar to the interface in the Opentrons App, as this would save a lot of time.
  2. Why is such a large offset required for the Z-axis? I needed to specify a 41 mm offset to pick up a tip correctly, which seems unusually large. Additionally, this value differs significantly from what I typically use in the Opentrons App. Is this expected, or could there be a specific reason for the discrepancy?

Thanks in advance for your insights!

2 Likes

thank you for the kind words!

yes, you can use OpentronsBackend.move_pipette_head.

from pylabrobot.resources import Coordinate
await lh.backend.move_pipette_head(
  Coordinate(x, y, z),
  pipette_id="left",
  force_direct=False, # True is faster but dangerous
)

No, this shouldn’t be the case.

I have had to apply

    # ad-hoc offset adjustment that makes it smoother.
    offset_z += 50

to get it to work for me. It has been a while since I worked with OT, but maybe i just never tested with 1000uL tips. The defined tip length for 1000uL tips is 88mm, which is close to 50 (our default) + 41 (your offset). I made a pull request here: use tip length in OT backend by rickwierenga · Pull Request #455 · PyLabRobot/pylabrobot · GitHub. Could you check it out and see if it works? I no longer work directly with OT.

Thank you so much for your swift response!

  1. The offset calibration aims to adjust the discrepancy between the expected position (i.e., where the robot expects the tips or wells to be) and the actual position. Since move_pipette_head() doesn’t take tips or wells as an argument, we need to manually calculate this discrepancy. Is it possible to obtain the xyz coordinates of the expected positions to help with this calculation?

  2. I tested the pull request, and it performed as intended. The robot successfully picked up a tip with an offset of (0.6, -2.0, 2.0), which seems reasonable to me. Thank you for the fix!

yes, you can use e.g. plate.get_well("A1").get_absolute_location(x="c", y="c", z="b") (center, center, bottom):

from pylabrobot.resources import Coordinate
await lh.backend.move_pipette_head(
  plate.get_well("A1").get_absolute_location(x="c", y="c", z="b") + \
    Coordinate(x, y, z),
  pipette_id="left",
  force_direct=False, # True is faster but dangerous
)

Thank you for testing, I will merge the branch!

Thank you for the answer! I will work with get_absolute_location().

1 Like