Skip to main content

PyLabRobot Integration

Orca Framework uses PyLabRobot (PLR) as its hardware abstraction layer. PLR provides:

  • Resource definitions — geometry for plates, tip racks, troughs, lids.
  • Device backends — drivers for liquid handlers, plate readers, and other instruments.
  • Chatterbox simulators — driver-level simulators that exercise the real driver code paths without touching hardware.

Orca wraps PLR resources and drivers in a workflow-aware layer so they can participate in scheduling, labware tracking, and event flow.

The catalog is the integration point

Orca templates do not take PLR objects. A PlateTemplate names a catalog slug string (labware_type); the runtime resolves that slug against the labware catalog at build time, and PLR sits one layer further down, backing the catalog rows. So the flow is:

PLR definition  →  catalog row (keyed by slug)  →  template (names the slug)  →  instance
Orca conceptWhat backs it
PlateTemplate(name, "slug")A catalog row whose geometry came from a PLR Plate definition.
TipRackTemplate(name, "slug", with_tips=...)A catalog row backed by a PLR TipRack definition.
TroughTemplate(name, "slug")A catalog row backed by a PLR Trough definition.
LiquidHandler deck modelingPLR-backed deck tree; the sim path uses ChatterboxLiquidHandlerWithProtocolDriver.
SimulationPLR Chatterbox backends.

See Labware for the three-layer model (catalog → template → instance).

How PLR definitions reach the catalog

cheshire_drivers.plr exposes factory callables (e.g. plr_plate_factory, plr_tip_rack_factory, plr_trough_factory, defined in cheshire_drivers.plr.labware) that wrap PLR resource definitions. These seed catalog rows under string slugs. Once a row exists, a template binds to it by slug:

from orca.sdk.labware import PlateTemplate

sample_plate = PlateTemplate("sample_plate", "Cor_96_wellplate_360ul_Fb")

PLR ships definitions for most common labware. Browse pylabrobot/resources/ to see what is available before adding your own.

Adding a labware definition PLR doesn't have

If your lab uses a plate PLR doesn't ship a definition for, you have two options:

  1. Contribute upstream — submit a PR to PyLabRobot. Best long-term.
  2. Define locally — register a custom catalog row (via the plr_plate_factory/catalog custom-row path) and reference it by slug from your template. You never pass a PLR object into PlateTemplate.

TODO: walkthrough of registering a custom catalog row from PLR primitives.

Liquid handler drivers

You do not pass a driver to LiquidHandler. The device class calls resolve_drivers(...) in its constructor; the device factory context in scope picks the driver, selected by run mode. The deck-modeling sim used by LiquidHandler is ChatterboxLiquidHandlerWithProtocolDriver (the handler owns its deck tree), not a driver you construct and pass in.

TODO: full factory-context walkthrough covering binding a real STAR/Vantage backend vs the Chatterbox sim.

Calling liquid-handler methods inside actions

Once you've narrowed ctx.device(ILiquidHandler), you call the interface methods, which wrap PLR underneath:

@orca.action(device=liquid_handler, inputs=[plate, tips])
async def transfer(ctx: ActionContext):
lh = ctx.device(ILiquidHandler)
rack = ctx.tip_rack("tips")
plate = ctx.plate("plate")

await lh.pick_up_tips([rack.tip_spot("A1")])
await lh.aspirate([plate.well("A1")], [50.0], flow_rates=[10.0])
await lh.dispense([plate.well("B1")], [50.0], flow_rates=[15.0])
await lh.drop_tips([rack.tip_spot("A1")])

The ctx.plate(name), ctx.tip_rack(name), and ctx.trough(name) accessors return the underlying PLR resource so you can address wells and tip spots (plate.well("A1"), rack.tip_spot("A1")). You pass those addresses to the ILiquidHandler interface methods (aspirate, dispense, pick_up_tips, ...), which wrap PLR. You call the interface, not the PLR backend directly.

Where to go next

  • Devices — Orca's device wrappers around PLR backends.
  • Labware — defining labware templates that bind to catalog slugs.
  • PyLabRobot docs — full PLR reference.