Skip to content

Commit 35edf78

Browse files
authored
feat(gripper): specify whether or not a gripper move should stay engaged after an error (#13214)
* update hardware message to add stay engaged param * stay engaged when grip * use stay engaged in hardware controller
1 parent e8a656b commit 35edf78

File tree

13 files changed

+34
-8
lines changed

13 files changed

+34
-8
lines changed

api/src/opentrons/hardware_control/backends/ot3controller.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,8 +660,11 @@ async def gripper_grip_jaw(
660660
self,
661661
duty_cycle: float,
662662
stop_condition: MoveStopCondition = MoveStopCondition.none,
663+
stay_engaged: bool = True,
663664
) -> None:
664-
move_group = create_gripper_jaw_grip_group(duty_cycle, stop_condition)
665+
move_group = create_gripper_jaw_grip_group(
666+
duty_cycle, stop_condition, stay_engaged
667+
)
665668
runner = MoveGroupRunner(move_groups=[move_group])
666669
positions = await runner.run(can_messenger=self._messenger)
667670
self._handle_motor_status_response(positions)

api/src/opentrons/hardware_control/backends/ot3simulator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,9 +353,10 @@ async def gripper_grip_jaw(
353353
self,
354354
duty_cycle: float,
355355
stop_condition: MoveStopCondition = MoveStopCondition.none,
356+
stay_engaged: bool = True,
356357
) -> None:
357358
"""Move gripper inward."""
358-
_ = create_gripper_jaw_grip_group(duty_cycle, stop_condition)
359+
_ = create_gripper_jaw_grip_group(duty_cycle, stop_condition, stay_engaged)
359360

360361
@ensure_yield
361362
async def gripper_home_jaw(self, duty_cycle: float) -> None:

api/src/opentrons/hardware_control/backends/ot3utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,12 +428,14 @@ def create_gear_motor_home_group(
428428
def create_gripper_jaw_grip_group(
429429
duty_cycle: float,
430430
stop_condition: MoveStopCondition = MoveStopCondition.none,
431+
stay_engaged: bool = True,
431432
) -> MoveGroup:
432433
step = create_gripper_jaw_step(
433434
duration=np.float64(GRIPPER_JAW_GRIP_TIME),
434435
duty_cycle=np.float32(round(duty_cycle)),
435436
stop_condition=stop_condition,
436437
move_type=MoveType.grip,
438+
stay_engaged=stay_engaged,
437439
)
438440
move_group: MoveGroup = [step]
439441
return move_group

api/src/opentrons/hardware_control/ot3api.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,10 +1395,12 @@ async def update_config(self, **kwargs: Any) -> None:
13951395
self._config = replace(self._config, **kwargs)
13961396

13971397
@ExecutionManagerProvider.wait_for_running
1398-
async def _grip(self, duty_cycle: float) -> None:
1398+
async def _grip(self, duty_cycle: float, stay_engaged: bool = True) -> None:
13991399
"""Move the gripper jaw inward to close."""
14001400
try:
1401-
await self._backend.gripper_grip_jaw(duty_cycle=duty_cycle)
1401+
await self._backend.gripper_grip_jaw(
1402+
duty_cycle=duty_cycle, stay_engaged=stay_engaged
1403+
)
14021404
await self._cache_encoder_position()
14031405
except Exception:
14041406
self._log.exception(
@@ -1431,12 +1433,14 @@ async def _hold_jaw_width(self, jaw_width_mm: float) -> None:
14311433
self._log.exception("Gripper set width failed")
14321434
raise
14331435

1434-
async def grip(self, force_newtons: Optional[float] = None) -> None:
1436+
async def grip(
1437+
self, force_newtons: Optional[float] = None, stay_engaged: bool = True
1438+
) -> None:
14351439
self._gripper_handler.check_ready_for_jaw_move()
14361440
dc = self._gripper_handler.get_duty_cycle_by_grip_force(
14371441
force_newtons or self._gripper_handler.get_gripper().default_grip_force
14381442
)
1439-
await self._grip(duty_cycle=dc)
1443+
await self._grip(duty_cycle=dc, stay_engaged=stay_engaged)
14401444
self._gripper_handler.set_jaw_state(GripperJawState.GRIPPING)
14411445

14421446
async def ungrip(self, force_newtons: Optional[float] = None) -> None:

api/src/opentrons/protocol_engine/execution/labware_movement.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ async def move_labware_with_gripper(
141141

142142
# Keep the gripper in idly gripped position to avoid colliding with
143143
# things like the thermocycler latches
144-
await ot3api.grip(force_newtons=IDLE_STATE_GRIP_FORCE)
144+
await ot3api.grip(force_newtons=IDLE_STATE_GRIP_FORCE, stay_engaged=False)
145145

146146
async def ensure_movement_not_obstructed_by_module(
147147
self, labware_id: str, new_location: LabwareLocation

api/tests/opentrons/hardware_control/test_ot3_api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,7 @@ async def test_gripper_action_works_with_gripper(
966966
await ot3_hardware.grip(5.0)
967967
mock_grip.assert_called_once_with(
968968
gc.duty_cycle_by_force(5.0, gripper_config.grip_force_profile),
969+
stay_engaged=True,
969970
)
970971

971972
await ot3_hardware.ungrip()

api/tests/opentrons/protocol_engine/execution/test_labware_movement_handler.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,9 @@ async def test_move_labware_with_gripper(
258258
await ot3_hardware_api.move_to(
259259
mount=gripper, abs_position=expected_waypoints[5]
260260
),
261-
await ot3_hardware_api.grip(force_newtons=IDLE_STATE_GRIP_FORCE),
261+
await ot3_hardware_api.grip(
262+
force_newtons=IDLE_STATE_GRIP_FORCE, stay_engaged=False
263+
),
262264
)
263265

264266

hardware/opentrons_hardware/firmware_bindings/messages/payloads.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ class GripperMoveRequestPayload(AddToMoveGroupRequestPayload):
526526

527527
duty_cycle: utils.UInt32Field
528528
encoder_position_um: utils.Int32Field
529+
stay_engaged: utils.UInt8Field
529530

530531

531532
@dataclass(eq=False)

hardware/opentrons_hardware/hardware_control/gripper_settings.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ async def grip(
128128
seq_id: int,
129129
duration_sec: float,
130130
duty_cycle: int,
131+
stay_engaged: bool = False,
131132
) -> None:
132133
"""Start grip motion."""
133134
await can_messenger.send(
@@ -141,6 +142,7 @@ async def grip(
141142
),
142143
duty_cycle=UInt32Field(duty_cycle),
143144
encoder_position_um=Int32Field(0),
145+
stay_engaged=UInt8Field(int(stay_engaged)),
144146
)
145147
),
146148
)
@@ -162,6 +164,7 @@ async def home(
162164
duration=UInt32Field(0),
163165
duty_cycle=UInt32Field(duty_cycle),
164166
encoder_position_um=Int32Field(0),
167+
stay_engaged=UInt8Field(0),
165168
)
166169
),
167170
)
@@ -173,6 +176,7 @@ async def move(
173176
seq_id: int,
174177
duty_cycle: int,
175178
encoder_position_um: int,
179+
stay_engaged: bool = False,
176180
) -> None:
177181
"""Start linear motion."""
178182
await can_messenger.send(
@@ -184,6 +188,7 @@ async def move(
184188
duration=UInt32Field(0),
185189
duty_cycle=UInt32Field(duty_cycle),
186190
encoder_position_um=Int32Field(encoder_position_um),
191+
stay_engaged=UInt8Field(int(stay_engaged)),
187192
)
188193
),
189194
)

hardware/opentrons_hardware/hardware_control/motion.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class MoveGroupSingleGripperStep:
7171
pwm_duty_cycle: np.float32
7272
encoder_position_um: np.int32
7373
pwm_frequency: np.float32 = np.float32(320000)
74+
stay_engaged: bool = False
7475
stop_condition: MoveStopCondition = MoveStopCondition.gripper_force
7576
move_type: MoveType = MoveType.grip
7677

@@ -214,6 +215,7 @@ def create_gripper_jaw_step(
214215
duty_cycle: np.float32,
215216
encoder_position_um: np.int32 = np.int32(0),
216217
frequency: np.float32 = np.float32(320000),
218+
stay_engaged: bool = False,
217219
stop_condition: MoveStopCondition = MoveStopCondition.gripper_force,
218220
move_type: MoveType = MoveType.grip,
219221
) -> MoveGroupStep:
@@ -223,6 +225,7 @@ def create_gripper_jaw_step(
223225
duration_sec=duration,
224226
pwm_frequency=frequency,
225227
pwm_duty_cycle=duty_cycle,
228+
stay_engaged=stay_engaged,
226229
stop_condition=stop_condition,
227230
move_type=move_type,
228231
encoder_position_um=encoder_position_um,

0 commit comments

Comments
 (0)