Skip to main content

Move and pipetting parameters

An arm grips a plate at a height, opens its jaws by a width, and backs off a site by a clearance. None of those numbers is a constant in the source. Each one is resolved when the move is planned, out of stored records an operator edits. This page is for whoever is calibrating a machine, or working out why a plate was handled the way it was.

The short answer: a move's numbers start from a built-in seed and are narrowed by the arm, the labware, the site, and finally the one plate being carried. orca move-defaults show <arm> prints the resolved record and the layer that decided each field. Where to change them gives the CLI verb for each layer. Pipetting parameters fold the same way, with fewer layers.

Where a move's numbers come from

Most moves are the same move. The answer starts from a built-in seed, picks up whatever this deployment has tuned about the arm, and is narrowed by whatever is more specific about this one move:

seed -> arm defaults -> labware -> site -> this labware at this site -> this one plate
LayerName in sourcesSays
SeedseedNobody has said anything about this field.
Arm defaultsdefaultsSomebody tuned this field for this arm, deployment-wide.
LabwarelabwareThe labware being carried decided it, wherever it is going.
SitesiteThe position being reached into decided it.
This labware heresite_labwareThis labware at this one position decided it.
CarrycarrySomebody said how to carry THIS piece of labware, for as long as it holds.

Each layer is strictly narrower than the one before. The labware comes before the site because a labware's profile travels with it everywhere, while a nest's clearance is about one place and should win over a general statement about the type. Last is the combination narrower than either: this labware, at this position, which is where a nest that suits every plate but one gets its exception without changing how that plate is handled anywhere else.

carry is the only layer about one physical object rather than a type, a place, or an arm. It exists for what the others cannot say: this plate came out of the sealer with a lid on it, carry it higher until the lid is off.

Layers are sparse

Each layer contributes a patch, and the merge is field-wise: a layer sets what it knows and inherits the rest. That is what lets one number be changed without restating the other nine. None in a patch means inherit, so a patch cannot express "put this field back to inheriting". Handing a field back is a separate operation that names the field: --clear <field> on every editing surface.

Which layer won

The resolved result carries sources, a field-to-layer map. Without it, a plate that moved oddly leaves nobody able to say which layer decided that, which is the failure mode a layered model invites. It is also what keeps "this deployment chose 25" apart from "nobody has ever looked at this number":

$ orca move-defaults show pf400
resource_width 75.0 (seed)
resource_height 14.35 (seed)
travel_margin 12.0 (defaults)
jaw_opening 14.0 (seed)

Where to change them

Every layer except the seed is a stored record an operator edits. The store the runtime resolves moves against is the one these surfaces edit, so a change reaches the next move rather than a second copy of the numbers. Reads never write: a transporter nobody has tuned reports the seed and says so.

LayerCLIRuntime facade
Arm defaultsorca move-defaults list | show | set | resetmove_defaults
Labwareorca grip-profiles list | show | set | resetgrip_profiles
Siteorca access-configs ..., referenced by a teachpointteachpoint and access config facades
This labware hereorca teachpoints labware set | clear | listteachpoint facade
Carryorca labware carry / orca labware carry-normallylabware
# The arm as a whole: this gripper opens wider than the seed assumes.
orca move-defaults set pf400 --jaw-opening 18.0

# A labware type, measured once and used by every arm.
orca grip-profiles set Cor_96_wellplate_360ul_Fb --resource-width 76.0 --grip-distance-from-top 2.0

# One labware type at one position.
orca teachpoints labware set pf400 sealer Cor_96_wellplate_360ul_Fb --z-offset -1.5

# This one plate, right now, because it came out of the sealer lidded.
orca labware carry a1b2c3d4 --z-offset 4.0
orca labware carry-normally a1b2c3d4

Both edit shapes take set and clear in one request. An operator swapping a gripper does both at once, and two requests would leave the arm briefly on a mixture neither of them intended. An edit naming a field in both halves is refused: no stored patch can hold both, so whichever way a merge resolved it, half the request would be dropped and the caller told the write succeeded.

These are physical edits, and the surfaces say so before they apply. Changing an arm's defaults changes every move it makes from then on, queued moves included, and a clearance or grip width that does not match the hardware crashes the arm into the deck.

Some fields belong to a layer and are refused elsewhere

The four approach fields (access_type, clearance, z_above, grasp_offset) belong to the position. Every teachpoint supplies all four on every move, whether or not an access config named them, so a value stored wider than the site is overwritten on every pick and place. Storing one would keep a number no move reads and report it back as somebody's choice, so an edit naming one on an arm's defaults or on a grip profile is refused, and the refusal names the fields and where they do belong. The layers narrower than the site are a different matter and are not checked: a teachpoint's per-labware override and a carry override both resolve after the site and are meant to win.

grip_distance_from_top belongs to the labware. Its one reader takes it off the labware's grip profile and nowhere else, so an edit naming it on an arm's move defaults is refused too. It is geometry of the plate rather than a preference of the arm: one number per arm would be wrong for every labware but the one it was measured on.

The fields

The resolved record is total: every field is present, and every field is a number the arm can act on. Nothing labware-shaped crosses to the driver, so a transporter driver never needs a labware catalog to work out how to hold something.

