Skip to content

Commit

Permalink
feat(engine): use grip force and height from definitions in pe (#13233)
Browse files Browse the repository at this point in the history
* fetch grip height and force from definitions and use for labware movement in PE

* updated test

* updated force values, allow force and height of zero

* updated tests with changed force values
  • Loading branch information
sanni-t authored Aug 4, 2023
1 parent 5cfb86d commit a5303d1
Show file tree
Hide file tree
Showing 18 changed files with 137 additions and 51 deletions.
12 changes: 5 additions & 7 deletions api/src/opentrons/protocol_engine/execution/labware_movement.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
from __future__ import annotations

from typing import Optional, TYPE_CHECKING
from opentrons_shared_data.gripper.constants import (
LABWARE_GRIP_FORCE,
IDLE_STATE_GRIP_FORCE,
)
from opentrons_shared_data.gripper.constants import IDLE_STATE_GRIP_FORCE

from opentrons.hardware_control import HardwareControlAPI
from opentrons.hardware_control.types import OT3Mount, Axis
Expand Down Expand Up @@ -117,10 +114,10 @@ async def move_labware_with_gripper(
additional_offset_vector=user_offset_data,
)
)
from_labware_center = self._state_store.geometry.get_labware_center(
from_labware_center = self._state_store.geometry.get_labware_grip_point(
labware_id=labware_id, location=current_location
)
to_labware_center = self._state_store.geometry.get_labware_center(
to_labware_center = self._state_store.geometry.get_labware_grip_point(
labware_id=labware_id, location=new_location
)
movement_waypoints = get_gripper_labware_movement_waypoints(
Expand All @@ -129,12 +126,13 @@ async def move_labware_with_gripper(
gripper_home_z=gripper_homed_position.z,
offset_data=final_offsets,
)
labware_grip_force = self._state_store.labware.get_grip_force(labware_id)

for waypoint_data in movement_waypoints:
if waypoint_data.jaw_open:
await ot3api.ungrip()
else:
await ot3api.grip(force_newtons=LABWARE_GRIP_FORCE)
await ot3api.grip(force_newtons=labware_grip_force)
await ot3api.move_to(
mount=gripper_mount, abs_position=waypoint_data.position
)
Expand Down
19 changes: 13 additions & 6 deletions api/src/opentrons/protocol_engine/state/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,17 +416,24 @@ def ensure_location_not_occupied(
self._modules.raise_if_module_in_location(location)
return location

def get_labware_center(
def get_labware_grip_point(
self,
labware_id: str,
location: Union[DeckSlotLocation, ModuleLocation, OnLabwareLocation],
) -> Point:
"""Get the center point of the labware as placed on the given location.
"""Get the grip point of the labware as placed on the given location.
Returns the absolute position of the labware as if it were placed on the
specified location. Labware offset not included.
Returns the absolute position of the labware's gripping point as if
it were placed on the specified location. Labware offset (LPC offset) not included.
Grip point is the location where critical point of the gripper should move to
in order to pick/drop the given labware in the specified location.
It is calculated as the xy center of the slot with z as the point indicated by
z-position of labware bottom + grip height from labware bottom.
"""
labware_dimensions = self._labware.get_dimensions(labware_id)
grip_height_from_labware_bottom = (
self._labware.get_grip_height_from_labware_bottom(labware_id)
)
offset = LabwareOffsetVector(x=0, y=0, z=0)
location_slot: DeckSlotName

Expand All @@ -452,7 +459,7 @@ def get_labware_center(
return Point(
slot_center.x + offset.x,
slot_center.y + offset.y,
slot_center.z + offset.z + labware_dimensions.z / 2,
slot_center.z + offset.z + grip_height_from_labware_bottom,
)

def get_extra_waypoints(
Expand Down
17 changes: 17 additions & 0 deletions api/src/opentrons/protocol_engine/state/labware.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
)

from opentrons_shared_data.deck.dev_types import DeckDefinitionV3, SlotDefV3
from opentrons_shared_data.gripper.constants import LABWARE_GRIP_FORCE
from opentrons_shared_data.labware.labware_definition import LabwareRole
from opentrons_shared_data.pipette.dev_types import LabwareUri

Expand Down Expand Up @@ -763,3 +764,19 @@ def get_labware_gripper_offsets(
if parsed_offsets
else None
)

def get_grip_force(self, labware_id: str) -> float:
"""Get the recommended grip force for gripping labware using gripper."""
recommended_force = self.get_definition(labware_id).gripForce
return (
recommended_force if recommended_force is not None else LABWARE_GRIP_FORCE
)

def get_grip_height_from_labware_bottom(self, labware_id: str) -> float:
"""Get the recommended grip height from labware bottom, if present."""
recommended_height = self.get_definition(labware_id).gripHeightFromLabwareBottom
return (
recommended_height
if recommended_height is not None
else self.get_dimensions(labware_id).z / 2
)
10 changes: 9 additions & 1 deletion api/tests/opentrons/protocol_engine/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,15 @@ def ot3_fixed_trash_def() -> LabwareDefinition:
def well_plate_def() -> LabwareDefinition:
"""Get the definition of a 96 well plate."""
return LabwareDefinition.parse_obj(
load_definition("corning_96_wellplate_360ul_flat", 1)
load_definition("corning_96_wellplate_360ul_flat", 2)
)


@pytest.fixture(scope="session")
def flex_50uL_tiprack() -> LabwareDefinition:
"""Get the definition of a Flex 50uL tiprack."""
return LabwareDefinition.parse_obj(
load_definition("opentrons_flex_96_filtertiprack_50ul", 1)
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@
from typing import TYPE_CHECKING, Union

from opentrons.protocol_engine.execution import EquipmentHandler, MovementHandler
from opentrons_shared_data.gripper.constants import (
LABWARE_GRIP_FORCE,
IDLE_STATE_GRIP_FORCE,
)
from opentrons_shared_data.gripper.constants import IDLE_STATE_GRIP_FORCE
from opentrons.hardware_control import HardwareControlAPI
from opentrons.types import DeckSlotName, Point

Expand Down Expand Up @@ -184,17 +181,19 @@ async def test_move_labware_with_gripper(
).then_return(final_offset_data)

decoy.when(
state_store.geometry.get_labware_center(
state_store.geometry.get_labware_grip_point(
labware_id="my-teleporting-labware", location=from_location
)
).then_return(Point(101, 102, 119.5))

decoy.when(
state_store.geometry.get_labware_center(
state_store.geometry.get_labware_grip_point(
labware_id="my-teleporting-labware", location=to_location
)
).then_return(Point(201, 202, 219.5))

decoy.when(
state_store.labware.get_grip_force("my-teleporting-labware")
).then_return(100)
mock_tc_context_manager = decoy.mock()
decoy.when(
thermocycler_plate_lifter.lift_plate_for_labware_movement(
Expand Down Expand Up @@ -234,23 +233,23 @@ async def test_move_labware_with_gripper(
decoy.verify(
await ot3_hardware_api.home(axes=[Axis.Z_L, Axis.Z_R, Axis.Z_G]),
await mock_tc_context_manager.__aenter__(),
await ot3_hardware_api.grip(force_newtons=LABWARE_GRIP_FORCE),
await ot3_hardware_api.grip(force_newtons=100),
await ot3_hardware_api.move_to(
mount=gripper, abs_position=expected_waypoints[0]
),
await ot3_hardware_api.ungrip(),
await ot3_hardware_api.move_to(
mount=gripper, abs_position=expected_waypoints[1]
),
await ot3_hardware_api.grip(force_newtons=LABWARE_GRIP_FORCE),
await ot3_hardware_api.grip(force_newtons=100),
await ot3_hardware_api.move_to(
mount=gripper, abs_position=expected_waypoints[2]
),
await ot3_hardware_api.grip(force_newtons=LABWARE_GRIP_FORCE),
await ot3_hardware_api.grip(force_newtons=100),
await ot3_hardware_api.move_to(
mount=gripper, abs_position=expected_waypoints[3]
),
await ot3_hardware_api.grip(force_newtons=LABWARE_GRIP_FORCE),
await ot3_hardware_api.grip(force_newtons=100),
await ot3_hardware_api.move_to(
mount=gripper, abs_position=expected_waypoints[4]
),
Expand Down
30 changes: 15 additions & 15 deletions api/tests/opentrons/protocol_engine/state/test_geometry_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -1078,11 +1078,11 @@ def test_ensure_location_not_occupied_raises(
@pytest.mark.parametrize(
argnames=["location", "expected_center_point"],
argvalues=[
(DeckSlotLocation(slotName=DeckSlotName.SLOT_1), Point(101.0, 102.0, 119.5)),
(ModuleLocation(moduleId="module-id"), Point(111.0, 122.0, 149.5)),
(DeckSlotLocation(slotName=DeckSlotName.SLOT_1), Point(101.0, 102.0, 203)),
(ModuleLocation(moduleId="module-id"), Point(111.0, 122.0, 233)),
],
)
def test_get_labware_center(
def test_get_labware_grip_point(
decoy: Decoy,
labware_view: LabwareView,
module_view: ModuleView,
Expand All @@ -1091,10 +1091,10 @@ def test_get_labware_center(
location: Union[DeckSlotLocation, ModuleLocation],
expected_center_point: Point,
) -> None:
"""It should get the center point of the labware at the specified location."""
decoy.when(labware_view.get_dimensions(labware_id="labware-id")).then_return(
Dimensions(x=11, y=22, z=33)
)
"""It should get the grip point of the labware at the specified location."""
decoy.when(
labware_view.get_grip_height_from_labware_bottom("labware-id")
).then_return(100)

if isinstance(location, ModuleLocation):
decoy.when(labware_view.get_deck_definition()).then_return(
Expand All @@ -1113,21 +1113,21 @@ def test_get_labware_center(
decoy.when(labware_view.get_slot_center_position(DeckSlotName.SLOT_1)).then_return(
Point(x=101, y=102, z=103)
)
labware_center = subject.get_labware_center(
labware_center = subject.get_labware_grip_point(
labware_id="labware-id", location=location
)

assert labware_center == expected_center_point


def test_get_labware_center_on_labware(
def test_get_labware_grip_point_on_labware(
decoy: Decoy,
labware_view: LabwareView,
module_view: ModuleView,
ot2_standard_deck_def: DeckDefinitionV3,
subject: GeometryView,
) -> None:
"""It should get the center point of a labware on another labware."""
"""It should get the grip point of a labware on another labware."""
decoy.when(labware_view.get(labware_id="labware-id")).then_return(
LoadedLabware(
id="labware-id",
Expand All @@ -1145,12 +1145,12 @@ def test_get_labware_center_on_labware(
)
)

decoy.when(labware_view.get_dimensions("labware-id")).then_return(
Dimensions(x=500, y=5001, z=10)
)
decoy.when(labware_view.get_dimensions("below-id")).then_return(
Dimensions(x=1000, y=1001, z=11)
)
decoy.when(
labware_view.get_grip_height_from_labware_bottom("labware-id")
).then_return(100)
decoy.when(
labware_view.get_labware_overlap_offsets("labware-id", "below-name")
).then_return(OverlapOffset(x=0, y=1, z=6))
Expand All @@ -1159,11 +1159,11 @@ def test_get_labware_center_on_labware(
Point(x=5, y=9, z=10)
)

labware_center = subject.get_labware_center(
grip_point = subject.get_labware_grip_point(
labware_id="labware-id", location=OnLabwareLocation(labwareId="below-id")
)

assert labware_center == Point(5, 10, 20)
assert grip_point == Point(5, 10, 115.0)


@pytest.mark.parametrize(
Expand Down
47 changes: 47 additions & 0 deletions api/tests/opentrons/protocol_engine/state/test_labware_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@
displayName="Fancy Plate Name",
)

flex_tiprack = LoadedLabware(
id="flex-tiprack-id",
loadName="flex-tiprack-load-name",
location=DeckSlotLocation(slotName=DeckSlotName.SLOT_1),
definitionUri="some-flex-tiprack-uri",
offsetId=None,
displayName="Flex Tiprack Name",
)

reservoir = LoadedLabware(
id="reservoir-id",
loadName="reservoir-load-name",
Expand Down Expand Up @@ -1315,3 +1324,41 @@ def test_get_labware_gripper_offsets(
pickUpOffset=LabwareOffsetVector(x=0, y=0, z=0),
dropOffset=LabwareOffsetVector(x=2, y=0, z=0),
)


def test_get_grip_force(
flex_50uL_tiprack: LabwareDefinition,
reservoir_def: LabwareDefinition,
) -> None:
"""It should get the grip force, if present, from labware definition or return default."""
subject = get_labware_view(
labware_by_id={"flex-tiprack-id": flex_tiprack, "reservoir-id": reservoir},
definitions_by_uri={
"some-flex-tiprack-uri": flex_50uL_tiprack,
"some-reservoir-uri": reservoir_def,
},
)

assert subject.get_grip_force("flex-tiprack-id") == 16 # from definition
assert subject.get_grip_force("reservoir-id") == 15 # default


def test_get_grip_height_from_labware_bottom(
well_plate_def: LabwareDefinition,
reservoir_def: LabwareDefinition,
) -> None:
"""It should get the grip height, if present, from labware definition or return default."""
subject = get_labware_view(
labware_by_id={"plate-id": plate, "reservoir-id": reservoir},
definitions_by_uri={
"some-plate-uri": well_plate_def,
"some-reservoir-uri": reservoir_def,
},
)

assert (
subject.get_grip_height_from_labware_bottom("plate-id") == 12.2
) # from definition
assert (
subject.get_grip_height_from_labware_bottom("reservoir-id") == 15.7
) # default
2 changes: 1 addition & 1 deletion protocol-designer/fixtures/protocol/7/doItAllV7.json
Original file line number Diff line number Diff line change
Expand Up @@ -1390,7 +1390,7 @@
"yDimension": 85.48,
"zDimension": 15.7
},
"gripForce": 3,
"gripForce": 15,
"gripHeightFromLabwareBottom": 10.65,
"wells": {
"A1": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
"x": 0,
"y": 0,
"z": 10.95
},
"opentrons_96_well_aluminum_block": {
"x": 0,
"y": 0,
"z": 11.91
}
},
"stackingOffsetWithModule": {
Expand All @@ -45,7 +50,7 @@
"z": 3.54
}
},
"gripForce": 6,
"gripForce": 15,
"gripHeightFromLabwareBottom": 10,
"ordering": [
["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@
"yDimension": 85.48,
"zDimension": 10.4
},
"gripForce": 6,
"gripForce": 15,
"gripHeightFromLabwareBottom": 9.3,
"wells": {
"A1": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"isMagneticModuleCompatible": true,
"magneticModuleEngageHeight": 18
},
"gripForce": 6,
"gripForce": 15,
"gripHeightFromLabwareBottom": 10.14,
"wells": {
"H1": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@
"isMagneticModuleCompatible": false,
"loadName": "corning_384_wellplate_112ul_flat"
},
"gripForce": 10,
"gripForce": 15,
"gripHeightFromLabwareBottom": 12.4,
"wells": {
"P1": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"y": 0,
"z": 0
},
"gripForce": 10,
"gripForce": 15,
"gripHeightFromLabwareBottom": 12.2,
"wells": {
"H1": {
Expand Down
Loading

0 comments on commit a5303d1

Please sign in to comment.