Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): Add RobotContext movement commands #16403

Merged
merged 46 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
73fd4d7
feat(hardware_control): allow Q moves to be processed by hw._move
Laura-Danielle Sep 2, 2024
e1ab989
feat: add movement commands to robot_context
Laura-Danielle Sep 9, 2024
535a0d5
feat: add robot core
Laura-Danielle Sep 9, 2024
966457f
feat: add protocol engine commands for robot movement commands
Laura-Danielle Sep 9, 2024
d1cf948
feat: add new axis types
Laura-Danielle Sep 9, 2024
d577abf
feat: Expose additional movement options in gantry mover and movement
Laura-Danielle Sep 17, 2024
c8bab64
feat: add robot core
Laura-Danielle Sep 17, 2024
4db3a3b
feat: add move_to, move_axes and move_axes rel functionality as well …
Laura-Danielle Sep 17, 2024
86dcd5b
feat: add robot MoveTo engine command
Laura-Danielle Sep 17, 2024
c709ade
feat: add robot moveAxesRelative engine command
Laura-Danielle Sep 17, 2024
230767d
feat: add robot moveAxesTo engine command
Laura-Danielle Sep 17, 2024
de7688c
command union stuff
Laura-Danielle Sep 17, 2024
edbca79
add validation and shared types
Laura-Danielle Sep 17, 2024
720f0a2
adding tests
Laura-Danielle Sep 17, 2024
9d87b55
some lint fixes for typing conflicts in robot_context
CaseyBatten Sep 18, 2024
6acdb01
cleanup and location error case
CaseyBatten Sep 18, 2024
a2e2b26
add logging and fix Q axis
Laura-Danielle Oct 8, 2024
7e70f4b
test: relative moves are moving in absolute
Laura-Danielle Oct 8, 2024
9e87c76
refactor: make critical point optional
Laura-Danielle Oct 8, 2024
3f15ba3
missed if statement block
Laura-Danielle Oct 8, 2024
9d5dcf7
use the right MotorAxis key
Laura-Danielle Oct 8, 2024
0e7ef61
fix: check that the axis is in the axis map
Laura-Danielle Oct 8, 2024
7f98b78
fix: correctly handle Q motor moves in the function and add a test
Laura-Danielle Oct 17, 2024
8a7338d
pass the right locations from gantry mover to the hardware controller…
Laura-Danielle Oct 17, 2024
90750c1
adding protocol engine command tests
Laura-Danielle Oct 17, 2024
0499118
formatting
Laura-Danielle Oct 17, 2024
b8cf9bb
bump api version to 2.21
Laura-Danielle Oct 17, 2024
6e92b5b
add logs for motion utilities
Laura-Danielle Oct 17, 2024
d1e716f
wrapper hardware offsets as point
Laura-Danielle Oct 17, 2024
e5d468d
fix move_axes_to move, add more logs for move_axes_relative
Laura-Danielle Oct 21, 2024
b8911f9
make machine to deck conversion func public hw API
Laura-Danielle Oct 21, 2024
aecb1c3
more linter fixes
Laura-Danielle Oct 21, 2024
e4ee72e
fix relative movements
Laura-Danielle Oct 22, 2024
c93de27
handle robot context loading skipping for legacy protocols
Laura-Danielle Oct 22, 2024
5ceb32d
additional attempt at fixing move rel
Laura-Danielle Oct 23, 2024
028a49a
fix tests, lint and formatting
Laura-Danielle Nov 1, 2024
1336292
fixups from rebase
Laura-Danielle Nov 1, 2024
7e9440e
bump schemma to version 11
Laura-Danielle Nov 1, 2024
b205a87
fix move group runner changes
Laura-Danielle Nov 1, 2024
4fae9a1
fix failing linters and formatting
Laura-Danielle Nov 2, 2024
6113fe9
fix more linter errors
Laura-Danielle Nov 3, 2024
7c2e804
fix linter errors
Laura-Danielle Nov 3, 2024
25f5916
refresh command schema
Laura-Danielle Nov 5, 2024
2b5dcd2
add additional commands to schema 11
Laura-Danielle Nov 6, 2024
6eeede9
fixup from last rebase
Laura-Danielle Nov 8, 2024
1fb5978
more rebase fixes
Laura-Danielle Nov 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 18 additions & 20 deletions api/src/opentrons/hardware_control/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,16 @@ def _update_door_state(self, door_state: DoorState) -> None:
def _reset_last_mount(self) -> None:
self._last_moved_mount = None

