Moving plate onto Alpaqua magnet using CORE grippers#

  • tags: #resourcemovement #plateadapter #hamiltonstar

  • Last updated: 2025-10-27

Prerequisites#

  • Machines used:

    • Hamilton STAR

  • Non-PLR dependencies: None

Preview of Machine Behvaiour#

Protocol Mode#

protocol_mode = "simulation" # "execution" or "simulation"

Import Statements#

Non-PLR Dependencies#

None

Machine & Visualizer#

%load_ext autoreload
%autoreload 2
       
import random 
import time

from pylabrobot.liquid_handling import LiquidHandler
from pylabrobot.resources.hamilton import STARLetDeck
from pylabrobot.visualizer.visualizer import Visualizer

if protocol_mode == "execution":

  from pylabrobot.liquid_handling.backends import STARBackend

  backend = STARBackend()

elif protocol_mode == "simulation":

  from pylabrobot.liquid_handling.backends.hamilton.STAR_chatterbox import STARChatterboxBackend
    
  backend = STARChatterboxBackend()

Required Resources#

from pylabrobot.resources import (
  hamilton_mfx_carrier_L5_base,
  hamilton_mfx_plateholder_DWP_metal_tapped,
  hamilton_mfx_plateholder_DWP_flat,
  alpaqua_96_plateadapter_magnum_flx,
  Azenta4titudeFrameStar_96_wellplate_200ul_Vb,
)

Note: if you’re unsure whether you have the resources mentioned in a PLR automated Protocol, use Python’s inbuild help function to check out the resource definition’s docstring - they always contain the manufacturer’s catalogue/part number (and if available a direct link to the product page):

help(hamilton_mfx_plateholder_DWP_metal_tapped) # for more information visit the docs 'Resource Management' section
Help on function hamilton_mfx_plateholder_DWP_metal_tapped in module pylabrobot.resources.hamilton.mfx_modules:

hamilton_mfx_plateholder_DWP_metal_tapped(name: str) -> pylabrobot.resources.carrier.PlateHolder
    Hamilton MFX DWP Module (cat.-no. 188042 / 188042-00).
    Hamilton name: 'MFX_DWP_rackbased_module'
    It also contains metal clamps at the corners.
    https://www.hamiltoncompany.com/other-robotics/188042

Instantiate Frontend & Connect to Machine#

deck = STARLetDeck()
lh = LiquidHandler(backend=backend, deck=deck)

await lh.setup()

vis = Visualizer(resource=lh)
await vis.setup()

await backend.disable_cover_control() # 😈
Websocket server started at http://127.0.0.1:2121
File server started at http://127.0.0.1:1337 . Open this URL in your browser.
C0CDid0001

Configure Deck Layout#

# Setup MFX Carrier for Magnetic Bead Resuspension

mfx_plateholder_dwp_0 = hamilton_mfx_plateholder_DWP_metal_tapped(
    name=f"mfx_plateholder_dwp_tapped_dwp_0"
)

mfx_carrier_tapped_plate_holder_example = hamilton_mfx_carrier_L5_base(
  name="mfx_carrier_tapped_plate_holder_example",
  modules={
    0: mfx_plateholder_dwp_0,
  }
)

mfx_carrier_tapped_plate_holder_example[0] = Azenta4titudeFrameStar_96_wellplate_200ul_Vb(
  name="wash_plate_0"
)

deck.assign_child_resource(mfx_carrier_tapped_plate_holder_example, rails=1)

# Setup Magnet-carrying MFX Carrier

plateholder_flat_0 = hamilton_mfx_plateholder_DWP_flat(name=f"plateholder_flat_0")
magnet_0 = alpaqua_96_plateadapter_magnum_flx(name=f"alpaqua_magnet_0")
plateholder_flat_0.assign_child_resource(magnet_0)

mfx_carrier_magnet_example = hamilton_mfx_carrier_L5_base(
  name="mfx_carrier_magnet_example",
  modules={
    0: plateholder_flat_0,
  }
)
magnet_0.plate_z_offset = 0.62  # <===== PLATE-SPECIFIC !
# empirical: distance between Alpaqua magnet hole bottom to
# cavity_bottom of the well that is placed on top of it
# use ztouch_probing to measure both 

deck.assign_child_resource(mfx_carrier_magnet_example, rails=8)

Execution#

Move Plate Onto Magnet PlateAdapter#

plate_index = 0 # always design for throughput adaptivness ;)

plate_to_move = lh.deck.get_resource(f"wash_plate_{plate_index}")
move_target = lh.deck.get_resource(f"alpaqua_magnet_{plate_index}")


back_channel_idx = random.randint(1, 6)  # Reduce wear & tear on any single channel

if protocol_mode == "simulation":
  time.sleep(2)
    
await lh.move_plate(
  plate=plate_to_move,
  to=move_target,
  use_arm="core",
  channel_1=back_channel_idx,
  channel_2=back_channel_idx + 1,
  pickup_distance_from_top=6,
  core_grip_strength=40,
  return_core_gripper=False,
)

if protocol_mode == "execution":
  # "smart" command, will ask operator for input if it cannot find plate in move_target location
  # place into condition for simulation mode

  # (1) check transfer success, (2) push plate flush
  await backend.core_check_resource_exists_at_location_center(
    location=plate_to_move.get_absolute_location(),
    resource=plate_to_move,
    gripper_y_margin=9,
    enable_recovery=True,
    audio_feedback=False,
  )

print(backend.core_parked)
# >>> False # save time - keep CORE grippers on channels during magnetisation time

if protocol_mode == "simulation":
  time.sleep(2)
C0ZPid0002xs01679xd0yj1147yv0050zj1932zy0500yo0885yg0825yw40th2800te2800
C0ZRid0003xs03254xd0yj1138zj2162zi000zy0500yo0885th2800te2800
False

Move Plate back onto tapped PlateHolder#

move_target = lh.deck.get_resource(f"mfx_plateholder_dwp_tapped_dwp_{plate_index}")

await lh.move_plate(
  plate=plate_to_move,
  to=move_target,
  use_arm="core",
  channel_1=back_channel_idx,
  channel_2=back_channel_idx + 1,
  pickup_distance_from_top=6,
  core_grip_strength=40,
  return_core_gripper=False,
)

if protocol_mode == "execution":

  await backend.core_check_resource_exists_at_location_center(
    location=plate_to_move.get_absolute_location(),
    resource=plate_to_move,
    gripper_y_margin=9,
    enable_recovery=True,
    audio_feedback=False,
  )
   
await backend.return_core_gripper_tools()

print(backend.core_parked)
# >>> True
C0ZPid0004xs03254xd0yj1138yv0050zj2162zy0500yo0885yg0825yw40th2800te2800
C0ZRid0005xs01679xd0yj1147zj1932zi000zy0500yo0885th2800te2800
C0ZSid0006xs07975xd0ya1250yb1070tp2150tz2050th2800te2800
True