OT2RobotGeometry: add static OT-2 robot geometry with per-mount reach checks#1130
Conversation
The OT-2's deck/gantry geometry is factory-identical across machines, so it lives as a constant rather than something probed from the device (unlike STAR hardware). This dataclass holds the gantry extents, partial-tip padding offsets, and left/right mount offsets, sourced from the Opentrons shared-data definitions. `single_channel_reach` / `can_reach_position` / `channel_y_offsets` mirror `STARBackend.can_reach_position`; the head8 channel offsets are validated against the Opentrons eight_channel nozzle map (A1 back-most at +31.5, 9 mm pitch). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| @staticmethod | ||
| def channel_y_offsets(num_channels: int = 8, channel_pitch: float = 9.0) -> List[float]: | ||
| """Y offset of each channel from the head center, in mm, for a linear multi-channel head. | ||
|
|
||
| Indexed back-to-front to match the Opentrons nozzle map: index 0 is the back-most nozzle (row | ||
| A, the primary/critical point) at the largest +y, descending to the front-most nozzle at -y. | ||
| Defaults describe the head8: 8 channels at 9 mm pitch spanning the head center by +-31.5 mm. | ||
| """ | ||
| half = (num_channels - 1) / 2 | ||
| return [(half - i) * channel_pitch for i in range(num_channels)] |
There was a problem hiding this comment.
given that the 8 channels are fixed, how to interpret num_channels?
There was a problem hiding this comment.
is this for partial tip pickup scenarios?
There was a problem hiding this comment.
as a simple start, I recommend here to index based on coordinates and usual channel indexing:
(1) left to right in x
(2) back to front in y
Each channel is still an independent unit that can act with others but doesn't have to.
That way a tip in tip_spot "C1" e.g. can still be picked up by an 8-channel head's channel_2 and act like a standalone channel, giving maximum power and flexibility.
In v1, with a fully developed head8 capability I would like to build the a "per-head channel indexing" system.
But this needs to be tested in steps and in v0 we can use the Opentrons OT-2 as a testing bed for it.
There was a problem hiding this comment.
That way a tip in tip_spot "C1" e.g. can still be picked up by an 8-channel head's channel_2 and act like a standalone channel, giving maximum power and flexibility.
so the answer to the question is yes?
But this needs to be tested in steps and in v0 we can use the Opentrons OT-2 as a testing bed for it.
this is better done in v1
There was a problem hiding this comment.
Yes this is for partial tip pickup
I agree getting this straight into v1 would be best but since v1 is not ready yet for production and we need this urgently im happy to develop in v0 and v1 in parallel, I.e. merge this and learn from it how to submit updates to v1 in parallel
There was a problem hiding this comment.
In regards to 8 channels: yes i agree that doesn't make sense to make mutable, will update
Co-authored-by: Rick Wierenga <rick_wierenga@icloud.com>
The OT-2 head8 has a fixed 8 channels at 9 mm pitch, so these are not per-call parameters. Inline them and drop the arguments.
| Mirrors ``STARBackend.can_reach_position``: a pure predicate over the reachable region, here the | ||
| single-channel deck extents for the mount. For a single channel use the default | ||
| ``channel_offset=0``. For a head8, pass each channel's offset from :meth:`channel_y_offsets`; the | ||
| center must land within the extents, so near the front/back limits only a subset of channels can | ||
| reach, which is why an edge column is picked up partially rather than all 8. Z is not bounded by | ||
| deck extents and is checked separately by the motion layer. | ||
| """ |
Describe the predicate in its own terms rather than by analogy to another backend.
Adds
OT2RobotGeometry, a frozen dataclass capturing the OT-2's fixed geometry in the robot frame: the gantry working extents, per-mount nozzle offsets, the head8 per-channel y-offsets, and the partial-tip body paddings. Because every OT-2 Standard shares this geometry it is a constant rather than something probed from the device - the key difference from Hamilton STAR, whose installed configuration must be read from firmware.It also provides a centre-based
can_reach_position(mount, position, channel_offset)predicate (mirroringSTARBackend.can_reach_position) that works for single and multi-channel alike: a single pipette useschannel_offset=0, a head8 passes each nozzle's offset fromchannel_y_offsets(). Near the front/back limits only a subset of a multi's nozzles reach, which is why an edge column is picked up partially rather than all 8.Pure additive: a new module, its tests, and one export. No behaviour change. This is the foundation for wiring reach checks into the OT-2 backend operations, which lands in a follow-up.
🤖 Generated with Claude Code