def get_deck_from_machine(
self, machine_pos: Dict[Axis, float]
) -> Dict[Axis, float]:
return deck_from_machine(
machine_pos=machine_pos,
attitude=self._robot_calibration.deck_calibration.attitude,
offset=top_types.Point(0, 0, 0),
robot_type=cast(RobotType, "OT-2 Standard"),
)

@classmethod
async def build_hardware_controller( # noqa: C901
cls,
Expand Down Expand Up @@ -657,11 +667,8 @@ async def home(self, axes: Optional[List[Axis]] = None) -> None:
async with self._motion_lock:
if smoothie_gantry:
smoothie_pos.update(await self._backend.home(smoothie_gantry))
self._current_position = deck_from_machine(
machine_pos=self._axis_map_from_string_map(smoothie_pos),
attitude=self._robot_calibration.deck_calibration.attitude,
offset=top_types.Point(0, 0, 0),
robot_type=cast(RobotType, "OT-2 Standard"),
self._current_position = self.get_deck_from_machine(
self._axis_map_from_string_map(smoothie_pos)
)
for plunger in plungers:
await self._do_plunger_home(axis=plunger, acquire_lock=False)
Expand Down Expand Up @@ -703,11 +710,8 @@ async def current_position(
async with self._motion_lock:
if refresh:
smoothie_pos = await self._backend.update_position()
self._current_position = deck_from_machine(
machine_pos=self._axis_map_from_string_map(smoothie_pos),
attitude=self._robot_calibration.deck_calibration.attitude,
offset=top_types.Point(0, 0, 0),
robot_type=cast(RobotType, "OT-2 Standard"),
self._current_position = self.get_deck_from_machine(
self._axis_map_from_string_map(smoothie_pos)
)
if mount == top_types.Mount.RIGHT:
offset = top_types.Point(0, 0, 0)
Expand Down Expand Up @@ -938,11 +942,8 @@ async def retract_axis(self, axis: Axis, margin: float = 10) -> None:

async with self._motion_lock:
smoothie_pos = await self._fast_home(smoothie_ax, margin)
self._current_position = deck_from_machine(
machine_pos=self._axis_map_from_string_map(smoothie_pos),
attitude=self._robot_calibration.deck_calibration.attitude,
offset=top_types.Point(0, 0, 0),
robot_type=cast(RobotType, "OT-2 Standard"),
self._current_position = self.get_deck_from_machine(
self._axis_map_from_string_map(smoothie_pos)
)

# Gantry/frame (i.e. not pipette) config API
Expand Down Expand Up @@ -1256,11 +1257,8 @@ async def tip_drop_moves(
axes=[ot2_axis_to_string(ax) for ax in move.home_axes],
margin=move.home_after_safety_margin,
)
self._current_position = deck_from_machine(
machine_pos=self._axis_map_from_string_map(smoothie_pos),
attitude=self._robot_calibration.deck_calibration.attitude,
offset=top_types.Point(0, 0, 0),
robot_type=cast(RobotType, "OT-2 Standard"),
self._current_position = self.get_deck_from_machine(
self._axis_map_from_string_map(smoothie_pos)
)

for shake in spec.shake_moves:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,6 @@ async def home_tip_motors(
def _build_tip_action_group(
self, origin: float, targets: List[Tuple[float, float]]
) -> MoveGroup:

move_targets = [
MoveTarget.build({Axis.Q: target_pos}, speed)
for target_pos, speed in targets
Expand Down
68 changes: 68 additions & 0 deletions api/src/opentrons/hardware_control/motion_utilities.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Utilities for calculating motion correctly."""
from logging import getLogger

from functools import lru_cache
from typing import Callable, Dict, Union, Optional, cast
from collections import OrderedDict
Expand All @@ -11,6 +13,7 @@

from .types import Axis, OT3Mount

log = getLogger(__name__)

# TODO: The offset_for_mount function should be defined with an overload
# set, as with other functions in this module. Unfortunately, mypy < 0.920
Expand All @@ -36,6 +39,19 @@
# ) -> Point:
# ...

EMPTY_ORDERED_DICT = OrderedDict(
(
(Axis.X, 0.0),
(Axis.Y, 0.0),
(Axis.Z_L, 0.0),
(Axis.Z_R, 0.0),
(Axis.Z_G, 0.0),
(Axis.P_L, 0.0),
(Axis.P_R, 0.0),
(Axis.Q, 0.0),
)
)


@lru_cache(4)
def offset_for_mount(
Expand Down Expand Up @@ -68,6 +84,7 @@ def target_position_from_absolute(
)
primary_cp = get_critical_point(mount)
primary_z = Axis.by_mount(mount)

target_position = OrderedDict(
(
(Axis.X, abs_position.x - offset.x - primary_cp.x),
Expand Down Expand Up @@ -97,6 +114,57 @@ def target_position_from_relative(
return target_position


def target_axis_map_from_absolute(
primary_mount: Union[OT3Mount, Mount],
axis_map: Dict[Axis, float],
get_critical_point: Callable[[Union[Mount, OT3Mount]], Point],
left_mount_offset: Point,
right_mount_offset: Point,
gripper_mount_offset: Optional[Point] = None,
) -> "OrderedDict[Axis, float]":
"""Create an absolute target position for all specified machine axes."""
keys_for_target_position = list(axis_map.keys())

offset = offset_for_mount(
primary_mount, left_mount_offset, right_mount_offset, gripper_mount_offset
)
primary_cp = get_critical_point(primary_mount)
primary_z = Axis.by_mount(primary_mount)
target_position = OrderedDict()

if Axis.X in keys_for_target_position:
target_position[Axis.X] = axis_map[Axis.X] - offset.x - primary_cp.x
if Axis.Y in keys_for_target_position:
target_position[Axis.Y] = axis_map[Axis.Y] - offset.y - primary_cp.y
if primary_z in keys_for_target_position:
# Since this function is intended to be used in conjunction with `API.move_axes`
# we must leave out the carriage offset subtraction from the target position as
# `move_axes` already does this calculation.
target_position[primary_z] = axis_map[primary_z] - primary_cp.z

target_position.update(
{ax: val for ax, val in axis_map.items() if ax not in Axis.gantry_axes()}
)
return target_position


def target_axis_map_from_relative(
axis_map: Dict[Axis, float],
current_position: Dict[Axis, float],
) -> "OrderedDict[Axis, float]":
"""Create a target position for all specified machine axes."""
target_position = OrderedDict(
(
(ax, current_position[ax] + axis_map[ax])
for ax in EMPTY_ORDERED_DICT.keys()
if ax in axis_map.keys()
)
)
log.info(f"Current position {current_position} and axis map delta {axis_map}")
log.info(f"Relative move target {target_position}")
return target_position


def target_position_from_plunger(
mount: Union[Mount, OT3Mount],
delta: float,
Expand Down
14 changes: 9 additions & 5 deletions api/src/opentrons/hardware_control/ot3api.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,9 @@ def _update_estop_state(self, event: HardwareEvent) -> "List[Future[None]]":
def _reset_last_mount(self) -> None:
self._last_moved_mount = None

def _deck_from_machine(self, machine_pos: Dict[Axis, float]) -> Dict[Axis, float]:
def get_deck_from_machine(
self, machine_pos: Dict[Axis, float]
) -> Dict[Axis, float]:
return deck_from_machine(
machine_pos=machine_pos,
attitude=self._robot_calibration.deck_calibration.attitude,
Expand Down Expand Up @@ -1020,14 +1022,14 @@ async def _refresh_jaw_state(self) -> None:

async def _cache_current_position(self) -> Dict[Axis, float]:
"""Cache current position from backend and return in absolute deck coords."""
self._current_position = self._deck_from_machine(
self._current_position = self.get_deck_from_machine(
await self._backend.update_position()
)
return self._current_position

async def _cache_encoder_position(self) -> Dict[Axis, float]:
"""Cache encoder position from backend and return in absolute deck coords."""
self._encoder_position = self._deck_from_machine(
self._encoder_position = self.get_deck_from_machine(
await self._backend.update_encoder_position()
)
if self.has_gripper():
Expand Down Expand Up @@ -1227,7 +1229,9 @@ async def move_axes( # noqa: C901
message=f"{axis} is not present", detail={"axis": str(axis)}
)

self._log.info(f"Attempting to move {position} with speed {speed}.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note which coordinates this is in in the log please

if not self._backend.check_encoder_status(list(position.keys())):
self._log.info("Calling home in move_axes")
await self.home()
self._assert_motor_ok(list(position.keys()))

Expand Down Expand Up @@ -2560,7 +2564,7 @@ def get_instrument_max_height(
mount: Union[top_types.Mount, OT3Mount],
critical_point: Optional[CriticalPoint] = None,
) -> float:
carriage_pos = self._deck_from_machine(self._backend.home_position())
carriage_pos = self.get_deck_from_machine(self._backend.home_position())
pos_at_home = self._effector_pos_from_carriage_pos(
OT3Mount.from_mount(mount), carriage_pos, critical_point
)
Expand Down Expand Up @@ -2662,7 +2666,7 @@ async def _liquid_probe_pass(
)
machine_pos = await self._backend.update_position()
machine_pos[Axis.by_mount(mount)] = end_z
deck_end_z = self._deck_from_machine(machine_pos)[Axis.by_mount(mount)]
deck_end_z = self.get_deck_from_machine(machine_pos)[Axis.by_mount(mount)]
offset = offset_for_mount(
mount,
top_types.Point(*self._config.left_mount_offset),
Expand Down
18 changes: 18 additions & 0 deletions api/src/opentrons/hardware_control/protocols/liquid_handler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from typing import Optional
from typing_extensions import Protocol

from opentrons.types import Point
from opentrons.hardware_control.types import CriticalPoint
from .types import MountArgType, CalibrationType, ConfigType

from .instrument_configurer import InstrumentConfigurer
Expand All @@ -16,6 +18,22 @@ class LiquidHandler(
Calibratable[CalibrationType],
Protocol[CalibrationType, MountArgType, ConfigType],
):
def critical_point_for(
self,
mount: MountArgType,
cp_override: Optional[CriticalPoint] = None,
) -> Point:
"""
Determine the current critical point for the specified mount.

:param mount: A robot mount that the instrument is on.
:param cp_override: The critical point override to use.

If no critical point override is specified, the robot defaults to nozzle location `A1` or the mount critical point.
:return: Point.
"""
...

async def update_nozzle_configuration_for_mount(
self,
mount: MountArgType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
class MotionController(Protocol[MountArgType]):
"""Protocol specifying fundamental motion controls."""

def get_deck_from_machine(
self, machine_pos: Dict[Axis, float]
) -> Dict[Axis, float]:
"""Convert machine coordinates to deck coordinates."""
...

async def halt(self, disengage_before_stopping: bool = False) -> None:
"""Immediately stop motion.

Expand Down
2 changes: 2 additions & 0 deletions api/src/opentrons/protocol_api/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
)
from .protocol import AbstractProtocol
from .well import AbstractWellCore
from .robot import AbstractRobot


WellCore = AbstractWellCore
Expand All @@ -26,4 +27,5 @@
HeaterShakerCore = AbstractHeaterShakerCore
MagneticBlockCore = AbstractMagneticBlockCore
AbsorbanceReaderCore = AbstractAbsorbanceReaderCore
RobotCore = AbstractRobot
ProtocolCore = AbstractProtocol[InstrumentCore, LabwareCore, ModuleCore]
11 changes: 10 additions & 1 deletion api/src/opentrons/protocol_api/core/engine/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
from ..labware import LabwareLoadParams
from .labware import LabwareCore
from .instrument import InstrumentCore
from .robot import RobotCore
from .module_core import (
ModuleCore,
TemperatureModuleCore,
Expand All @@ -82,7 +83,9 @@

class ProtocolCore(
AbstractProtocol[
InstrumentCore, LabwareCore, Union[ModuleCore, NonConnectedModuleCore]
InstrumentCore,
LabwareCore,
Union[ModuleCore, NonConnectedModuleCore],
]
):
"""Protocol API core using a ProtocolEngine.
Expand Down Expand Up @@ -531,6 +534,12 @@ def _get_module_core(
load_module_result=load_module_result, model=model
)

def load_robot(self) -> RobotCore:
"""Load a robot core into the RobotContext."""
return RobotCore(
engine_client=self._engine_client, sync_hardware_api=self._sync_hardware
)

def load_instrument(
self,
instrument_name: PipetteNameType,
Expand Down
Loading
Loading