Skip to content

Commit

Permalink
feat(api): add mechanisms to save and load deck calibration transform…
Browse files Browse the repository at this point in the history
… matrix (#12381)

* utilize legacy deck transform save/load mechanisms

* refactored for ot3 deck_attitude methods

* fixed test and refactored

* refactored and added RobotCalibrationProvider

* fixed test

* refactor. Still need to fix broken tests

* fixed tests

* verified apply_machine_transform

* fixes for failing tests

* cleaned up API integration

* removed unnecessary code and attempted OT3RobotCalibrationProvider superclass

* refactored and fixed tests that broke

* reverted pytest fixture to work with updated decoy library

* fixed one naming

* partway through user flow fix

* update to deck calibration user flow and fixed tests

* fixed tests

* changes based off Seth's recent PR review

* fixed test

* fixed linting error

* fixed resetting and loading of deck transform data duringcalibration

* fix

* undo fix

* redo fix

* moved cache clearing

* updated resetting/loading procedure

* attempted fix

* additional part of fix

* fixed lint error

* Fix to OT3RobotCalibrationProvider initialization

* fix for testing

* added addtional info for debugging

* fix to calculate attitude matrix correctly

* get rid of debug data and utilize saved deck calibration data now

* added belt calibration data deletion mechanism

* fix to repl

* fix to repl

* repl fix

* test fix

* test fix

* fixed pipette config test

* fixed linting error

* similar lint fix

* fixed test

* fixed delete_belt_calibration_data
  • Loading branch information
pmoegenburg authored May 22, 2023
1 parent bdb8c66 commit e5e499e
Show file tree
Hide file tree
Showing 37 changed files with 479 additions and 292 deletions.
23 changes: 13 additions & 10 deletions api/src/opentrons/calibration_storage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@
_save_custom_tiprack_definition,
)
from .ot2.pipette_offset import get_all_pipette_offset_calibrations
from .ot3.deck_attitude import (
save_robot_belt_attitude,
get_robot_belt_attitude,
delete_robot_belt_attitude,
)
from .ot2.deck_attitude import (
save_robot_deck_attitude,
get_robot_deck_attitude,
delete_robot_deck_attitude,
)

