Skip to content

Commit 23b37a9

Browse files
authored
fix(api): fix incorrect gripper point calculation for schema 3 labware (#18920)
1 parent 127003a commit 23b37a9

File tree

2 files changed

+142
-29
lines changed

2 files changed

+142
-29
lines changed

api/src/opentrons/protocol_engine/state/geometry.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from opentrons_shared_data.labware.constants import WELL_NAME_PATTERN
2424
from opentrons_shared_data.labware.labware_definition import (
2525
LabwareDefinition,
26+
LabwareDefinition2,
2627
InnerWellGeometry,
2728
)
2829
from opentrons_shared_data.deck.types import CutoutFixture
@@ -91,6 +92,7 @@
9192
WellLocationType,
9293
WellLocationFunction,
9394
LabwareParentDefinition,
95+
AddressableArea,
9496
)
9597
from ..types.liquid_level_detection import SimulatedProbeResult, LiquidTrackingType
9698
from .config import Config
@@ -1041,26 +1043,44 @@ def get_labware_grip_point(
10411043
grip_height_from_labware_bottom = (
10421044
self._labware.get_grip_height_from_labware_bottom(labware_definition)
10431045
)
1044-
location_name = self._get_underlying_addressable_area_name(location)
1046+
aa_name = self._get_underlying_addressable_area_name(location)
10451047
parent_to_lw_offset = self._get_stackup_placement_origin_to_lw_origin(
10461048
location=location,
10471049
definition=labware_definition,
10481050
is_topmost_labware=True, # We aren't concerned with entities above the gripped labware.
10491051
)
1050-
mod_cal_offset = self._get_calibrated_module_offset(location)
1051-
location_center = self._addressable_areas.get_addressable_area_center(
1052-
location_name
1052+
addressable_area = self._addressable_areas.get_addressable_area(aa_name)
1053+
lw_origin_to_parent = self._get_lw_origin_to_parent(
1054+
labware_definition=labware_definition, addressable_area=addressable_area
10531055
)
1056+
mod_cal_offset = self._get_calibrated_module_offset(location)
1057+
location_center = self._addressable_areas.get_addressable_area_center(aa_name)
10541058

1055-
return Point(
1056-
x=location_center.x + parent_to_lw_offset.x + mod_cal_offset.x,
1057-
y=location_center.y + parent_to_lw_offset.y + mod_cal_offset.y,
1058-
z=location_center.z
1059-
+ parent_to_lw_offset.z
1060-
+ mod_cal_offset.z
1061-
+ grip_height_from_labware_bottom,
1059+
return (
1060+
location_center
1061+
+ parent_to_lw_offset
1062+
+ lw_origin_to_parent
1063+
+ mod_cal_offset
1064+
+ Point(0, 0, grip_height_from_labware_bottom)
10621065
)
10631066

1067+
def _get_lw_origin_to_parent(
1068+
self, labware_definition: LabwareDefinition, addressable_area: AddressableArea
1069+
) -> Point:
1070+
if isinstance(labware_definition, LabwareDefinition2):
1071+
return Point(0, 0, 0)
1072+
else:
1073+
bb_y = addressable_area.bounding_box.y
1074+
bb_z = addressable_area.bounding_box.z
1075+
return (
1076+
Point(
1077+
x=0,
1078+
y=bb_y,
1079+
z=bb_z,
1080+
)
1081+
* -1
1082+
)
1083+
10641084
def get_extra_waypoints(
10651085
self,
10661086
location: Optional[CurrentPipetteLocation],

api/tests/opentrons/protocol_engine/state/test_geometry_view.py

Lines changed: 111 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,15 @@
192192
),
193193
)
194194

195+
_MOCK_LABWARE_DEFINITION2 = LabwareDefinition2.model_construct( # type: ignore[call-arg]
196+
namespace="test",
197+
version=1,
198+
schemaVersion=2,
199+
dimensions=LabwareDimensions(xDimension=1000, yDimension=1200, zDimension=750),
200+
parameters=LabwareDefinition2Parameters.model_construct(loadName="labware-name"), # type: ignore[call-arg]
201+
)
202+
203+
195204
MOCK_ADDRESSABLE_AREA = AddressableArea(
196205
area_name="1",
197206
area_type=AreaType.SLOT,
@@ -2735,31 +2744,76 @@ def test_ensure_location_not_occupied_raises(
27352744
)
27362745

27372746

2738-
def test_get_labware_grip_point(
2747+
def test_get_labware_grip_point_v2_definition(
27392748
decoy: Decoy,
27402749
mock_labware_view: LabwareView,
27412750
mock_addressable_area_view: AddressableAreaView,
27422751
subject: GeometryView,
27432752
) -> None:
2744-
"""It should get the grip point of the labware at the specified location."""
2753+
"""It should get the grip point of a LabwareDefinition2 labware at the specified location."""
27452754
decoy.when(
2746-
mock_labware_view.get_grip_height_from_labware_bottom(
2747-
sentinel.labware_definition
2748-
)
2755+
mock_labware_view.get_grip_height_from_labware_bottom(_MOCK_LABWARE_DEFINITION2)
27492756
).then_return(100)
27502757

27512758
decoy.when(
27522759
mock_addressable_area_view.get_addressable_area_center(DeckSlotName.SLOT_1.id)
27532760
).then_return(Point(x=101, y=102, z=103))
2761+
2762+
decoy.when(
2763+
mock_addressable_area_view.get_addressable_area(DeckSlotName.SLOT_1.id)
2764+
).then_return(MOCK_ADDRESSABLE_AREA)
2765+
2766+
expected_lw_origin_to_parent = Point(0, 0, 0)
2767+
27542768
labware_center = subject.get_labware_grip_point(
2755-
labware_definition=sentinel.labware_definition,
2769+
labware_definition=_MOCK_LABWARE_DEFINITION2,
27562770
location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1),
27572771
)
27582772

27592773
assert labware_center == Point(
2760-
101.0 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.x,
2761-
102.0 + +_PARENT_ORIGIN_TO_LABWARE_ORIGIN.y,
2762-
203 + +_PARENT_ORIGIN_TO_LABWARE_ORIGIN.z,
2774+
101.0 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.x + expected_lw_origin_to_parent.x,
2775+
102.0 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.y + expected_lw_origin_to_parent.y,
2776+
203 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.z + expected_lw_origin_to_parent.z,
2777+
)
2778+
2779+
2780+
def test_get_labware_grip_point_v3_definition(
2781+
decoy: Decoy,
2782+
mock_labware_view: LabwareView,
2783+
mock_addressable_area_view: AddressableAreaView,
2784+
subject: GeometryView,
2785+
) -> None:
2786+
"""It should get the grip point of a LabwareDefinition3 labware at the specified location."""
2787+
decoy.when(
2788+
mock_labware_view.get_grip_height_from_labware_bottom(_MOCK_LABWARE_DEFINITION3)
2789+
).then_return(100)
2790+
2791+
decoy.when(
2792+
mock_addressable_area_view.get_addressable_area_center(DeckSlotName.SLOT_1.id)
2793+
).then_return(Point(x=101, y=102, z=103))
2794+
2795+
decoy.when(
2796+
mock_addressable_area_view.get_addressable_area(DeckSlotName.SLOT_1.id)
2797+
).then_return(MOCK_ADDRESSABLE_AREA)
2798+
2799+
expected_lw_origin_to_parent = (
2800+
Point(
2801+
0,
2802+
MOCK_ADDRESSABLE_AREA.bounding_box.y,
2803+
MOCK_ADDRESSABLE_AREA.bounding_box.z,
2804+
)
2805+
* -1
2806+
)
2807+
2808+
labware_center = subject.get_labware_grip_point(
2809+
labware_definition=_MOCK_LABWARE_DEFINITION3,
2810+
location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1),
2811+
)
2812+
2813+
assert labware_center == Point(
2814+
101.0 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.x + expected_lw_origin_to_parent.x,
2815+
102.0 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.y + expected_lw_origin_to_parent.y,
2816+
203 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.z + expected_lw_origin_to_parent.z,
27632817
)
27642818

27652819

@@ -2790,15 +2844,31 @@ def test_get_labware_grip_point_on_labware(
27902844
mock_addressable_area_view.get_addressable_area_center(DeckSlotName.SLOT_4.id)
27912845
).then_return(Point(x=5, y=9, z=10))
27922846

2847+
decoy.when(
2848+
mock_addressable_area_view.get_addressable_area(DeckSlotName.SLOT_4.id)
2849+
).then_return(MOCK_ADDRESSABLE_AREA)
2850+
2851+
expected_lw_origin_to_parent = (
2852+
Point(
2853+
0,
2854+
MOCK_ADDRESSABLE_AREA.bounding_box.y,
2855+
MOCK_ADDRESSABLE_AREA.bounding_box.z,
2856+
)
2857+
* -1
2858+
)
2859+
27932860
grip_point = subject.get_labware_grip_point(
27942861
labware_definition=sentinel.definition,
27952862
location=OnLabwareLocation(labwareId="below-id"),
27962863
)
27972864

27982865
assert grip_point == Point(
2799-
5.0 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.x * 2, # The labware and adapter
2800-
9.0 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.y * 2,
2801-
110.0 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.z * 2,
2866+
5.0 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.x * 2 + expected_lw_origin_to_parent.x,
2867+
9.0 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.y * 2 + expected_lw_origin_to_parent.y,
2868+
10.0
2869+
+ 100
2870+
+ _PARENT_ORIGIN_TO_LABWARE_ORIGIN.z * 2
2871+
+ expected_lw_origin_to_parent.z,
28022872
)
28032873

28042874

@@ -2868,14 +2938,23 @@ def test_get_labware_grip_point_for_labware_on_module(
28682938
)
28692939
).then_return(Point(x=0, y=0, z=0))
28702940

2941+
expected_lw_origin_to_parent = (
2942+
Point(
2943+
0,
2944+
MOCK_ADDRESSABLE_AREA.bounding_box.y,
2945+
MOCK_ADDRESSABLE_AREA.bounding_box.z,
2946+
)
2947+
* -1
2948+
)
2949+
28712950
result_grip_point = subject.get_labware_grip_point(
28722951
labware_definition=sentinel.labware_definition,
28732952
location=ModuleLocation(moduleId="module-id"),
28742953
)
28752954
assert result_grip_point == Point(
2876-
x=492 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.x,
2877-
y=350 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.y,
2878-
z=838 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.z,
2955+
x=492 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.x + expected_lw_origin_to_parent.x,
2956+
y=350 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.y + expected_lw_origin_to_parent.y,
2957+
z=838 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.z + expected_lw_origin_to_parent.z,
28792958
)
28802959

28812960

@@ -2953,16 +3032,30 @@ def test_get_labware_grip_point_for_labware_stack_on_module(
29533032
"magneticBlockV1C3"
29543033
)
29553034

3035+
expected_lw_origin_to_parent = (
3036+
Point(
3037+
0,
3038+
MOCK_ADDRESSABLE_AREA.bounding_box.y,
3039+
MOCK_ADDRESSABLE_AREA.bounding_box.z,
3040+
)
3041+
* -1
3042+
)
3043+
29563044
result_grip_point = subject.get_labware_grip_point(
29573045
labware_definition=sentinel.labware_definition,
29583046
location=OnLabwareLocation(labwareId="below-id-9"),
29593047
)
29603048

29613049
assert result_grip_point == Point(
29623050
x=492.0
2963-
+ _PARENT_ORIGIN_TO_LABWARE_ORIGIN.x * 2, # The labware and module beneath it.
2964-
y=350.0 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.y * 2,
2965-
z=838.0 + _PARENT_ORIGIN_TO_LABWARE_ORIGIN.z * 2,
3051+
+ _PARENT_ORIGIN_TO_LABWARE_ORIGIN.x * 2 # The labware and module beneath it.
3052+
+ expected_lw_origin_to_parent.x,
3053+
y=350.0
3054+
+ _PARENT_ORIGIN_TO_LABWARE_ORIGIN.y * 2
3055+
+ expected_lw_origin_to_parent.y,
3056+
z=838.0
3057+
+ _PARENT_ORIGIN_TO_LABWARE_ORIGIN.z * 2
3058+
+ expected_lw_origin_to_parent.z,
29663059
)
29673060

29683061

0 commit comments

Comments
 (0)