FieldMeaningSeed
access_typeHow the site is entered and left: "vertical" or "horizontal"."vertical"
clearanceHow far the arm backs off the labware approaching and departing. On a vertical site this is a floor on the entry height, not the entry height: the arm works out how high it must be from what is standing at the site and takes this only if it is higher.20.0
z_aboveLift after a horizontal retract. Zero for a vertical approach.0.0
grasp_offsetHow deep the site is: the lip of the pad or the wall of the pocket, which a carried leg has to rise clear of. A property of the site, so it is the same for a tip box and a microplate in one nest.20.0
resource_widthJaw separation for the grip, measured on the skirt rather than the footprint.75.0
resource_heightHow tall the labware is. Used to pass over whatever is standing at a site on the way in to pick it.14.35
travel_marginSafety margin added to the rise.10.0
jaw_openingHow far the jaws open to release, or to clear the labware on approach.14.0
plate_present_marginHow far a held labware must hold the jaws above the grip position for the grip to count as holding something.2.0
z_offsetGrip height relative to what this teachpoint was taught with. Zero when the labware being moved is the labware the position was taught on.0.0
grip_distance_from_topHow far below the labware's own top the jaws close, for a gripper with no teachpoint to measure from.None
speedPercent of full speed, or None to leave the arm where it is.None

Three seeds are weak on purpose. resource_width is one plate's measurement standing in for every labware until a grip profile supplies the real one. resource_height is a standard microplate, and stands only for a move carrying nothing (see below). grip_distance_from_top is seeded None rather than weakly, because it differs per labware by more than a factor of ten: 1 to 2.5 mm below the top on a flat plate, around 19 mm on a deep-well block. There is no number that is merely imprecise for everything, so None hands the decision to the robot's own labware definition, and a grip profile is where the measured answer goes.

A load's height comes from the labware, not from its name

Every move needs the height of what the arm is holding, because the retreat has to lift the load clear of what held it. That height is read off the labware instance itself and is the base of the labware layer, under any grip profile, so someone who measured this labware on this arm still overrules the catalog figure.

It used to be resolved on the driver side from a name-keyed catalog, and the name did not match the key: an Opentrons tip rack stored its vendor load name while the catalog was keyed by the factory name. The lookup missed silently, the seeded 14.35 mm microplate height stood in for a 99 mm rack, and an arm was one command away from approaching a 99 mm rack at 44 mm.

A move that is carrying nothing states no height. "How does this arm reach that position" and "how will it hold this rack" are different questions, and the first must not answer the second.

A handler's own gripper asks how the labware should be held

An external arm knows the height it was taught at and adjusts from there with z_offset. A liquid handler's own deck gripper is told a slot and works the height out from the labware, so it needs the answer in the labware's own terms. That is grip_distance_from_top, and the deck gripper reads it off the grip profile of the labware it is carrying before issuing its move.

Left at None it hands the decision to the robot's own labware definition, and a definition that states none leaves an Opentrons Flex gripping at the labware's mid-height, roughly 5 mm too low on a standard flat-bottom microplate. Grip the skirt: Opentrons' own definitions put it 1 to 2.5 mm below the top on a flat plate, around 6 mm on a PCR plate with a raised well block, and around 19 mm on a deep-well block. A plate that is not held where it should be wants a grip profile.

One answer, whoever asked

An operator moving a plate by hand and a workflow moving the same plate resolve through the same code. Two ways of deciding how a labware is handled is how a plate moves one way under a workflow and another way when someone picks it up by hand.

Two cases where the answer is deliberately narrower rather than absent:

  • A bare gripper command names no teachpoint, so nothing narrows it, but it still has to open by the number a pick opens by. It resolves the deployment's edit over the seed, which keeps the button and the pick on one answer.
  • A name that resolves to nothing (an unmounted topology, an ad-hoc coordinate pick at a device the system does not model) still gets a real answer: the seed narrowed by the site. It gets no labware layer, because the profiles live on the runtime the name did not resolve against.

Pipetting parameters

Pipetting folds the same way, in one place, with fewer layers:

defaults -> the liquid being moved -> the step being run

A parameter gets set for two different reasons and only one of them is about the liquid. Glycerol is drawn slowly because it is viscous, wherever it goes. A last top-up into a nearly-full well is dispensed slowly so it does not splash, whatever is in it. So one shape serves both layers, PipettingProfile, and they differ by what they are keyed on:

from cheshire_drivers.pipetting import MixParams, PipettingProfile

await lh.aspirate(
wells, [100.0] * 8,
liquid_class=PipettingProfile(name="glycerol", height=2.0, flow_rate=20.0),
technique=PipettingProfile(mix=MixParams(volume=80.0, repetitions=5, flow_rate=150.0)),
)

PipettingProfile fields: name (labels the layer in sources, never sent to a machine), height (tip end above the inner floor of the well, mm), flow_rate (uL/s), blow_out, blow_out_volume, blow_out_flow_rate, and mix. A field left None says nothing and inherits from the layer underneath.

Direction lives in the instance, not the shape. A liquid whose draw and delivery want different rates is two profiles built from one base with dataclasses.replace. Put a blow-out on the base rather than on the delivery profile: a head that expels air the tip took in needs the aspirate to have taken it, so a blow-out named at delivery alone fails the dispense with a tip already full of liquid.

Every surface that pipettes folds through the same resolver, so an operator pipetting ad hoc gets the answer a workflow gets. The result carries sources in the same way a move does, and a named profile labels its own layer, because "glycerol" tells a reader more than "liquid class". height and flow_rate seed as None on purpose: the right bottom clearance differs per machine, and pinning a number would raise a Hamilton's tip by the Flex's 3 mm.

See also

  • Transporters: teachpoints, access configs, and what happens around a move.
  • Devices: which capabilities a driver advertises, including the gantry parking a step-aside needs.
  • Labware: templates, instance identity and the position ledger.
  • CLI: operator interventions: driving a device by hand, and the calibration verbs.