if config.feature_flags.enable_ot3_hardware_controller():
from .ot3.deck_attitude import (
save_robot_deck_attitude,
get_robot_deck_attitude,
delete_robot_deck_attitude,
)
from .ot3.pipette_offset import (
save_pipette_calibration,
clear_pipette_offset_calibrations,
Expand All @@ -41,11 +46,6 @@
delete_module_offset_file,
)
else:
from .ot2.deck_attitude import (
save_robot_deck_attitude,
get_robot_deck_attitude,
delete_robot_deck_attitude,
)
from .ot2.pipette_offset import (
save_pipette_calibration,
clear_pipette_offset_calibrations,
Expand All @@ -67,6 +67,9 @@
"save_robot_deck_attitude",
"get_robot_deck_attitude",
"delete_robot_deck_attitude",
"save_robot_belt_attitude",
"get_robot_belt_attitude",
"delete_robot_belt_attitude",
# pipette calibration functions
"save_pipette_calibration",
"get_pipette_offset",
Expand Down
2 changes: 1 addition & 1 deletion api/src/opentrons/calibration_storage/ot2/models/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class Config:

class DeckCalibrationModel(BaseModel):
attitude: types.AttitudeMatrix = Field(
..., description="Attitude matrix found from calibration."
..., description="Attitude matrix for deck found from calibration."
)
last_modified: datetime = Field(
default=None, description="The last time this deck was calibrated."
Expand Down
30 changes: 15 additions & 15 deletions api/src/opentrons/calibration_storage/ot3/deck_attitude.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,26 @@
log = logging.getLogger(__name__)


# Delete Deck Calibration
# Delete Belt Calibration


@no_type_check
def delete_robot_deck_attitude() -> None:
def delete_robot_belt_attitude() -> None:
"""
Delete the robot deck attitude calibration.
Delete the robot belt attitude calibration.
"""
gantry_path = (
config.get_opentrons_path("robot_calibration_dir") / "deck_calibration.json"
config.get_opentrons_path("robot_calibration_dir") / "belt_calibration.json"
)

io.delete_file(gantry_path)


# Save Deck Calibration
# Save Belt Calibration


@no_type_check
def save_robot_deck_attitude(
def save_robot_belt_attitude(
transform: local_types.AttitudeMatrix,
pip_id: Optional[str],
source: Optional[local_types.SourceType] = None,
Expand All @@ -51,33 +51,33 @@ def save_robot_deck_attitude(
else:
cal_status_model = v1.CalibrationStatus()

gantry_calibration = v1.DeckCalibrationModel(
gantry_calibration = v1.BeltCalibrationModel(
attitude=transform,
pipetteCalibratedWith=pip_id,
lastModified=utc_now(),
source=source or local_types.SourceType.user,
status=cal_status_model,
)
# convert to schema + validate json conversion
io.save_to_file(robot_dir, "deck_calibration", gantry_calibration)
io.save_to_file(robot_dir, "belt_calibration", gantry_calibration)


# Get Deck Calibration
# Get Belt Calibration


@no_type_check
def get_robot_deck_attitude() -> Optional[v1.DeckCalibrationModel]:
deck_calibration_path = (
config.get_opentrons_path("robot_calibration_dir") / "deck_calibration.json"
def get_robot_belt_attitude() -> Optional[v1.BeltCalibrationModel]:
belt_calibration_path = (
config.get_opentrons_path("robot_calibration_dir") / "belt_calibration.json"
)
try:
return v1.DeckCalibrationModel(**io.read_cal_file(deck_calibration_path))
return v1.BeltCalibrationModel(**io.read_cal_file(belt_calibration_path))
except FileNotFoundError:
log.warning("Deck calibration not found.")
log.warning("Belt calibration not found.")
pass
except (json.JSONDecodeError, ValidationError):
log.warning(
"Deck calibration is malformed. Please factory reset your calibrations."
"Belt calibration is malformed. Please factory reset your calibrations."
)
pass
return None
4 changes: 2 additions & 2 deletions api/src/opentrons/calibration_storage/ot3/models/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ class Config:
json_decoders = {datetime: lambda obj: datetime.fromisoformat(obj)}


class DeckCalibrationModel(BaseModel):
class BeltCalibrationModel(BaseModel):
attitude: types.AttitudeMatrix = Field(
..., description="Attitude matrix found from calibration."
..., description="Attitude matrix for belts found from calibration."
)
lastModified: datetime = Field(
..., description="The last time this deck was calibrated."
Expand Down
9 changes: 7 additions & 2 deletions api/src/opentrons/config/defaults_ot3.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,16 @@

ROBOT_CONFIG_VERSION: Final = 1
DEFAULT_LOG_LEVEL: Final = "INFO"
DEFAULT_DECK_TRANSFORM: Final[OT3Transform] = [
DEFAULT_MACHINE_TRANSFORM: Final[OT3Transform] = [
[-1.0, 0.0, 0.0],
[0.0, -1.0, 0.0],
[0.0, 0.0, -1.0],
]
DEFAULT_BELT_ATTITUDE: Final[OT3Transform] = [
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
]
DEFAULT_CARRIAGE_OFFSET: Final[Offset] = (477.20, 493.8, 253.475)
DEFAULT_LEFT_MOUNT_OFFSET: Final[Offset] = (-13.5, -60.5, 255.675)
DEFAULT_RIGHT_MOUNT_OFFSET: Final[Offset] = (40.5, -60.5, 255.675)
Expand Down Expand Up @@ -382,7 +387,7 @@ def build_with_defaults(robot_settings: Dict[str, Any]) -> OT3Config:
"safe_home_distance", DEFAULT_SAFE_HOME_DISTANCE
),
deck_transform=_build_default_transform(
robot_settings.get("deck_transform", []), DEFAULT_DECK_TRANSFORM
robot_settings.get("deck_transform", []), DEFAULT_MACHINE_TRANSFORM
),
carriage_offset=_build_default_offset(
robot_settings.get("carriage_offset", []), DEFAULT_CARRIAGE_OFFSET
Expand Down
11 changes: 6 additions & 5 deletions api/src/opentrons/config/robot_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,12 @@ def _save_config_data(data: str, filename: Union[str, Path]) -> None:
log.exception("Write failed with exception:")


def default_deck_calibration() -> List[List[float]]:
if ff.enable_ot3_hardware_controller():
return defaults_ot3.DEFAULT_DECK_TRANSFORM
else:
return defaults_ot2.DEFAULT_DECK_CALIBRATION_V2
def default_ot2_deck_calibration() -> List[List[float]]:
return defaults_ot2.DEFAULT_DECK_CALIBRATION_V2


def default_ot3_deck_calibration() -> List[List[float]]:
return defaults_ot3.DEFAULT_BELT_ATTITUDE


def default_pipette_offset() -> List[float]:
Expand Down
8 changes: 7 additions & 1 deletion api/src/opentrons/hardware_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,20 @@
from .thread_manager import ThreadManager
from .execution_manager import ExecutionManager
from .threaded_async_lock import ThreadedAsyncLock, ThreadedAsyncForbidden
from .protocols import HardwareControlAPI
from .protocols import HardwareControlInterface
from .instruments import AbstractInstrument, Gripper
from typing import Union
from .ot3_calibration import OT3Transforms
from .robot_calibration import RobotCalibration

# TODO (lc 12-05-2022) We should 1. figure out if we need
# to globally export a class that is strictly used in the hardware controller
# and 2. how to properly export an ot2 and ot3 pipette.
from .instruments.ot2.pipette import Pipette

OT2HardwareControlAPI = HardwareControlInterface[RobotCalibration]
OT3HardwareControlAPI = HardwareControlInterface[OT3Transforms]
HardwareControlAPI = Union[OT2HardwareControlAPI, OT3HardwareControlAPI]

ThreadManagedHardware = ThreadManager[HardwareControlAPI]
SyncHardwareAPI = SynchronousAdapter[HardwareControlAPI]
Expand Down
4 changes: 2 additions & 2 deletions api/src/opentrons/hardware_control/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
RobotCalibrationProvider,
RobotCalibration,
)
from .protocols import HardwareControlAPI
from .protocols import HardwareControlInterface
from .instruments.ot2.pipette_handler import PipetteHandlerProvider
from .instruments.ot2.instrument_calibration import load_pipette_offset
from .motion_utilities import (
Expand All @@ -79,7 +79,7 @@ class API(
# of methods that are present in the protocol will call the (empty,
# do-nothing) methods in the protocol. This will happily make all the
# tests fail.
HardwareControlAPI,
HardwareControlInterface[RobotCalibration],
):
"""This API is the primary interface to the hardware controller.
Expand Down
Loading

0 comments on commit e5e499e

Please sign in to comment.