From 532d20a2c7d51eb6fbe7032c54099ddb01779f49 Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 20 Oct 2022 12:12:52 -0400 Subject: [PATCH 01/53] pick up tip test script --- .../hardware_testing/scripts/pick_up_tip.py | 455 ++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100644 hardware-testing/hardware_testing/scripts/pick_up_tip.py diff --git a/hardware-testing/hardware_testing/scripts/pick_up_tip.py b/hardware-testing/hardware_testing/scripts/pick_up_tip.py new file mode 100644 index 00000000000..d9a85645ee0 --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/pick_up_tip.py @@ -0,0 +1,455 @@ +"""Demo OT3 Gantry Functionality.""" +import argparse +import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import GantryLoad, OT3Mount, OT3Axis, Point, Axis +from hardware_testing.opentrons_api.helpers_ot3 import ( + OT3API, + build_async_ot3_hardware_api, + GantryLoadSettings, + set_gantry_load_per_axis_settings_ot3, + home_ot3, + get_endstop_position_ot3, + move_plunger_absolute_ot3, + update_pick_up_current, + update_pick_up_distance +) + +from hardware_testing import data +from hardware_testing.drivers.mark10 import Mark10 + +MOUNT = OT3Mount.LEFT +PIPETTE_SPEED = 10 + +SPEED_XY = 500 +SPEED_Z = 250 + +pick_up_speed = 5 +press_distance = 15 +aspirate_depth = 10 +volume = 50 +liquid_retract_dist = 12 +liquid_retract_speed = 5 +retract_dist = 100 +retract_speed = 60 + +leak_test_time = 30 + +def _create_relative_point(axis: OT3Axis, distance: float) -> Point: + if axis == OT3Axis.X: + return Point(x=distance) + elif axis == OT3Axis.Y: + return Point(y=distance) + elif axis == OT3Axis.Z_L or axis == OT3Axis.Z_R: + return Point(z=distance) + raise ValueError(f"Unexpected axis: {axis}") + +async def get_encoder_position( + api: OT3API, mount: OT3Mount) -> Dict[Axis, float]: + enc_position = await api.encoder_current_position(mount=MOUNT, refresh=True) + return enc_position + +async def jog(api: OT3API)-> Dict[OT3Axis, float]: + motion = True + cur_pos = await api.current_position_ot3(MOUNT) + print(f"X: {cur_pos[OT3Axis.X]}, Y: {cur_pos[OT3Axis.Y]}, Z: {cur_pos[OT3Axis.by_mount(MOUNT)]}") + print(f"Enter coordinates as example: 100,10,3") + while motion: + coord = ast.literal_eval(input('Enter Coordinates as: ')) + print(f"Tuple type: {isinstance(coord, Tuple)}") + if isinstance(coord, Tuple): + await api.move_to(MOUNT, Point(coord[0], coord[1], coord[2]), speed=90) + cur_pos = await api.current_position_ot3(MOUNT) + print(f"X: {cur_pos[OT3Axis.X]}, Y: {cur_pos[OT3Axis.Y]}, Z: {cur_pos[OT3Axis.by_mount(MOUNT)]}") + else: + motion = False + return await api.current_position_ot3(MOUNT) + +async def set_default_current_settings(api: OT3API, load: Optional[GantryLoad] = None): + default_run_settings = { + OT3Axis.X: GantryLoadSettings( + max_speed=SPEED_XY, + acceleration=2000, + max_start_stop_speed=0, + max_change_dir_speed=0, + hold_current=0.1, + run_current=1.4, + ), + OT3Axis.Y: GantryLoadSettings( + max_speed=SPEED_XY, + acceleration=2000, + max_start_stop_speed=0, + max_change_dir_speed=0, + hold_current=0.1, + run_current=1.4, + ), + OT3Axis.Z_L: GantryLoadSettings( + max_speed=SPEED_Z, + acceleration=1500, + max_start_stop_speed=0, + max_change_dir_speed=0, + hold_current=0.1, + run_current=1.0, + ), + OT3Axis.Z_R: GantryLoadSettings( + max_speed=SPEED_Z, + acceleration=1500, + max_start_stop_speed=0, + max_change_dir_speed=0, + hold_current=0.1, + run_current=1.0, + ), + } + # if load is None: + # LOAD = api._gantry_load + await set_gantry_load_per_axis_settings_ot3(api, + default_run_settings, + load=None + ) + # await api.set_gantry_load(gantry_load=LOAD) + +async def set_current_settings(api: OT3API, motor_current: float, load: Optional[GantryLoad] = None): + z_pickup_run_settings = { + OT3Axis.X: GantryLoadSettings( + max_speed=SPEED_XY, + acceleration=2000, + max_start_stop_speed=0, + max_change_dir_speed=0, + hold_current=0.1, + run_current=1.4, + ), + OT3Axis.Y: GantryLoadSettings( + max_speed=SPEED_XY, + acceleration=2000, + max_start_stop_speed=0, + max_change_dir_speed=0, + hold_current=0.1, + run_current=1.4, + ), + OT3Axis.Z_L: GantryLoadSettings( + max_speed=SPEED_Z, + acceleration=1500, + max_start_stop_speed=0, + max_change_dir_speed=0, + hold_current=0.1, + run_current=motor_current, + ), + OT3Axis.Z_R: GantryLoadSettings( + max_speed=SPEED_Z, + acceleration=1500, + max_start_stop_speed=0, + max_change_dir_speed=0, + hold_current=0.1, + run_current=motor_current, + ), + } + # if load is None: + # LOAD = api._gantry_load + await set_gantry_load_per_axis_settings_ot3(api, + z_pickup_run_settings, + load=None) + # await api.set_gantry_load(gantry_load=LOAD) + +async def pick_up_function(api: OT3API, + loc, speed, press_distance): + # Pick up tip function + await api.move_to(MOUNT, + Point(loc[0], loc[1], loc_[2]-press_distance), + speed = speed) + +async def update_tip_spec(api, action): + if action == 'pickup': + realmount = OT3Mount.from_mount(MOUNT) + spec, _add_tip_to_instrs = api._pipette_handler.plan_check_pick_up_tip( + realmount, 78.5, None, None + ) + _add_tip_to_instrs() + elif action == 'drop_tip': + realmount = OT3Mount.from_mount(MOUNT) + spec, _remove = self._pipette_handler.plan_check_drop_tip(realmount, home_after) + _remove() + else: + raise("Pass a pickup or drop_tip to function") + +async def countdown(count_time: float): + """ + This function loops through a countdown before checking the leak visually + """ + time_suspend = 0 + while time_suspend < count_time: + await asyncio.sleep(1) + time_suspend +=1 + print(f"Remaining: {count_time-time_suspend} (s)", end='') + print('\r', end='') + print('') + +async def _main() -> None: + hw_api = await build_async_ot3_hardware_api(is_simulating=args.simulate, + use_defaults=True) + await set_default_current_settings(hw_api, load=None) + await home_ot3(hw_api, [OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.cache_instruments() + global encoder_position + global encoder_end + global stop_threads + global motion + encoder_end = None + await hw_api.home_plunger(MOUNT) + # fg_loc = X: 186.0, Y: 34.0, Z: 125.0 + if args.fg_jog: + # print("Force Gauge Calibrate") + # fg_loc = await jog(hw_api) + # fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(MOUNT)]] + # await hw_api.home_z(MOUNT, allow_home_other = False) + #fg_loc = [186.0, 34.0, 125.0] + #fg_loc = [22.5 , 34, 125] + # fg_loc = [24.0, 64, 65.0] + fg_loc = [24.0, 64, 125.0] # Multi channel coord + # 22.5, 34, 125, + if args.tiprack: + # print("Tiprack") + # tiprack_loc = await jog(hw_api) + # tiprack_loc = [tiprack_loc[OT3Axis.X], tiprack_loc[OT3Axis.Y], tiprack_loc[OT3Axis.by_mount(MOUNT)]] + # await hw_api.home_z(MOUNT, allow_home_other = False) + # tiprack_loc = [136.7, 62.3, 80] Single Channel + tiprack_loc = [138.0, 61.5, 80.0] # Multi Channel + #X: 137.0, Y: 62.3, Z: 80.0 + if args.trough: + # print("Trough") + # await hw_api.add_tip(MOUNT, 58.5) + # trough_loc = await jog(hw_api) + # trough_loc = [trough_loc[OT3Axis.X], trough_loc[OT3Axis.Y], trough_loc[OT3Axis.by_mount(MOUNT)]] + # await hw_api.home_z(MOUNT, allow_home_other = False) + # await hw_api.remove_tip(MOUNT) + trough_loc = [301.5, 61.5, 24.0] + # trough_loc = [301.5, 61.5, -10.0] # P50 Multi Channel coord + # trough_loc = [300, 40, 85-78.5] + #X: 300.0, Y: 40.0, Z: 85.0 + # await hw_api.disengage_axes([OT3Axis.of_main_tool_actuator(MOUNT)]) + try: + while True: + await set_default_current_settings(hw_api, load=None) + cur_pos = await hw_api.current_position_ot3(MOUNT) + z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] + m_current = float(input("motor_current in amps: ")) + await hw_api.move_to(MOUNT, Point(fg_loc[0], fg_loc[1], z_pos)) + # Move pipette to Force Gauge calibrated location + await hw_api.move_to(MOUNT, Point(fg_loc[0], fg_loc[1], fg_loc[2]), speed = 65) + + encoder_position = await get_encoder_position(hw_api, MOUNT) + encoder_position = encoder_position[Axis.Z] + location = 'Force_Gauge' + force_thread = Thread(target=force_record, args=(m_current, location, )) + force_thread.start() + await set_current_settings(hw_api, m_current) + # Move pipette to Force Gauge press location + await hw_api.move_to(MOUNT, + Point(fg_loc[0], fg_loc[1], fg_loc[2] - press_distance), + speed = pick_up_speed) + await asyncio.sleep(2) + encoder_position = await get_encoder_position(hw_api, MOUNT) + encoder_position = encoder_position[Axis.Z] + print(encoder_position) + motion = False + stop_threads = True + force_thread.join() #Thread Finished + await set_default_current_settings(hw_api, load=None) + await hw_api.home_z(MOUNT, allow_home_other = False) + # Obtain the current position of the Z mount + cur_pos = await hw_api.current_position_ot3(MOUNT) + z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] + # Move over to the TipRack location and + await hw_api.move_to(MOUNT, Point(tiprack_loc[0], tiprack_loc[1], z_pos)) + + await set_default_current_settings(hw_api, load=None) + # Move Pipette to top of Tip Rack Location + await hw_api.move_to(MOUNT, Point(tiprack_loc[0], tiprack_loc[1], tiprack_loc[2]), speed = 65) + location = 'Tiprack' + # Start recording the encoder + encoder_position = await get_encoder_position(hw_api, MOUNT) + encoder_position = encoder_position[Axis.Z] + enc_thread = Thread(target=force_record, args=(m_current,location,)) + enc_thread.start() + # Press Pipette into the tip + await set_current_settings(hw_api, m_current) + await hw_api.move_to(MOUNT, + Point(tiprack_loc[0], + tiprack_loc[1], + tiprack_loc[2]-press_distance), + speed = pick_up_speed + ) + await hw_api.add_tip(MOUNT, 58.5) + await asyncio.sleep(2) + encoder_end = await get_encoder_position(hw_api, MOUNT) + encoder_end = encoder_end[Axis.Z] + stop_threads = True + enc_thread.join() #Thread Finished + # Reset Current Settings + await set_default_current_settings(hw_api, load=None) + # Home Z + await hw_api.home_z(MOUNT, allow_home_other = False) + input("Feel the Tip") + + cur_pos = await hw_api.current_position_ot3(MOUNT) + z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] + await hw_api.move_to(MOUNT, Point(trough_loc[0], trough_loc[1], z_pos)) + await hw_api.move_to(MOUNT, Point(trough_loc[0], trough_loc[1], trough_loc[2]+10), speed = 65) + await hw_api.move_to(MOUNT, Point(trough_loc[0], trough_loc[1], trough_loc[2]-aspirate_depth), speed = 1) + await hw_api.prepare_for_aspirate(MOUNT) + await hw_api.aspirate(MOUNT) + + cur_pos = await hw_api.current_position_ot3(MOUNT) + z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] + await hw_api.move_to(MOUNT, + Point(trough_loc[0], + trough_loc[1], + z_pos+liquid_retract_dist), + speed = liquid_retract_speed) + cur_pos = await hw_api.current_position_ot3(MOUNT) + z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] + await hw_api.move_to(MOUNT, + Point(trough_loc[0], + trough_loc[1], + z_pos+retract_dist), + speed = retract_speed) + await hw_api.home_z(MOUNT, allow_home_other = False) + await countdown(count_time = leak_test_time) + input("Check to see if the pipette is leaking") + await hw_api.move_to(MOUNT, Point(trough_loc[0], + trough_loc[1], + trough_loc[2]+10), + speed = 65) + await hw_api.move_to(MOUNT, Point(trough_loc[0], + trough_loc[1], + trough_loc[2]-aspirate_depth), + speed = 5) + await hw_api.dispense(MOUNT) + # await hw_api.blow_out(MOUNT) + await hw_api.home_z(MOUNT, allow_home_other = False) + await hw_api.drop_tip(MOUNT) + await hw_api.remove_tip(MOUNT) + + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + + + +def force_record(motor_current, location): + dir = os.getcwd() + global encoder_position + global encoder_end + global stop_threads + global motion + encoder_end = None + file_name = "/results/force_pu_test_%s-%s-%s.csv" %(motor_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + location) + print(dir+file_name) + with open(dir+file_name, 'w', newline='') as f: + test_data = {'Time(s)':None, + 'Force(N)':None, + 'M_current(amps)':None, + 'encoder_pos(mm)': None, + 'end_enc_pos(mm)': None} + log_file = csv.DictWriter(f, test_data) + log_file.writeheader() + start_time = time.perf_counter() + try: + motion = True + stop_threads = False + while motion: + reading = float(fg.read_force()) + test_data['Time(s)'] = (time.perf_counter()-start_time) + test_data['Force(N)'] = reading + test_data['M_current(amps)'] = motor_current + test_data['encoder_pos(mm)'] = encoder_position + test_data['end_enc_pos(mm)'] = encoder_end + log_file.writerow(test_data) + print(test_data) + f.flush() + if stop_threads: + break + except KeyboardInterrupt: + print("Test Cancelled") + test_data['Errors'] = "Test Cancelled" + f.flush() + except Exception as e: + print("ERROR OCCURED") + test_data['Errors'] = e + f.flush() + raise e + print("Test done") + f.flush() + f.close() + +def enc_record(motor_current, location): + dir = os.getcwd() + global encoder_position + global encoder_end + global stop_threads + global motion + encoder_end = None + file_name = "/results/enc_pu_test_%s-%s-%s.csv" %(motor_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + location) + print(file_name) + print(dir+file_name) + with open(dir+file_name, 'wb', newline='') as f: + test_data = {'time(s)':None, + 'start_enc_pos':None, + 'end_enc_pos(mm)': None} + log_file = csv.DictWriter(f, test_data) + log_file.writeheader() + start_time = time.perf_counter() + try: + motion = True + stop_threads = False + while motion: + test_data['time(s)'] = (time.perf_counter()-start_time) + test_data['start_enc_pos(mm)'] = encoder_position + test_data['end_enc_pos(mm)'] = encoder_end + log_file.writerow(test_data) + print(test_data) + f.flush() + if stop_threads: + break + except KeyboardInterrupt: + print("Test Cancelled") + test_data['Errors'] = "Test Cancelled" + f.flush() + except Exception as e: + print("ERROR OCCURED") + test_data['Errors'] = e + f.flush() + raise e + print("Test done") + f.flush() + f.close() + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--fg_jog", action="store_true") + parser.add_argument("--trough", action="store_true") + parser.add_argument("--tiprack", action="store_true") + parser.add_argument("--cycles", type=int, + default = 1000, help = "Number of Cycles to run") + parser.add_argument("--port", type=str, + default = '/dev/ttyUSB0', help = "Force Gauge Port") + args = parser.parse_args() + + fg = Mark10.create(port=args.port) + fg.connect() + asyncio.run(_main()) From d87aaaead7eb1ebc97e30ede5c03c19e18323d67 Mon Sep 17 00:00:00 2001 From: Carlos Date: Fri, 4 Nov 2022 12:48:08 -0400 Subject: [PATCH 02/53] pick up tip testing --- .gitattributes | 2 + api/src/opentrons/config/defaults_ot3.py | 22 +-- .../hardware_testing/scripts/pick_up_tip.py | 155 ++++++++++-------- .../definitions/pipetteModelSpecs.json | 6 +- 4 files changed, 103 insertions(+), 82 deletions(-) diff --git a/.gitattributes b/.gitattributes index c44cace90c6..3d6db98ee68 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,3 +4,5 @@ *.hex binary *.bat binary api/pypi-readme.rst text eol=lf +git ls-files --eol +* -text diff --git a/api/src/opentrons/config/defaults_ot3.py b/api/src/opentrons/config/defaults_ot3.py index acbfef06398..e9e4db2395d 100644 --- a/api/src/opentrons/config/defaults_ot3.py +++ b/api/src/opentrons/config/defaults_ot3.py @@ -53,10 +53,10 @@ [0.0, -1.0, 0.0], [0.0, 0.0, -1.0], ] -DEFAULT_CARRIAGE_OFFSET: Final[Offset] = (477.20, 493.8, 253.475) +DEFAULT_CARRIAGE_OFFSET: Final[Offset] = (436.605, 484.975, 233.475) DEFAULT_LEFT_MOUNT_OFFSET: Final[Offset] = (-21.0, -63.05, 256.175) DEFAULT_RIGHT_MOUNT_OFFSET: Final[Offset] = (33, -63.05, 256.175) -DEFAULT_GRIPPER_MOUNT_OFFSET: Final[Offset] = (84.55, -12.75, 93.85) +DEFAULT_GRIPPER_MOUNT_OFFSET: Final[Offset] = (82.15, -16, 92.55) DEFAULT_Z_RETRACT_DISTANCE: Final = 2 DEFAULT_MAX_SPEEDS: Final[ByGantryLoad[Dict[OT3AxisKind, float]]] = ByGantryLoad( @@ -179,27 +179,27 @@ DEFAULT_HOLD_CURRENT: Final[ByGantryLoad[Dict[OT3AxisKind, float]]] = ByGantryLoad( none={ - OT3AxisKind.X: 0.1, - OT3AxisKind.Y: 0.1, - OT3AxisKind.Z: 0.1, + OT3AxisKind.X: 0.5, + OT3AxisKind.Y: 0.5, + OT3AxisKind.Z: 0.5, OT3AxisKind.P: 0.3, OT3AxisKind.Z_G: 0.2, }, high_throughput={ - OT3AxisKind.X: 0.1, - OT3AxisKind.Y: 0.1, + OT3AxisKind.X: 1.0, + OT3AxisKind.Y: 1.0, OT3AxisKind.Z: 0.1, OT3AxisKind.P: 0.3, }, low_throughput={ - OT3AxisKind.X: 0.1, - OT3AxisKind.Y: 0.1, + OT3AxisKind.X: 0.5, + OT3AxisKind.Y: 0.5, OT3AxisKind.Z: 0.1, OT3AxisKind.P: 0.3, }, two_low_throughput={ - OT3AxisKind.X: 0.1, - OT3AxisKind.Y: 0.1, + OT3AxisKind.X: 0.5, + OT3AxisKind.Y: 0.5, }, gripper={ OT3AxisKind.Z: 0.1, diff --git a/hardware-testing/hardware_testing/scripts/pick_up_tip.py b/hardware-testing/hardware_testing/scripts/pick_up_tip.py index d9a85645ee0..7db951a859b 100644 --- a/hardware-testing/hardware_testing/scripts/pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/pick_up_tip.py @@ -34,7 +34,7 @@ pick_up_speed = 5 press_distance = 15 -aspirate_depth = 10 +aspirate_depth = 7 volume = 50 liquid_retract_dist = 12 liquid_retract_speed = 5 @@ -66,7 +66,7 @@ async def jog(api: OT3API)-> Dict[OT3Axis, float]: coord = ast.literal_eval(input('Enter Coordinates as: ')) print(f"Tuple type: {isinstance(coord, Tuple)}") if isinstance(coord, Tuple): - await api.move_to(MOUNT, Point(coord[0], coord[1], coord[2]), speed=90) + await api.move_to(MOUNT, Point(coord[0], coord[1], coord[2]), speed=65) cur_pos = await api.current_position_ot3(MOUNT) print(f"X: {cur_pos[OT3Axis.X]}, Y: {cur_pos[OT3Axis.Y]}, Z: {cur_pos[OT3Axis.by_mount(MOUNT)]}") else: @@ -78,43 +78,40 @@ async def set_default_current_settings(api: OT3API, load: Optional[GantryLoad] = OT3Axis.X: GantryLoadSettings( max_speed=SPEED_XY, acceleration=2000, - max_start_stop_speed=0, - max_change_dir_speed=0, + max_start_stop_speed=10, + max_change_dir_speed=5, hold_current=0.1, run_current=1.4, ), OT3Axis.Y: GantryLoadSettings( max_speed=SPEED_XY, acceleration=2000, - max_start_stop_speed=0, - max_change_dir_speed=0, + max_start_stop_speed=10, + max_change_dir_speed=5, hold_current=0.1, run_current=1.4, ), OT3Axis.Z_L: GantryLoadSettings( max_speed=SPEED_Z, acceleration=1500, - max_start_stop_speed=0, - max_change_dir_speed=0, + max_start_stop_speed=10, + max_change_dir_speed=5, hold_current=0.1, - run_current=1.0, + run_current=1.4, ), OT3Axis.Z_R: GantryLoadSettings( max_speed=SPEED_Z, acceleration=1500, - max_start_stop_speed=0, - max_change_dir_speed=0, + max_start_stop_speed=10, + max_change_dir_speed=5, hold_current=0.1, - run_current=1.0, + run_current=1.4, ), } - # if load is None: - # LOAD = api._gantry_load await set_gantry_load_per_axis_settings_ot3(api, default_run_settings, load=None ) - # await api.set_gantry_load(gantry_load=LOAD) async def set_current_settings(api: OT3API, motor_current: float, load: Optional[GantryLoad] = None): z_pickup_run_settings = { @@ -123,7 +120,7 @@ async def set_current_settings(api: OT3API, motor_current: float, load: Optional acceleration=2000, max_start_stop_speed=0, max_change_dir_speed=0, - hold_current=0.1, + hold_current=0.5, run_current=1.4, ), OT3Axis.Y: GantryLoadSettings( @@ -131,7 +128,7 @@ async def set_current_settings(api: OT3API, motor_current: float, load: Optional acceleration=2000, max_start_stop_speed=0, max_change_dir_speed=0, - hold_current=0.1, + hold_current=0.5, run_current=1.4, ), OT3Axis.Z_L: GantryLoadSettings( @@ -151,12 +148,9 @@ async def set_current_settings(api: OT3API, motor_current: float, load: Optional run_current=motor_current, ), } - # if load is None: - # LOAD = api._gantry_load await set_gantry_load_per_axis_settings_ot3(api, z_pickup_run_settings, load=None) - # await api.set_gantry_load(gantry_load=LOAD) async def pick_up_function(api: OT3API, loc, speed, press_distance): @@ -196,78 +190,90 @@ async def _main() -> None: use_defaults=True) await set_default_current_settings(hw_api, load=None) await home_ot3(hw_api, [OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + home_position = await hw_api.current_position_ot3(MOUNT) await hw_api.cache_instruments() global encoder_position global encoder_end global stop_threads global motion encoder_end = None + trash_loc = () await hw_api.home_plunger(MOUNT) # fg_loc = X: 186.0, Y: 34.0, Z: 125.0 - if args.fg_jog: - # print("Force Gauge Calibrate") + # if args.fg_jog: + # print("Move to Force Gauge") # fg_loc = await jog(hw_api) # fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(MOUNT)]] # await hw_api.home_z(MOUNT, allow_home_other = False) - #fg_loc = [186.0, 34.0, 125.0] - #fg_loc = [22.5 , 34, 125] + # fg_loc = [-4.0, 87.25, 410.0] # 96 Pipette Channel + # fg_loc = [186.0, 34.0, 125.0] + # fg_loc = [22.5 , 34, 125] # P1KS Coordinate + # fg_loc = [24.0, 64, 65.0] - fg_loc = [24.0, 64, 125.0] # Multi channel coord - # 22.5, 34, 125, + # fg_loc = [24.0, 64, 125.0] # Multi channel coord if args.tiprack: - # print("Tiprack") + # print("Move to Tiprack") # tiprack_loc = await jog(hw_api) # tiprack_loc = [tiprack_loc[OT3Axis.X], tiprack_loc[OT3Axis.Y], tiprack_loc[OT3Axis.by_mount(MOUNT)]] # await hw_api.home_z(MOUNT, allow_home_other = False) - # tiprack_loc = [136.7, 62.3, 80] Single Channel - tiprack_loc = [138.0, 61.5, 80.0] # Multi Channel - #X: 137.0, Y: 62.3, Z: 80.0 + tiprack_loc = [135.7, 63.1, 80.0] + #tiprack_loc = [136.5, 63.3, 78.0] # Oolong + # tiprack_loc = [136.5, 62.6, 80.0] #Mr T robot + #tiprack_loc = [157.25, 84.25, 366.0] # 96 Channel + # tiprack_loc = [136.0, 62.3, 80] # P1K Single Channel + # tiprack_loc = [138.0, 61.5, 80.0] # 1KS Multi Channel, P50M if args.trough: - # print("Trough") + # print("Move to Trough") # await hw_api.add_tip(MOUNT, 58.5) # trough_loc = await jog(hw_api) # trough_loc = [trough_loc[OT3Axis.X], trough_loc[OT3Axis.Y], trough_loc[OT3Axis.by_mount(MOUNT)]] # await hw_api.home_z(MOUNT, allow_home_other = False) # await hw_api.remove_tip(MOUNT) - trough_loc = [301.5, 61.5, 24.0] + trough_loc=[310.0, 40.0, -8.5] + #trough_loc = [310.0, 40.0, 24.0] + # trough_loc = [299.5, 40.0, 30.0] # Mr T + # trough_loc = [301.5, 61.5, 24.0] # P1K Multi Channel Coord # trough_loc = [301.5, 61.5, -10.0] # P50 Multi Channel coord + # trough_loc = [299.0, 40.0, 30.0] # P1KS Coord # trough_loc = [300, 40, 85-78.5] #X: 300.0, Y: 40.0, Z: 85.0 - # await hw_api.disengage_axes([OT3Axis.of_main_tool_actuator(MOUNT)]) try: while True: - await set_default_current_settings(hw_api, load=None) - cur_pos = await hw_api.current_position_ot3(MOUNT) - z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] + # #-----------------------Force Gauge----------------------------------- + # await set_default_current_settings(hw_api, load=None) + # cur_pos = await hw_api.current_position_ot3(MOUNT) + # z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] m_current = float(input("motor_current in amps: ")) - await hw_api.move_to(MOUNT, Point(fg_loc[0], fg_loc[1], z_pos)) - # Move pipette to Force Gauge calibrated location - await hw_api.move_to(MOUNT, Point(fg_loc[0], fg_loc[1], fg_loc[2]), speed = 65) + # await hw_api.move_to(MOUNT, Point(fg_loc[0], fg_loc[1], z_pos)) + # # Move pipette to Force Gauge calibrated location + # await hw_api.move_to(MOUNT, Point(fg_loc[0], fg_loc[1], fg_loc[2]), speed = 65) + # + # encoder_position = await get_encoder_position(hw_api, MOUNT) + # encoder_position = encoder_position[Axis.Z] + # location = 'Force_Gauge' + # force_thread = Thread(target=force_record, args=(m_current, location, )) + # force_thread.start() + # await set_current_settings(hw_api, m_current) + # # Move pipette to Force Gauge press location + # await hw_api.move_to(MOUNT, + # Point(fg_loc[0], fg_loc[1], fg_loc[2] - press_distance), + # speed = pick_up_speed) + # await asyncio.sleep(2) + # encoder_position = await get_encoder_position(hw_api, MOUNT) + # encoder_position = encoder_position[Axis.Z] + # print(encoder_position) + # motion = False + # stop_threads = True + # force_thread.join() #Thread Finished + # await set_default_current_settings(hw_api, load=None) + # await hw_api.home_z(MOUNT, allow_home_other = False) - encoder_position = await get_encoder_position(hw_api, MOUNT) - encoder_position = encoder_position[Axis.Z] - location = 'Force_Gauge' - force_thread = Thread(target=force_record, args=(m_current, location, )) - force_thread.start() - await set_current_settings(hw_api, m_current) - # Move pipette to Force Gauge press location - await hw_api.move_to(MOUNT, - Point(fg_loc[0], fg_loc[1], fg_loc[2] - press_distance), - speed = pick_up_speed) - await asyncio.sleep(2) - encoder_position = await get_encoder_position(hw_api, MOUNT) - encoder_position = encoder_position[Axis.Z] - print(encoder_position) - motion = False - stop_threads = True - force_thread.join() #Thread Finished - await set_default_current_settings(hw_api, load=None) - await hw_api.home_z(MOUNT, allow_home_other = False) + #-----------------------Tiprack------------------------------------ # Obtain the current position of the Z mount cur_pos = await hw_api.current_position_ot3(MOUNT) z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] # Move over to the TipRack location and - await hw_api.move_to(MOUNT, Point(tiprack_loc[0], tiprack_loc[1], z_pos)) + await hw_api.move_to(MOUNT, Point(tiprack_loc[0], tiprack_loc[1], z_pos), speed = 120) await set_default_current_settings(hw_api, load=None) # Move Pipette to top of Tip Rack Location @@ -275,9 +281,10 @@ async def _main() -> None: location = 'Tiprack' # Start recording the encoder encoder_position = await get_encoder_position(hw_api, MOUNT) + print(f"Start encoder: {encoder_position}") encoder_position = encoder_position[Axis.Z] - enc_thread = Thread(target=force_record, args=(m_current,location,)) - enc_thread.start() + # enc_thread = Thread(target=force_record, args=(m_current,location,)) + # enc_thread.start() # Press Pipette into the tip await set_current_settings(hw_api, m_current) await hw_api.move_to(MOUNT, @@ -289,18 +296,20 @@ async def _main() -> None: await hw_api.add_tip(MOUNT, 58.5) await asyncio.sleep(2) encoder_end = await get_encoder_position(hw_api, MOUNT) + print(f"End Encoder: {encoder_end}") encoder_end = encoder_end[Axis.Z] - stop_threads = True - enc_thread.join() #Thread Finished + # stop_threads = True + # enc_thread.join() #Thread Finished # Reset Current Settings await set_default_current_settings(hw_api, load=None) # Home Z - await hw_api.home_z(MOUNT, allow_home_other = False) + await hw_api.home([OT3Axis.by_mount(MOUNT)]) input("Feel the Tip") + #-----------------------Aspirate----------------------------------- cur_pos = await hw_api.current_position_ot3(MOUNT) z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] - await hw_api.move_to(MOUNT, Point(trough_loc[0], trough_loc[1], z_pos)) + await hw_api.move_to(MOUNT, Point(trough_loc[0], trough_loc[1], z_pos), speed = 120) await hw_api.move_to(MOUNT, Point(trough_loc[0], trough_loc[1], trough_loc[2]+10), speed = 65) await hw_api.move_to(MOUNT, Point(trough_loc[0], trough_loc[1], trough_loc[2]-aspirate_depth), speed = 1) await hw_api.prepare_for_aspirate(MOUNT) @@ -333,9 +342,20 @@ async def _main() -> None: speed = 5) await hw_api.dispense(MOUNT) # await hw_api.blow_out(MOUNT) + #--------------------Drop Tip-------------------------------------- await hw_api.home_z(MOUNT, allow_home_other = False) + cur_pos = await hw_api.current_position_ot3(MOUNT) + z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] + await hw_api.move_to(MOUNT, Point(home_position[OT3Axis.X]-70, + home_position[OT3Axis.Y]-50, z_pos), + speed = 120) + cur_pos = await hw_api.current_position_ot3(MOUNT) + z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] + await hw_api.move_to(MOUNT, Point(cur_pos[OT3Axis.X], + cur_pos[OT3Axis.Y], z_pos-150), speed = 65) await hw_api.drop_tip(MOUNT) await hw_api.remove_tip(MOUNT) + await hw_api.home_z(MOUNT, allow_home_other = False) await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) except KeyboardInterrupt: @@ -345,7 +365,6 @@ async def _main() -> None: await hw_api.clean_up() - def force_record(motor_current, location): dir = os.getcwd() global encoder_position @@ -450,6 +469,6 @@ def enc_record(motor_current, location): default = '/dev/ttyUSB0', help = "Force Gauge Port") args = parser.parse_args() - fg = Mark10.create(port=args.port) - fg.connect() + # fg = Mark10.create(port=args.port) + # fg.connect() asyncio.run(_main()) diff --git a/shared-data/pipette/definitions/pipetteModelSpecs.json b/shared-data/pipette/definitions/pipetteModelSpecs.json index a9c96e2373c..80e77fe5331 100644 --- a/shared-data/pipette/definitions/pipetteModelSpecs.json +++ b/shared-data/pipette/definitions/pipetteModelSpecs.json @@ -7005,21 +7005,21 @@ "type": "float" }, "bottom": { - "value": 71.5, + "value": 64.5, "min": 55, "max": 80, "type": "float", "units": "mm" }, "blowout": { - "value": 76.5, + "value": 68.5, "min": 60, "max": 85, "units": "mm", "type": "float" }, "dropTip": { - "value": 90.5, + "value": 80.5, "min": 78, "max": 110, "units": "mm", From 2e4b9c5cbb6404a1a3eac7a9690118fc6364dfd6 Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 10 Jan 2023 17:11:11 -0500 Subject: [PATCH 03/53] pick_up_tip_testing --- .../scripts/pick_up_tip_testing.py | 297 ++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py diff --git a/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py b/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py new file mode 100644 index 00000000000..0534f941832 --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py @@ -0,0 +1,297 @@ +import logging +import asyncio +import argparse +from numpy import float64 +import termios +import sys, tty, os, time + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import GantryLoad, OT3Mount, OT3Axis, Point, Axis +from hardware_testing.opentrons_api.helpers_ot3 import ( + OT3API, + build_async_ot3_hardware_api, + GantryLoadSettings, + set_gantry_load_per_axis_settings_ot3, + home_ot3, + get_endstop_position_ot3, + move_plunger_absolute_ot3, + update_pick_up_current, + update_pick_up_distance +) + +from hardware_testing import data +from hardware_testing.drivers import mitutoyo_digimatic_indicator + + +def dict_values_to_line(dict): + return str.join(",", list(dict.values()))+"\n" + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys()))+"\n" + +def file_setup(test_data): + current_val = float(input("Enter Motor Current to be Tested:")) + test_name = "Tip_Attachment_Test" + test_header = dict_keys_to_line(test_data) + test_tag = "{}Amps-start-time-{}".format(current_val, int(time.time())) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + +def dial_indicator_setup(): + gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator(port='/dev/ttyUSB0') + gauge.connect() + return gauge + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + return _getch() + +async def _jog_axis(api, position) -> None: + step_size = [0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 150 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == 'a': + # minus x direction + sys.stdout.flush() + await api.move_rel(mount, + Point( + -step_size[step_length_index],0,0), + speed = xy_speed) + + elif input == 'd': + #plus x direction + sys.stdout.flush() + await api.move_rel(mount, + Point( + step_size[step_length_index],0,0), + speed = xy_speed) + + elif input == 'w': + #minus y direction + sys.stdout.flush() + await api.move_rel(mount, + Point( + 0,step_size[step_length_index],0), + speed = xy_speed) + + elif input == 's': + #plus y direction + sys.stdout.flush() + await api.move_rel(mount, + Point( + 0,-step_size[step_length_index],0), + speed = xy_speed) + + elif input == 'i': + sys.stdout.flush() + await api.move_rel(mount, + Point( + 0,0,step_size[step_length_index]), + speed = za_speed) + + elif input == 'k': + sys.stdout.flush() + await api.move_rel(mount, + Point( + 0,0,-step_size[step_length_index]), + speed = za_speed) + + elif input == 'q': + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == '+': + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 6: + step_length_index = 6 + step = step_size[step_length_index] + + elif input == '-': + sys.stdout.flush() + step_length_index = step_length_index -1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == '\r': + sys.stdout.flush() + return position + position = await api.current_position_ot3(mount) + + print('Coordinates: ', round(position[OT3Axis.X], 2), ',', + round(position[OT3Axis.Y], 2), ',', + round(position[OT3Axis.by_mount(mount)], 2), ' Motor Step: ', + step_size[step_length_index], + end = '') + print('\r', end='') + +async def _main() -> None: + mount = OT3Mount.RIGHT + # test_n , test_f = file_setup(test_data) + # gauge = dial_indicator_setup() + hw_api = await build_async_ot3_hardware_api(is_simulating=args.simulate, + use_defaults=True) + # await set_default_current_settings(hw_api, load=None) + await home_ot3(hw_api, [OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + home_pos = await hw_api.current_position_ot3(mount) + start_loc = {OT3Axis.X: 150, + OT3Axis.Y: 200, + OT3Axis.by_mount(mount): home_pos[OT3Axis.by_mount(mount)]} + # await hw_api.current_position_ot3(mount + await hw_api.cache_instruments() + trash_loc = () + await hw_api.home_plunger(mount) + tip_column = 0 + columns_to_use = 12 + try: + start_time = time.time() + # await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], + # start_loc[OT3Axis.Y], + # home_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to(mount, Point(home_pos[OT3Axis.X], + start_loc[OT3Axis.Y], + home_pos[OT3Axis.by_mount(mount)]), + speed = 50) + await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], + start_loc[OT3Axis.Y], + home_pos[OT3Axis.by_mount(mount)])) + for cycle in range(1, columns_to_use+1): + if cycle <= 1: + print("Move to Tiprack") + home_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await _jog_axis(hw_api, home_position) + + await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X] + tip_column, + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)]), + speed = 50) + #await hw_api.pick_up_tip(mount, tip_length = 58.5, presses = 2, increment = 1) + await update_pick_up_current(hw_api, mount, 0.5) + await hw_api.pick_up_tip(mount, tip_length = 58.5) + current_pos = await hw_api.current_position_ot3(mount) + block = await _jog_axis(hw_api, current_pos) + await hw_api.home_z(mount, allow_home_other = False) + + # if cycle <= 1: + # print("Move to Tiprack") + # home_position = await hw_api.current_position_ot3(mount) + # dial_pos = await _jog_axis(hw_api, home_position) + # await hw_api.home_z(mount) + # current_pos = await hw_api.current_position_ot3(mount) + # await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], + # dial_pos[OT3Axis.Y], + # current_pos[OT3Axis.by_mount(mount)]-3)) + # current_pos = await hw_api.current_position_ot3(mount) + # await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], + # dial_pos[OT3Axis.Y], + # current_pos[OT3Axis.by_mount(mount)])) + # tip_increment = 0 + # tips = 8 + # z_distance_press = 6 + # for tip in range(1, tips+1): + # # Press onto the dial indicator + # await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], + # dial_pos[OT3Axis.Y]-tip_increment, + # dial_pos[OT3Axis.by_mount(mount)])) + # await asyncio.sleep(2) + # elasped_time = (time.time() - start_time)/60 + # test_data["Time"] = round(elasped_time, 3) + # test_data["Tip Height(mm)"] = gauge.read_stable(timeout=20) + # test_data["Tip"] = tip + # print(test_data) + # d_str = f"{elasped_time}, {gauge.read_stable(timeout=20)}, {tip}\n" + # data.append_data_to_file(test_n, test_f, d_str) + # await asyncio.sleep(1) + # # Retract from the dial indicator by 6mm + # await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], + # dial_pos[OT3Axis.Y]-tip_increment, + # dial_pos[OT3Axis.by_mount(mount)]+z_distance_press)) + # # backlash compensation + # await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], + # dial_pos[OT3Axis.Y]-tip_increment, + # dial_pos[OT3Axis.by_mount(mount)]+z_distance_press-3)) + # # move to the next nozzle + # tip_increment += 9 + # current_pos = await hw_api.current_position_ot3(mount) + # await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], + # dial_pos[OT3Axis.Y]-tip_increment, + # current_pos[OT3Axis.by_mount(mount)])) + + trash_loc = [432.7 , 393.3 , 100.0] + await hw_api.home_z(mount, allow_home_other = False) + current_pos = await hw_api.current_position_ot3(mount) + await hw_api.move_to(mount, Point(trash_loc[0], + trash_loc[1], + current_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to(mount, Point(trash_loc[0], + trash_loc[1], + trash_loc[2])) + await hw_api.drop_tip(mount) + await hw_api.home_z(mount, allow_home_other = False) + current_pos = await hw_api.current_position_ot3(mount) + tip_column += 9 + await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X] + tip_column, + tiprack_loc[OT3Axis.Y], + current_pos[OT3Axis.by_mount(mount)])) + + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + args = parser.parse_args() + + mount = OT3Mount.RIGHT + xy_speed = 250 + speed_z = 60 + + pick_up_speed = 5 + press_distance = 15 + PIPETTE_SPEED = 10 + test_data ={ + "Time": None, + "Tip Height(mm)": None, + "Tip": None + } + asyncio.run(_main()) From 9d061bf199f5a047b4f5ff1d92aa116fc3fe6f5a Mon Sep 17 00:00:00 2001 From: Carlos Date: Fri, 13 Jan 2023 14:21:45 -0500 Subject: [PATCH 04/53] new added tests --- .../scripts/pick_up_tip_testing.py | 301 ++++++++++++------ 1 file changed, 203 insertions(+), 98 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py b/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py index 0534f941832..517fd6212a6 100644 --- a/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py +++ b/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py @@ -130,6 +130,21 @@ async def _jog_axis(api, position) -> None: 0,0,-step_size[step_length_index]), speed = za_speed) + elif input == 'r': + sys.stdout.flush() + position = await api.current_position_ot3(mount) + gauge_reading = gauge.read_stable(timeout=20) + test_data["X-Coordinate"] = round(position[OT3Axis.X], 2) + test_data["Y-Coordinate"] = round(position[OT3Axis.Y], 2) + test_data["Z-Coordinate"] = round(position[OT3Axis.by_mount(mount)], 2) + test_data["Deck Height(mm)"] = gauge_reading + print(test_data) + d_str = f"{round(position[OT3Axis.X], 2)}, \ + {round(position[OT3Axis.Y], 2)}, \ + {round(position[OT3Axis.by_mount(mount)], 2)}, \ + {gauge.read_stable(timeout=20)}, {gauge_reading}\n" + data.append_data_to_file(test_n, test_f, d_str) + elif input == 'q': sys.stdout.flush() print("TEST CANCELLED") @@ -162,9 +177,6 @@ async def _jog_axis(api, position) -> None: print('\r', end='') async def _main() -> None: - mount = OT3Mount.RIGHT - # test_n , test_f = file_setup(test_data) - # gauge = dial_indicator_setup() hw_api = await build_async_ot3_hardware_api(is_simulating=args.simulate, use_defaults=True) # await set_default_current_settings(hw_api, load=None) @@ -180,96 +192,166 @@ async def _main() -> None: tip_column = 0 columns_to_use = 12 try: - start_time = time.time() - # await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], - # start_loc[OT3Axis.Y], - # home_pos[OT3Axis.by_mount(mount)])) - await hw_api.move_to(mount, Point(home_pos[OT3Axis.X], - start_loc[OT3Axis.Y], - home_pos[OT3Axis.by_mount(mount)]), - speed = 50) - await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], - start_loc[OT3Axis.Y], - home_pos[OT3Axis.by_mount(mount)])) - for cycle in range(1, columns_to_use+1): - if cycle <= 1: - print("Move to Tiprack") - home_position = await hw_api.current_position_ot3(mount) - tiprack_loc = await _jog_axis(hw_api, home_position) + if args.test == 'pick_up_tip': + start_time = time.time() + await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], + start_loc[OT3Axis.Y], + home_pos[OT3Axis.by_mount(mount)])) + # await hw_api.move_to(mount, Point(home_pos[OT3Axis.X], + # start_loc[OT3Axis.Y], + # home_pos[OT3Axis.by_mount(mount)])) + # await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], + # start_loc[OT3Axis.Y], + # home_pos[OT3Axis.by_mount(mount)])) + for cycle in range(1, columns_to_use+1): + if cycle <= 1: + print("Move to Tiprack") + home_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await _jog_axis(hw_api, home_position) + # Coordinates: 177.0 , 182.1 , 97.5 + # tiprack_loc = {OT3Axis.X: 177.0, OT3Axis.Y: 182.1, OT3Axis.by_mount(mount): 97.5} + # await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X], + # tiprack_loc[OT3Axis.Y], + # home_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X] + tip_column, + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)])) + #await hw_api.pick_up_tip(mount, tip_length = 58.5, presses = 2, increment = 1) + current_val = float(input("Enter Current Val: ")) + print(f"Current Val: {current_val}") + await update_pick_up_current(hw_api, mount, current_val) + await hw_api.pick_up_tip(mount, tip_length = 58.5) + current_pos = await hw_api.current_position_ot3(mount) + # Move to Block + await hw_api.move_to(mount, Point(207.2, + 72.7, + current_pos[OT3Axis.by_mount(mount)])) + current_pos = await hw_api.current_position_ot3(mount) + block = await _jog_axis(hw_api, current_pos) + await hw_api.home_z(mount, allow_home_other = False) - await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X] + tip_column, - tiprack_loc[OT3Axis.Y], - tiprack_loc[OT3Axis.by_mount(mount)]), - speed = 50) - #await hw_api.pick_up_tip(mount, tip_length = 58.5, presses = 2, increment = 1) - await update_pick_up_current(hw_api, mount, 0.5) - await hw_api.pick_up_tip(mount, tip_length = 58.5) - current_pos = await hw_api.current_position_ot3(mount) - block = await _jog_axis(hw_api, current_pos) - await hw_api.home_z(mount, allow_home_other = False) + trash_loc = [432.7 , 393.3 , 100.0] + await hw_api.home_z(mount, allow_home_other = False) + current_pos = await hw_api.current_position_ot3(mount) + await hw_api.move_to(mount, Point(trash_loc[0], + trash_loc[1], + current_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to(mount, Point(trash_loc[0], + trash_loc[1], + trash_loc[2])) + await hw_api.drop_tip(mount) + await hw_api.home_z(mount, allow_home_other = False) + current_pos = await hw_api.current_position_ot3(mount) + # tip_column += 9 + await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X] + tip_column, + tiprack_loc[OT3Axis.Y], + current_pos[OT3Axis.by_mount(mount)])) + elif args.test == 'tip_height_test': + start_time = time.time() + await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], + start_loc[OT3Axis.Y], + home_pos[OT3Axis.by_mount(mount)])) + # await hw_api.move_to(mount, Point(home_pos[OT3Axis.X], + # start_loc[OT3Axis.Y], + # home_pos[OT3Axis.by_mount(mount)])) + # await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], + # start_loc[OT3Axis.Y], + # home_pos[OT3Axis.by_mount(mount)])) + for cycle in range(1, columns_to_use+1): + if cycle <= 1: + print("Move to Tiprack") + home_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await _jog_axis(hw_api, home_position) + # Coordinates: 177.0 , 182.1 , 97.5 + # tiprack_loc = {OT3Axis.X: 177.0, OT3Axis.Y: 182.1, OT3Axis.by_mount(mount): 97.5} + # await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X], + # tiprack_loc[OT3Axis.Y], + # home_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X] + tip_column, + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)])) + #await hw_api.pick_up_tip(mount, tip_length = 58.5, presses = 2, increment = 1) + current_val = float(input("Enter Current Val: ")) + print(f"Current Val: {current_val}") + await update_pick_up_current(hw_api, mount, current_val) + await hw_api.pick_up_tip(mount, tip_length = 58.5) + current_pos = await hw_api.current_position_ot3(mount) + # Move to Block + await hw_api.move_to(mount, Point(207.2, + 72.7, + current_pos[OT3Axis.by_mount(mount)])) + current_pos = await hw_api.current_position_ot3(mount) + block = await _jog_axis(hw_api, current_pos) + await hw_api.home_z(mount, allow_home_other = False) - # if cycle <= 1: - # print("Move to Tiprack") - # home_position = await hw_api.current_position_ot3(mount) - # dial_pos = await _jog_axis(hw_api, home_position) - # await hw_api.home_z(mount) - # current_pos = await hw_api.current_position_ot3(mount) - # await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], - # dial_pos[OT3Axis.Y], - # current_pos[OT3Axis.by_mount(mount)]-3)) - # current_pos = await hw_api.current_position_ot3(mount) - # await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], - # dial_pos[OT3Axis.Y], - # current_pos[OT3Axis.by_mount(mount)])) - # tip_increment = 0 - # tips = 8 - # z_distance_press = 6 - # for tip in range(1, tips+1): - # # Press onto the dial indicator - # await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], - # dial_pos[OT3Axis.Y]-tip_increment, - # dial_pos[OT3Axis.by_mount(mount)])) - # await asyncio.sleep(2) - # elasped_time = (time.time() - start_time)/60 - # test_data["Time"] = round(elasped_time, 3) - # test_data["Tip Height(mm)"] = gauge.read_stable(timeout=20) - # test_data["Tip"] = tip - # print(test_data) - # d_str = f"{elasped_time}, {gauge.read_stable(timeout=20)}, {tip}\n" - # data.append_data_to_file(test_n, test_f, d_str) - # await asyncio.sleep(1) - # # Retract from the dial indicator by 6mm - # await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], - # dial_pos[OT3Axis.Y]-tip_increment, - # dial_pos[OT3Axis.by_mount(mount)]+z_distance_press)) - # # backlash compensation - # await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], - # dial_pos[OT3Axis.Y]-tip_increment, - # dial_pos[OT3Axis.by_mount(mount)]+z_distance_press-3)) - # # move to the next nozzle - # tip_increment += 9 - # current_pos = await hw_api.current_position_ot3(mount) - # await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], - # dial_pos[OT3Axis.Y]-tip_increment, - # current_pos[OT3Axis.by_mount(mount)])) - - trash_loc = [432.7 , 393.3 , 100.0] - await hw_api.home_z(mount, allow_home_other = False) - current_pos = await hw_api.current_position_ot3(mount) - await hw_api.move_to(mount, Point(trash_loc[0], - trash_loc[1], - current_pos[OT3Axis.by_mount(mount)])) - await hw_api.move_to(mount, Point(trash_loc[0], - trash_loc[1], - trash_loc[2])) - await hw_api.drop_tip(mount) - await hw_api.home_z(mount, allow_home_other = False) - current_pos = await hw_api.current_position_ot3(mount) - tip_column += 9 - await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X] + tip_column, - tiprack_loc[OT3Axis.Y], - current_pos[OT3Axis.by_mount(mount)])) + if cycle <= 1: + print("Move to Tiprack") + home_position = await hw_api.current_position_ot3(mount) + dial_pos = await _jog_axis(hw_api, home_position) + await hw_api.home_z(mount) + current_pos = await hw_api.current_position_ot3(mount) + await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], + dial_pos[OT3Axis.Y], + current_pos[OT3Axis.by_mount(mount)]-3)) + current_pos = await hw_api.current_position_ot3(mount) + await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], + dial_pos[OT3Axis.Y], + current_pos[OT3Axis.by_mount(mount)])) + tip_increment = 0 + tips = 8 + z_distance_press = 6 + for tip in range(1, tips+1): + # Press onto the dial indicator + await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], + dial_pos[OT3Axis.Y]-tip_increment, + dial_pos[OT3Axis.by_mount(mount)])) + await asyncio.sleep(2) + elasped_time = (time.time() - start_time)/60 + test_data["Time"] = round(elasped_time, 3) + test_data["Tip Height(mm)"] = gauge.read_stable(timeout=20) + test_data["Tip"] = tip + print(test_data) + d_str = f"{elasped_time}, {gauge.read_stable(timeout=20)}, {tip}\n" + data.append_data_to_file(test_n, test_f, d_str) + await asyncio.sleep(1) + # Retract from the dial indicator by 6mm + await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], + dial_pos[OT3Axis.Y]-tip_increment, + dial_pos[OT3Axis.by_mount(mount)]+z_distance_press)) + # backlash compensation + await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], + dial_pos[OT3Axis.Y]-tip_increment, + dial_pos[OT3Axis.by_mount(mount)]+z_distance_press-3)) + # move to the next nozzle + tip_increment += 9 + current_pos = await hw_api.current_position_ot3(mount) + await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], + dial_pos[OT3Axis.Y]-tip_increment, + current_pos[OT3Axis.by_mount(mount)])) + trash_loc = [432.7 , 393.3 , 100.0] + await hw_api.home_z(mount, allow_home_other = False) + current_pos = await hw_api.current_position_ot3(mount) + await hw_api.move_to(mount, Point(trash_loc[0], + trash_loc[1], + current_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to(mount, Point(trash_loc[0], + trash_loc[1], + trash_loc[2])) + await hw_api.drop_tip(mount) + await hw_api.home_z(mount, allow_home_other = False) + current_pos = await hw_api.current_position_ot3(mount) + # tip_column += 9 + await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X] + tip_column, + tiprack_loc[OT3Axis.Y], + current_pos[OT3Axis.by_mount(mount)])) + elif args.test == 'flatness': + home_position = await hw_api.current_position_ot3(mount) + await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], + start_loc[OT3Axis.Y], + home_pos[OT3Axis.by_mount(mount)])) + # we don't need this, this is just for a placeholder + flatness = await _jog_axis(hw_api, home_position) await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) except KeyboardInterrupt: await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) @@ -277,21 +359,44 @@ async def _main() -> None: await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) await hw_api.clean_up() + + if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--simulate", action="store_true") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument( + "--test", + type=str, + help="pick_up_tip, tip_height_test, flatness", + default="pick_up_tip", + ) + parser.add_argument("--dial_indicator", action="store_true") + parser.add_argument("--flatness", action="store_true") args = parser.parse_args() - - mount = OT3Mount.RIGHT + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT xy_speed = 250 speed_z = 60 - + if args.dial_indicator: + if args.flatness: + test_data ={ + "X-Coordinate": None, + "Y-Coordinate": None, + "Z-Coordinate": None, + "Deck Height(mm)": None, + } + else: + test_data ={ + "Time": None, + "Tip Height(mm)": None, + "Tip": None + } + gauge = dial_indicator_setup() + test_n , test_f = file_setup(test_data) pick_up_speed = 5 press_distance = 15 PIPETTE_SPEED = 10 - test_data ={ - "Time": None, - "Tip Height(mm)": None, - "Tip": None - } asyncio.run(_main()) From 00d445572504e743a72bcffe1f8b0db3a0576681 Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 25 Jan 2023 16:43:31 -0500 Subject: [PATCH 05/53] Dial indicator flatness test --- .../scripts/pick_up_tip_testing.py | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py b/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py index 517fd6212a6..533d253b955 100644 --- a/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py +++ b/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py @@ -352,6 +352,83 @@ async def _main() -> None: home_pos[OT3Axis.by_mount(mount)])) # we don't need this, this is just for a placeholder flatness = await _jog_axis(hw_api, home_position) + elif args.test == 'flatness_with_move': + coordinates = [(515, 29, 294.16), + (465, 29, 294.16), + (415, 29, 294.16), + (317.23, 29, 294.16), + (317.23, 96.72, 294.16), + (326.73, 96.72, 294.16), + (326.73, 143.32, 294.16), + (415, 143.32, 294.16), + (465, 143.32, 294.16), + (515, 143.32, 294.16), + (317.24, 203.12, 294.16), + (326.24, 250.11, 294.16), + (415, 250.11, 294.16), + (465, 250.11, 294.16), + (515, 250.11, 294.16), + (317.22, 303.62, 294.16), + (326.25, 357.62, 294.16), + (415, 357.62, 294.16), + (465, 357.62, 294.16), + (515, 357.62, 294.16), + (317.22, 411.11, 294.16), + (256.22, 29, 294.16), + (268.28, 29, 294.16), + (218.28, 29, 294.16), + (154.29, 29, 294.16), + (154.29, 96.72, 294.16), + (162.29, 143.32, 294.16), + (218.29, 143.32, 294.16), + (268.29, 143.32, 294.16), + (154.8, 203.12, 294.16), + (162.3, 250.11, 294.16), + (218.3, 250.11, 294.16), + (268.3, 250.11, 294.16), + (154.81, 303.62, 294.16), + (162.31, 357.62, 294.16), + (218.3, 357.62, 294.16), + (268.31, 357.62, 294.16), + (154.86, 411.11, 294.16), + (-21.85, 29, 294.16), + (62.85, 29, 294.16), + (-21.85, 143.32, 294.16), + (62.85, 143.32, 294.16), + (-21.85, 250.11, 294.16), + (62.85, 250.11, 294.16)] + home_position = await hw_api.current_position_ot3(mount) + + array_num = 0 + for coord in coordinates: + await hw_api.move_to(mount, Point(coord[0], + coord[1], + home_pos[OT3Axis.by_mount(mount)]), + speed = 60) + await asyncio.sleep(1) + await hw_api.move_to(mount, Point(coord[0], + coord[1], + coord[2]), speed = 65) + await asyncio.sleep(3) + gauge_reading = gauge.read_stable(timeout=20) + position = await hw_api.current_position_ot3(mount) + test_data["X-Coordinate"] = round(position[OT3Axis.X], 2) + test_data["Y-Coordinate"] = round(position[OT3Axis.Y], 2) + test_data["Z-Coordinate"] = round(position[OT3Axis.by_mount(mount)], 2) + test_data["Deck Height(mm)"] = gauge_reading + print(test_data) + d_str = f"{round(position[OT3Axis.X], 2)}, \ + {round(position[OT3Axis.Y], 2)}, \ + {round(position[OT3Axis.by_mount(mount)], 2)}, \ + {gauge.read_stable(timeout=20)}, {gauge_reading}\n" + data.append_data_to_file(test_n, test_f, d_str) + await hw_api.move_to(mount, Point(coord[0], + coord[1], + home_pos[OT3Axis.by_mount(mount)]), + speed = 65) + array_num += 1 + # we don't need this, this is just for a placeholder + flatness = await _jog_axis(hw_api, home_position) await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) except KeyboardInterrupt: await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) @@ -368,7 +445,7 @@ async def _main() -> None: parser.add_argument( "--test", type=str, - help="pick_up_tip, tip_height_test, flatness", + help="pick_up_tip, tip_height_test, flatness, flatness_with_move", default="pick_up_tip", ) parser.add_argument("--dial_indicator", action="store_true") From fea12e21f9a2f27ed8f1e452427bea2ebddfbd5d Mon Sep 17 00:00:00 2001 From: Carlos Date: Fri, 3 Mar 2023 13:12:18 -0500 Subject: [PATCH 06/53] new partial pick up test script with dial indicators --- .../scripts/96_pipette_partial_pick_up.py | 643 ++++++++++++++++++ 1 file changed, 643 insertions(+) create mode 100644 hardware/opentrons_hardware/scripts/96_pipette_partial_pick_up.py diff --git a/hardware/opentrons_hardware/scripts/96_pipette_partial_pick_up.py b/hardware/opentrons_hardware/scripts/96_pipette_partial_pick_up.py new file mode 100644 index 00000000000..0a486c88c8b --- /dev/null +++ b/hardware/opentrons_hardware/scripts/96_pipette_partial_pick_up.py @@ -0,0 +1,643 @@ +"""A script for sending and receiving data from sensors on the OT3.""" +import enum +import logging +import asyncio +import argparse +from numpy import float64 +import termios +import sys, tty + +from typing import Callable +from logging.config import dictConfig + +from opentrons_hardware.firmware_bindings.messages.message_definitions import ( + EnableMotorRequest, + WriteMotorCurrentRequest, +) +from opentrons.hardware_control.backends.ot3utils import (create_move_group) +from opentrons_hardware.hardware_control.motion_planning import ( + Move, + Coordinates, +) + +from hardware_testing import data +from opentrons_hardware.firmware_bindings.utils import UInt32Field +from opentrons_hardware.firmware_bindings.messages import payloads +from opentrons_hardware.drivers.can_bus.can_messenger import CanMessenger +from opentrons_hardware.firmware_bindings.constants import NodeId, PipetteTipActionType +from opentrons_hardware.scripts.can_args import add_can_args, build_settings +from opentrons_hardware.hardware_control.motion import ( + MoveGroupTipActionStep, + MoveGroupSingleAxisStep, + MoveStopCondition, + create_home_step, + MoveType, +) +from opentrons_hardware.hardware_control.move_group_runner import MoveGroupRunner + +from opentrons_hardware.drivers.can_bus.build import build_driver + + +"""Mitutoyo ABSOLUTE Digimatic Indicator ID-S.""" +import time +import numpy +import serial # type: ignore[import] + + +class Mitutoyo_Digimatic_Indicator: + """Driver class to use dial indicator.""" + + def __init__(self, port: str = "/dev/ttyUSB0", baudrate: int = 9600) -> None: + """Initialize class.""" + self.PORT = port + self.BAUDRATE = baudrate + self.reading_raw = "" + self.GCODE = { + "READ": "r", + } + self.gauge = serial.Serial() + self.packet = "" + + def connect(self) -> None: + """Connect communication ports.""" + try: + self.gauge = serial.Serial( + port=self.PORT, + baudrate=self.BAUDRATE + ) + except serial.SerialException: + error = "Unable to access Serial port" + raise serial.SerialException(error) + + def disconnect(self) -> None: + """Disconnect communication ports.""" + self.gauge.close() + + def _send_packet(self, packet: str) -> None: + self.gauge.flush() + self.gauge.flushInput() + self.gauge.write(packet.encode("utf-8")) + + def _get_packet(self) -> str: + self.gauge.flushOutput() + packet = self.gauge.readline().decode("utf-8") + return packet + + def read(self) -> float: + """Reads dial indicator.""" + self.packet = self.GCODE["READ"] + self._send_packet(self.packet) + time.sleep(0.001) + reading = True + while reading: + data = self._get_packet() + if data != "": + reading = False + return float(data) + +GetInputFunc = Callable[[str], str] +OutputFunc = Callable[[str], None] + + +class InvalidInput(Exception): + """Invalid input exception.""" + pass + +def prompt_int_input(prompt_name: str) -> int: + """Prompt to choose a member of the enum. + Args: + output_func: Function to output text to user. + get_user_input: Function to get user input. + enum_type: an enum type + Returns: + The choice. + """ + try: + return int(input(f"{prompt_name}: ")) + except (ValueError, IndexError) as e: + raise InvalidInput(e) + +def output_details(i: int, total_i: int) -> None: + """Print out test details.""" + print(f"\n\033[95mRound {i}/{total_i}:\033[0m") + +def calc_time(distance, speed): + time = abs(distance/speed) + # print(time) + return time + +async def set_current(messenger: CanMessenger, current: float, node: NodeId): + await messenger.send( + node_id=node, + message=WriteMotorCurrentRequest( + payload=payloads.MotorCurrentPayload( + hold_current=UInt32Field(int(0 * (2**16))), + run_current=UInt32Field(int(current * (2**16))), + ) + ), + ) + +async def hold_current(messenger: CanMessenger, current: float, node: NodeId): + await messenger.send( + node_id=node, + message=WriteMotorCurrentRequest( + payload=payloads.MotorCurrentPayload( + hold_current=UInt32Field(int(current * (2**16))), + run_current=UInt32Field(int(0 * (2**16))), + ) + ), + ) + +def move_pipette_mechanism(distance, velocity): + pipette_node = NodeId.pipette_left + move = MoveGroupRunner( + move_groups=[ + [ + { + pipette_node: MoveGroupTipActionStep( + velocity_mm_sec=float64(velocity), + duration_sec=float64(calc_time(distance, + velocity)), + stop_condition=MoveStopCondition.none, + action=PipetteTipActionType.pick_up, + ) + } + ] + ] + ) + return move + +def home_pipette_jaw(): + velocity = 5.5 + distance = 40 + pipette_node = NodeId.pipette_left + move = MoveGroupRunner( + move_groups=[ + [ + { + pipette_node: MoveGroupTipActionStep( + velocity_mm_sec=float64(-velocity), + duration_sec=float64(calc_time(distance, + velocity)), + stop_condition=MoveStopCondition.limit_switch, + action=PipetteTipActionType.pick_up, + ) + } + ] + ] + ) + return move + +def move_z_axis(distance, velocity): + move_z = MoveGroupRunner( + move_groups=[ + # Group 1 + [ + { + NodeId.head_l: MoveGroupSingleAxisStep( + distance_mm=float64(0), + velocity_mm_sec=float64(velocity), + duration_sec=float64(calc_time(distance, + velocity)), + ) + } + ], + ] + ) + return move_z + +def move_x_axis(distance, velocity): + move_x = MoveGroupRunner( + move_groups=[ + # Group 1 + [ + { + NodeId.gantry_x: MoveGroupSingleAxisStep( + distance_mm=float64(0), + velocity_mm_sec=float64(velocity), + duration_sec=float64(calc_time(distance, + velocity)), + ) + } + ], + ] + ) + return move_x + +def move_to(orgin, coordinate, velocity): + acceleration = {OT3Axis.X.name: 500, + OT3Axis.Y.name: 500, + OT3Axis.Z_L.name: 50} + move = MoveGroupRunner( + move_groups=[ + # Group 1 + [ + { + NodeId.gantry_x: MoveGroupSingleAxisStep( + distance_mm=float64(0), + # acceleration_mm_sec_sq=float64(acceleration[OT3Axis.X.name]), + velocity_mm_sec=float64(velocity), + duration_sec=float64(calc_time(coordinate[OT3Axis.X.name], + velocity)), + move_type = MoveType.linear, + ) + }, + { + NodeId.gantry_y: MoveGroupSingleAxisStep( + distance_mm=float64(0), + # acceleration_mm_sec_sq=float64(acceleration[OT3Axis.Y.name]), + velocity_mm_sec=float64(velocity), + duration_sec=float64(calc_time(coordinate[OT3Axis.Y.name], + velocity)), + move_type = MoveType.linear, + ) + }, + { + NodeId.head_l: MoveGroupSingleAxisStep( + distance_mm=float64(0), + # acceleration_mm_sec_sq=float64(acceleration[OT3Axis.Z_L.name]), + velocity_mm_sec=float64(velocity), + duration_sec=float64(calc_time(coordinate[OT3Axis.Z_L.name], + velocity)), + move_type = MoveType.linear, + ) + }, + ], + ] + ) + return move + +def move_y_axis(distance, velocity): + move_y = MoveGroupRunner( + move_groups=[ + # Group 1 + [ + { + NodeId.gantry_y: MoveGroupSingleAxisStep( + distance_mm=float64(0), + velocity_mm_sec=float64(velocity), + duration_sec=float64(calc_time(distance, + velocity)), + ) + } + ], + ] + ) + return move_y + +def move_plunger(distance, velocity): + pipette_node = NodeId.pipette_left + move_plunger_runner = MoveGroupRunner( + # Group 0 + move_groups=[ + [ + { + pipette_node: MoveGroupSingleAxisStep( + distance_mm=float64(0), + velocity_mm_sec=float64(velocity), + duration_sec=float64(calc_time(distance, + velocity)), + ) + } + ] + ], + ) + return move_plunger_runner + +def home_z_axis(): + speed = 10.5 + home_z = MoveGroupRunner( + move_groups=[ + [ + { + NodeId.head_l: MoveGroupSingleAxisStep( + distance_mm=float64(0), + velocity_mm_sec=float64(-speed), + duration_sec=float64(100), + stop_condition=MoveStopCondition.limit_switch, + ) + } + ] + ] + ) + return home_z + +def home_gantry_xy(): + speed = 20 + home_z = MoveGroupRunner( + move_groups=[ + [ + { + NodeId.gantry_x: MoveGroupSingleAxisStep( + distance_mm=float64(0), + velocity_mm_sec=float64(-speed), + duration_sec=float64(100), + stop_condition=MoveStopCondition.limit_switch, + ) + } + ], + [ + { + NodeId.gantry_y: MoveGroupSingleAxisStep( + distance_mm=float64(0), + velocity_mm_sec=float64(-speed), + duration_sec=float64(100), + stop_condition=MoveStopCondition.limit_switch, + ) + } + ], + ] + ) + return home_z + +def home_plunger(): + pipette_node = NodeId.pipette_left + home_plunger_runner = MoveGroupRunner( + move_groups=[ + [ + create_home_step( + {pipette_node: float64(100.0)}, + {pipette_node: float64(-5)} + ) + ] + ] + ) + return home_plunger_runner + +def getch(): + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + return _getch() + +async def _jog_axis(messenger: CanMessenger , position) -> None: + step_size = [0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + x_speed = 30 + y_speed = 30 + z_speed = 10.5 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> ESC << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == 'a': + # minus x direction + sys.stdout.flush() + x_pos = position[OT3Axis.X.name] + step + position.update({OT3Axis.X.name: x_pos}) + x_move = move_x_axis(step, x_speed) + await x_move.run(can_messenger = messenger) + + elif input == 'd': + #plus x direction + sys.stdout.flush() + x_pos = position[OT3Axis.X.name] - step + # position[OT3Axis.X.name] = position[OT3Axis.X.name] - x_pos + position.update({OT3Axis.X.name: x_pos}) + x_move = move_x_axis(step, -x_speed) + await x_move.run(can_messenger = messenger) + + elif input == 'w': + #minus y direction + sys.stdout.flush() + y_pos = position[OT3Axis.Y.name] - step + # position[OT3Axis.Y.name] = position[OT3Axis.Y.name]- y_pos + position.update({OT3Axis.Y.name: y_pos}) + y_move = move_y_axis(step, -y_speed) + await y_move.run(can_messenger = messenger) + + elif input == 's': + #plus y direction + sys.stdout.flush() + y_pos = position[OT3Axis.Y.name] + step + # position[OT3Axis.Y.name] = position[OT3Axis.Y.name] + y_pos + position.update({OT3Axis.Y.name: y_pos}) + y_move = move_y_axis(step, y_speed) + await y_move.run(can_messenger = messenger) + + elif input == 'i': + sys.stdout.flush() + z_pos = position[OT3Axis.Z_L.name] - step + # position[OT3Axis.Z_L.name] = position[OT3Axis.Z_L.name] - z_pos + position.update({OT3Axis.Z_L.name: z_pos}) + z_move = move_z_axis(step, -z_speed) + await z_move.run(can_messenger = messenger) + + elif input == 'k': + sys.stdout.flush() + z_pos = position[OT3Axis.Z_L.name] + step + # position[OT3Axis.Z_L.name] = position[OT3Axis.Z_L.name] + z_pos + position.update({OT3Axis.Z_L.name: z_pos}) + z_move = move_z_axis(step, z_speed) + await z_move.run(can_messenger = messenger) + + elif input == '+': + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 6: + step_length_index = 6 + step = step_size[step_length_index] + + elif input == '-': + sys.stdout.flush() + step_length_index = step_length_index -1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == 'q': + sys.stdout.flush() + quit() + + elif input == '\r': + sys.stdout.flush() + print('\r\n') + return position + print("Coordinates: X: {} Y: {} Z: {}".format( + round(position[OT3Axis.X.name],2), + round(position[OT3Axis.Y.name],2), + round(position[OT3Axis.Z_L.name],2)), + " Motor Step: ", + step_size[step_length_index], + end='') + print('\r', end='') + +class OT3Axis(enum.Enum): + X = 0 # gantry + Y = 1 + Z_L = 2 # left pipette mount Z + Z_R = 3 # right pipette mount Z + Z_G = 4 # gripper mount Z + P_L = 5 # left pipette plunger + P_R = 6 # right pipette plunger + Q = 7 # hi-throughput pipette tiprack grab + G = 8 # gripper grab + +class position_tracker: + def __init__(self): + self.current_position = {OT3Axis.X.name: 0, + OT3Axis.Y.name: 0, + OT3Axis.Z_L.name: 0} + + def current_position(self): + return self.current_position + + def update_position(self, new_position): + self.current_position.update(new_position) + + +async def run(args: argparse.Namespace) -> None: + """Entry point for script.""" + today = datetime.date.today() + file_name = '/var/96_pickup_test_{}.csv'.format(today.strftime("%b-%d-%Y")) + print("Test tip pick up for the 96 channel\n") + # reps = prompt_int_input("Number of repetitions for pick up and drop tip") + # delay = prompt_int_input("Delay in seconds between pick up and drop tip") + slot_loc = {"A1": (13.42 , 394.92,110), "A2": (177.32 , 394.92,110), "A3": (341.03 , 394.92,110), + "B1": (13.42, 288.42 , 110), "B2": (177.32 , 288.92 ,110), "B3": (341.03, 288.92,110), + "C1": (13.42, 181.92, 110), "C2": (177.32, 181.92,110), "C3": (341.03, 181.92,110), + "D1": (13.42, 75.5, 110), "D2": (177.32, 75.5,110), "D3": (341.03, 75.5,110)} + default_speed = {OT3Axis.X.name: 200, + OT3Axis.Y.name: 200, + OT3Axis.Z_L.name: 10} + delay = 2 + # 96 channel can only be mounted to the left + pipette_node = NodeId.pipette_left + driver = await build_driver(build_settings(args)) + messenger = CanMessenger(driver=driver) + messenger.start() + # await messenger.send(node_id=NodeId.broadcast, message=EnableMotorRequest()) + z_speed = 20 + gantry_speed = 30 + pick_up_speed = 5 + grap_speed = 5.5 + grap_distance = 19 + drop_speed = 5.5 + drop_distance = 27 + pick_up_distance = 12 + trough_calibrate = True + press = False + retract_position = 80 + trials = 10 + # grab_tips = move_pipette_mechanism(grap_distance, grap_speed) + # drop_tips = move_pipette_mechanism(drop_distance, drop_speed) + # home_jaw = home_pipette_jaw() + home_z = home_z_axis() + home_gantry = home_gantry_xy() + home_pipette = home_plunger() + position = {OT3Axis.X.name: slot_loc[args.slot][0], + OT3Axis.Y.name: slot_loc[args.slot][0], + OT3Axis.Z_L.name: 0} + origin = {OT3Axis.X.name: 0, OT3Axis.Y.name: 0, OT3Axis.Z_L.name: 0} + try: + for t in range(1, trials+1): + await home_z.run(can_messenger = messenger) + await home_gantry.run(can_messenger = messenger) + if t <= 1: + await move_to(origin, position, gantry_speed).run(can_messenger = messenger) + tiprack_loc = await _jog_axis(messenger, position) + position.update(tiprack_loc) + else: + gantry_move = {OT3Axis.X.name: tiprack_loc[OT3Axis.X.name], + OT3Axis.Y.name: tiprack_loc[OT3Axis.Y.name], + OT3Axis.Z_L.name: 0} + await move_to(origin, gantry_move, gantry_speed).run(can_messenger = messenger) + z_move = {OT3Axis.X.name: 0, + OT3Axis.Y.name: 0, + OT3Axis.Z_L.name: tiprack_loc[OT3Axis.Z_L.name]} + await move_to(origin, z_move, default_speed[OT3Axis.Z_L.name]).run(can_messenger = messenger) + position.update(tiprack_loc) + input_current = float(input("Enter Current: ")) + await set_current(messenger, input_current, NodeId.head_l) + await hold_current(messenger, 1.4, NodeId.gantry_y) + await hold_current(messenger, 1.4, NodeId.gantry_x) + await move_z_axis(pick_up_distance, pick_up_speed).run(can_messenger = messenger) + position.update({OT3Axis.Z_L.name: position[OT3Axis.Z_L.name] + pick_up_distance}) + # position[OT3Axis.Z_L.name] = position[OT3Axis.Z_L.name] + pick_up_distance + await asyncio.sleep(delay) + await set_current(messenger, 1.4, NodeId.head_l) + await home_z.run(can_messenger = messenger) + position.update({OT3Axis.Z_L.name: 0}) + if press: + slower_speed = 1 + await set_current(messenger, 1.5, NodeId.head_l) + await move_z_axis(5, -slower_speed).run(can_messenger = messenger) + position[OT3Axis.Z_L.name] = position[OT3Axis.Z_L.name] + pick_up_distance + await asyncio.sleep(delay) + await set_current(messenger, input_current+0.05, NodeId.head_l) + await hold_current(messenger, 1.4, NodeId.gantry_y) + await hold_current(messenger, 1.4, NodeId.gantry_x) + await move_z_axis(5+2, slower_speed).run(can_messenger = messenger) + position[OT3Axis.Z_L.name] = position[OT3Axis.Z_L.name] + pick_up_distance + await asyncio.sleep(delay) + if t <= 1: + measure_loc = await _jog_axis(messenger, position) + position.update(measure_loc) + await asyncio.sleep(1) + if args.dial_indicator: + pass + await asyncio.sleep(1) + # Take Reading + else: + await move_z_axis(measure_loc[OT3Axis.Z_L.name], default_speed[OT3Axis.Z_L.name]).run(can_messenger = messenger) + await asyncio.sleep(1) + if args.dial_indicator: + read_1 = gauge_1.read() + read_2 = gauge_2.read() + await asyncio.sleep(0.5) + # Take Reading + await home_z.run(can_messenger = messenger) + input("Press Enter to Move to Tiprack") + + except asyncio.CancelledError: + pass + finally: + print("\nTesting finishes...\n") + await messenger.stop() + driver.shutdown() + + +def main() -> None: + """Entry point.""" + slot_locs = ["A1", "A2", "A3", + "B1", "B2", "B3:", + "C1", "C2", "C3", + "D1", "D2", "D3"] + parser = argparse.ArgumentParser( + description="96 channel tip handling testing script." + ) + add_can_args(parser) + parser.add_argument("--dial_indicator", action="store_true") + parser.add_argument("--calibrate", action="store_true") + parser.add_argument("--slot", type=str, choices=slot_locs, default="B2") + args = parser.parse_args() + if args.dial_indicator: + gauge_1 = Mitutoyo_Digimatic_Indicator(port='/dev/ttyUSB0') + gauge_2 = Mitutoyo_Digimatic_Indicator(port='/dev/ttyUSB1') + gauge_1.connect() + gauge_2.connect() + test_n , test_f = file_setup(test_data) + + asyncio.run(run(args)) + + +if __name__ == "__main__": + main() From 28c0adba9087001c57cf9a4297ed929a9e49c7ff Mon Sep 17 00:00:00 2001 From: Carlos Date: Fri, 3 Mar 2023 13:14:06 -0500 Subject: [PATCH 07/53] move the homing --- .../scripts/96_pipette_partial_pick_up.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hardware/opentrons_hardware/scripts/96_pipette_partial_pick_up.py b/hardware/opentrons_hardware/scripts/96_pipette_partial_pick_up.py index 0a486c88c8b..82c7e7b5001 100644 --- a/hardware/opentrons_hardware/scripts/96_pipette_partial_pick_up.py +++ b/hardware/opentrons_hardware/scripts/96_pipette_partial_pick_up.py @@ -573,9 +573,6 @@ async def run(args: argparse.Namespace) -> None: position.update({OT3Axis.Z_L.name: position[OT3Axis.Z_L.name] + pick_up_distance}) # position[OT3Axis.Z_L.name] = position[OT3Axis.Z_L.name] + pick_up_distance await asyncio.sleep(delay) - await set_current(messenger, 1.4, NodeId.head_l) - await home_z.run(can_messenger = messenger) - position.update({OT3Axis.Z_L.name: 0}) if press: slower_speed = 1 await set_current(messenger, 1.5, NodeId.head_l) @@ -588,6 +585,9 @@ async def run(args: argparse.Namespace) -> None: await move_z_axis(5+2, slower_speed).run(can_messenger = messenger) position[OT3Axis.Z_L.name] = position[OT3Axis.Z_L.name] + pick_up_distance await asyncio.sleep(delay) + await set_current(messenger, 1.4, NodeId.head_l) + await home_z.run(can_messenger = messenger) + position.update({OT3Axis.Z_L.name: 0}) if t <= 1: measure_loc = await _jog_axis(messenger, position) position.update(measure_loc) From ca461fda9fbf1db399f972c6c9eaa60623d9c9a4 Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 13 Mar 2023 12:55:48 -0400 Subject: [PATCH 08/53] adding some changes to pick tip testing --- .../hardware_testing/scripts/pick_up_tip.py | 719 ++++++++++-------- .../scripts/pick_up_tip_testing.py | 519 +++++++------ 2 files changed, 720 insertions(+), 518 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/pick_up_tip.py b/hardware-testing/hardware_testing/scripts/pick_up_tip.py index 7db951a859b..0c001da7572 100644 --- a/hardware-testing/hardware_testing/scripts/pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/pick_up_tip.py @@ -10,32 +10,25 @@ import os from opentrons.hardware_control.motion_utilities import target_position_from_plunger -from hardware_testing.opentrons_api.types import GantryLoad, OT3Mount, OT3Axis, Point, Axis +from hardware_testing.opentrons_api.types import ( + OT3Mount, + OT3Axis, + Point, +) from hardware_testing.opentrons_api.helpers_ot3 import ( - OT3API, build_async_ot3_hardware_api, - GantryLoadSettings, - set_gantry_load_per_axis_settings_ot3, home_ot3, - get_endstop_position_ot3, move_plunger_absolute_ot3, + move_plunger_relative_ot3, update_pick_up_current, - update_pick_up_distance + update_pick_up_distance, ) from hardware_testing import data from hardware_testing.drivers.mark10 import Mark10 +from hardware_testing.drivers import mitutoyo_digimatic_indicator -MOUNT = OT3Mount.LEFT -PIPETTE_SPEED = 10 - -SPEED_XY = 500 -SPEED_Z = 250 - -pick_up_speed = 5 -press_distance = 15 aspirate_depth = 7 -volume = 50 liquid_retract_dist = 12 liquid_retract_speed = 5 retract_dist = 100 @@ -43,135 +36,133 @@ leak_test_time = 30 -def _create_relative_point(axis: OT3Axis, distance: float) -> Point: - if axis == OT3Axis.X: - return Point(x=distance) - elif axis == OT3Axis.Y: - return Point(y=distance) - elif axis == OT3Axis.Z_L or axis == OT3Axis.Z_R: - return Point(z=distance) - raise ValueError(f"Unexpected axis: {axis}") - -async def get_encoder_position( - api: OT3API, mount: OT3Mount) -> Dict[Axis, float]: - enc_position = await api.encoder_current_position(mount=MOUNT, refresh=True) - return enc_position - -async def jog(api: OT3API)-> Dict[OT3Axis, float]: - motion = True - cur_pos = await api.current_position_ot3(MOUNT) - print(f"X: {cur_pos[OT3Axis.X]}, Y: {cur_pos[OT3Axis.Y]}, Z: {cur_pos[OT3Axis.by_mount(MOUNT)]}") - print(f"Enter coordinates as example: 100,10,3") - while motion: - coord = ast.literal_eval(input('Enter Coordinates as: ')) - print(f"Tuple type: {isinstance(coord, Tuple)}") - if isinstance(coord, Tuple): - await api.move_to(MOUNT, Point(coord[0], coord[1], coord[2]), speed=65) - cur_pos = await api.current_position_ot3(MOUNT) - print(f"X: {cur_pos[OT3Axis.X]}, Y: {cur_pos[OT3Axis.Y]}, Z: {cur_pos[OT3Axis.by_mount(MOUNT)]}") - else: - motion = False - return await api.current_position_ot3(MOUNT) - -async def set_default_current_settings(api: OT3API, load: Optional[GantryLoad] = None): - default_run_settings = { - OT3Axis.X: GantryLoadSettings( - max_speed=SPEED_XY, - acceleration=2000, - max_start_stop_speed=10, - max_change_dir_speed=5, - hold_current=0.1, - run_current=1.4, - ), - OT3Axis.Y: GantryLoadSettings( - max_speed=SPEED_XY, - acceleration=2000, - max_start_stop_speed=10, - max_change_dir_speed=5, - hold_current=0.1, - run_current=1.4, - ), - OT3Axis.Z_L: GantryLoadSettings( - max_speed=SPEED_Z, - acceleration=1500, - max_start_stop_speed=10, - max_change_dir_speed=5, - hold_current=0.1, - run_current=1.4, - ), - OT3Axis.Z_R: GantryLoadSettings( - max_speed=SPEED_Z, - acceleration=1500, - max_start_stop_speed=10, - max_change_dir_speed=5, - hold_current=0.1, - run_current=1.4, - ), - } - await set_gantry_load_per_axis_settings_ot3(api, - default_run_settings, - load=None - ) - -async def set_current_settings(api: OT3API, motor_current: float, load: Optional[GantryLoad] = None): - z_pickup_run_settings = { - OT3Axis.X: GantryLoadSettings( - max_speed=SPEED_XY, - acceleration=2000, - max_start_stop_speed=0, - max_change_dir_speed=0, - hold_current=0.5, - run_current=1.4, - ), - OT3Axis.Y: GantryLoadSettings( - max_speed=SPEED_XY, - acceleration=2000, - max_start_stop_speed=0, - max_change_dir_speed=0, - hold_current=0.5, - run_current=1.4, - ), - OT3Axis.Z_L: GantryLoadSettings( - max_speed=SPEED_Z, - acceleration=1500, - max_start_stop_speed=0, - max_change_dir_speed=0, - hold_current=0.1, - run_current=motor_current, - ), - OT3Axis.Z_R: GantryLoadSettings( - max_speed=SPEED_Z, - acceleration=1500, - max_start_stop_speed=0, - max_change_dir_speed=0, - hold_current=0.1, - run_current=motor_current, - ), - } - await set_gantry_load_per_axis_settings_ot3(api, - z_pickup_run_settings, - load=None) - -async def pick_up_function(api: OT3API, - loc, speed, press_distance): - # Pick up tip function - await api.move_to(MOUNT, - Point(loc[0], loc[1], loc_[2]-press_distance), - speed = speed) - -async def update_tip_spec(api, action): - if action == 'pickup': - realmount = OT3Mount.from_mount(MOUNT) - spec, _add_tip_to_instrs = api._pipette_handler.plan_check_pick_up_tip( - realmount, 78.5, None, None +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + + +def file_setup(test_data, details, pipette_model): + today = datetime.date.today() + test_name = "{}-LSD-Z-{}-P-{}-Threshold-{}".format( + details[0], # Pipette model + details[1], # mount_speed + details[2], # plunger_speed + details[3], + ) # sensor threshold + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + + +def dial_indicator_setup(): + gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator(port='/dev/ttyUSB0') + gauge.connect() + return gauge + +async def _jog_axis(api, position) -> Dict[OT3Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3(mount, refresh=True) + return position + position = await api.current_position_ot3(mount, refresh=True) + + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", ) - _add_tip_to_instrs() - elif action == 'drop_tip': - realmount = OT3Mount.from_mount(MOUNT) - spec, _remove = self._pipette_handler.plan_check_drop_tip(realmount, home_after) - _remove() - else: - raise("Pass a pickup or drop_tip to function") + print("\r", end="") + async def countdown(count_time: float): """ @@ -180,31 +171,46 @@ async def countdown(count_time: float): time_suspend = 0 while time_suspend < count_time: await asyncio.sleep(1) - time_suspend +=1 - print(f"Remaining: {count_time-time_suspend} (s)", end='') - print('\r', end='') - print('') + time_suspend += 1 + print(f"Remaining: {count_time-time_suspend} (s)", end="") + print("\r", end="") + print("") + async def _main() -> None: - hw_api = await build_async_ot3_hardware_api(is_simulating=args.simulate, - use_defaults=True) - await set_default_current_settings(hw_api, load=None) + today = datetime.date.today() + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.92, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + tip_length = {"T1K": 85.7, "T50": 57.9} + pipette_model = hw_api._pipette_handler.hardware_instruments[mount] await home_ot3(hw_api, [OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) - home_position = await hw_api.current_position_ot3(MOUNT) - await hw_api.cache_instruments() + await hw_api.home_plunger(mount) + home_position = await hw_api.current_position_ot3(mount) global encoder_position global encoder_end global stop_threads global motion encoder_end = None - trash_loc = () - await hw_api.home_plunger(MOUNT) - # fg_loc = X: 186.0, Y: 34.0, Z: 125.0 - # if args.fg_jog: - # print("Move to Force Gauge") - # fg_loc = await jog(hw_api) - # fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(MOUNT)]] - # await hw_api.home_z(MOUNT, allow_home_other = False) + if args.fg_jog: + print("Move to Force Gauge") + fg_loc = await jog(hw_api) + fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(MOUNT)]] + await hw_api.home_z(MOUNT, allow_home_other=False) # fg_loc = [-4.0, 87.25, 410.0] # 96 Pipette Channel # fg_loc = [186.0, 34.0, 125.0] # fg_loc = [22.5 , 34, 125] # P1KS Coordinate @@ -217,9 +223,9 @@ async def _main() -> None: # tiprack_loc = [tiprack_loc[OT3Axis.X], tiprack_loc[OT3Axis.Y], tiprack_loc[OT3Axis.by_mount(MOUNT)]] # await hw_api.home_z(MOUNT, allow_home_other = False) tiprack_loc = [135.7, 63.1, 80.0] - #tiprack_loc = [136.5, 63.3, 78.0] # Oolong + # tiprack_loc = [136.5, 63.3, 78.0] # Oolong # tiprack_loc = [136.5, 62.6, 80.0] #Mr T robot - #tiprack_loc = [157.25, 84.25, 366.0] # 96 Channel + # tiprack_loc = [157.25, 84.25, 366.0] # 96 Channel # tiprack_loc = [136.0, 62.3, 80] # P1K Single Channel # tiprack_loc = [138.0, 61.5, 80.0] # 1KS Multi Channel, P50M if args.trough: @@ -229,133 +235,198 @@ async def _main() -> None: # trough_loc = [trough_loc[OT3Axis.X], trough_loc[OT3Axis.Y], trough_loc[OT3Axis.by_mount(MOUNT)]] # await hw_api.home_z(MOUNT, allow_home_other = False) # await hw_api.remove_tip(MOUNT) - trough_loc=[310.0, 40.0, -8.5] - #trough_loc = [310.0, 40.0, 24.0] + trough_loc = [310.0, 40.0, -8.5] + # trough_loc = [310.0, 40.0, 24.0] # trough_loc = [299.5, 40.0, 30.0] # Mr T # trough_loc = [301.5, 61.5, 24.0] # P1K Multi Channel Coord # trough_loc = [301.5, 61.5, -10.0] # P50 Multi Channel coord # trough_loc = [299.0, 40.0, 30.0] # P1KS Coord # trough_loc = [300, 40, 85-78.5] - #X: 300.0, Y: 40.0, Z: 85.0 + # X: 300.0, Y: 40.0, Z: 85.0 + lp_file_name = "/var/pressure_sensor_data_P-{}_Z-{}-{}.csv".format( + args.plunger_speed, args.mount_speed, today.strftime("%b-%d-%Y") + ) + liquid_probe_settings = LiquidProbeSettings( + max_z_distance=args.max_z_distance, + min_z_distance=args.min_z_distance, + mount_speed=args.mount_speed, + plunger_speed=args.plunger_speed, + sensor_threshold_pascals=args.sensor_threshold, + expected_liquid_height=args.expected_liquid_height, + log_pressure=args.log_pressure, + aspirate_while_sensing=True, + data_file=lp_file_name, + ) try: while True: # #-----------------------Force Gauge----------------------------------- - # await set_default_current_settings(hw_api, load=None) - # cur_pos = await hw_api.current_position_ot3(MOUNT) - # z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] + # Set Motor current by tester m_current = float(input("motor_current in amps: ")) - # await hw_api.move_to(MOUNT, Point(fg_loc[0], fg_loc[1], z_pos)) + await hw_api.move_to( + mount, + Point(fg_loc[0], fg_loc[1], home_position[OT3Axis.by_mount(mount)]), + ) # # Move pipette to Force Gauge calibrated location - # await hw_api.move_to(MOUNT, Point(fg_loc[0], fg_loc[1], fg_loc[2]), speed = 65) - # - # encoder_position = await get_encoder_position(hw_api, MOUNT) - # encoder_position = encoder_position[Axis.Z] - # location = 'Force_Gauge' - # force_thread = Thread(target=force_record, args=(m_current, location, )) - # force_thread.start() - # await set_current_settings(hw_api, m_current) - # # Move pipette to Force Gauge press location - # await hw_api.move_to(MOUNT, - # Point(fg_loc[0], fg_loc[1], fg_loc[2] - press_distance), - # speed = pick_up_speed) - # await asyncio.sleep(2) - # encoder_position = await get_encoder_position(hw_api, MOUNT) - # encoder_position = encoder_position[Axis.Z] - # print(encoder_position) - # motion = False - # stop_threads = True - # force_thread.join() #Thread Finished - # await set_default_current_settings(hw_api, load=None) - # await hw_api.home_z(MOUNT, allow_home_other = False) - - #-----------------------Tiprack------------------------------------ - # Obtain the current position of the Z mount - cur_pos = await hw_api.current_position_ot3(MOUNT) - z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] + await hw_api.move_to(mount, Point(fg_loc[0], fg_loc[1], fg_loc[2])) + init_fg_loc = await encoder_current_position_ot3(mount, CriticalPoint.NONE)) + init_fg_loc = encoder_position[OT3Axis.by_mount(mount)] + location = "Force_Gauge" + force_thread = Thread( + target=force_record, + args=( + m_current, + location, + ), + ) + force_thread.start() + await update_pick_up_current(hw_api, mount, m_current) + # Move pipette to Force Gauge press location + await pick_up_tip(mount, tip_length=tip_length[args.tip_size]) + home_with_tip = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + await asyncio.sleep(2) + final_fg_loc = await encoder_current_position_ot3(mount, CriticalPoint.NONE) + final_fg_loc = encoder_position[OT3Axis.by_mount(mount)] + print(encoder_position) + motion = False + stop_threads = True + force_thread.join() # Thread Finished + await remove_tip(mount) + await hw_api.home_z(mount, allow_home_other=False) + + # -----------------------Tiprack------------------------------------ # Move over to the TipRack location and - await hw_api.move_to(MOUNT, Point(tiprack_loc[0], tiprack_loc[1], z_pos), speed = 120) + await hw_api.move_to( + mount, + Point( + tiprack_loc[0], tiprack_loc[1], home_pos[OT3Axis.by_mount(mount)] + ), + ) - await set_default_current_settings(hw_api, load=None) # Move Pipette to top of Tip Rack Location - await hw_api.move_to(MOUNT, Point(tiprack_loc[0], tiprack_loc[1], tiprack_loc[2]), speed = 65) - location = 'Tiprack' + await hw_api.move_to( + MOUNT, Point(tiprack_loc[0], tiprack_loc[1], tiprack_loc[2]), speed=65 + ) + location = "Tiprack" # Start recording the encoder - encoder_position = await get_encoder_position(hw_api, MOUNT) - print(f"Start encoder: {encoder_position}") - encoder_position = encoder_position[Axis.Z] - # enc_thread = Thread(target=force_record, args=(m_current,location,)) - # enc_thread.start() + init_tip_loc = await encoder_current_position_ot3(mount, CriticalPoint.NONE) + print(f"Start encoder: {init_tip_loc}") + init_tip_loc = encoder_position[OT3Axis.by_mount(mount)] + enc_thread = Thread( + target=force_record, + args=( + m_current, + location, + ), + ) + enc_thread.start() # Press Pipette into the tip - await set_current_settings(hw_api, m_current) - await hw_api.move_to(MOUNT, - Point(tiprack_loc[0], - tiprack_loc[1], - tiprack_loc[2]-press_distance), - speed = pick_up_speed - ) - await hw_api.add_tip(MOUNT, 58.5) + await update_pick_up_current(hw_api, mount, m_current) + # Move pipette to Force Gauge press location + await pick_up_tip(mount, tip_length=tip_length[args.tip_size]) + home_with_tip = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) await asyncio.sleep(2) - encoder_end = await get_encoder_position(hw_api, MOUNT) + final_tip_loc = await encoder_current_position_ot3(mount, CriticalPoint.NONE) print(f"End Encoder: {encoder_end}") - encoder_end = encoder_end[Axis.Z] - # stop_threads = True - # enc_thread.join() #Thread Finished - # Reset Current Settings - await set_default_current_settings(hw_api, load=None) + final_tip_loc = encoder_position[OT3Axis.by_mount(mount)] + stop_threads = True + enc_thread.join() # Thread Finished # Home Z - await hw_api.home([OT3Axis.by_mount(MOUNT)]) + await hw_api.home([OT3Axis.by_mount(mount)]) input("Feel the Tip") + # -----------------------Aspirate----------------------------------- + await hw_api.move_to( + mount, + Point(trough_loc[0], trough_loc[1], home_pos[OT3Axis.by_mount(mount)]), + ) + # Move to offset from trough + await hw_api.move_to( + mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) + ) - #-----------------------Aspirate----------------------------------- - cur_pos = await hw_api.current_position_ot3(MOUNT) - z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] - await hw_api.move_to(MOUNT, Point(trough_loc[0], trough_loc[1], z_pos), speed = 120) - await hw_api.move_to(MOUNT, Point(trough_loc[0], trough_loc[1], trough_loc[2]+10), speed = 65) - await hw_api.move_to(MOUNT, Point(trough_loc[0], trough_loc[1], trough_loc[2]-aspirate_depth), speed = 1) - await hw_api.prepare_for_aspirate(MOUNT) - await hw_api.aspirate(MOUNT) - - cur_pos = await hw_api.current_position_ot3(MOUNT) + # Prepare to aspirate before descending to trough well + await hw_api.prepare_for_aspirate(mount) + # Liquid Probe + await hw_api.liquid_probe(mount, probe_settings=liquid_probe_settings) + liquid_height = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + # await move_plunger_relative_ot3(hw_api, mount, 1.5, None, speed = 2) # P50S + await move_plunger_relative_ot3(hw_api, mount, 0.25, None, speed=2) # P1KS + await hw_api.move_to( + mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) + ) + # Descend to aspirate depth + await hw_api.move_to( + mount, + Point( + liquid_height[OT3Axis.X], + liquid_height[OT3Axis.Y], + liquid_height[OT3Axis.by_mount(mount)] - aspirate_depth, + ), + speed=5, + critical_point=CriticalPoint.TIP, + ) + # Aspirate + await hw_api.aspirate(mount) + cur_pos = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] - await hw_api.move_to(MOUNT, - Point(trough_loc[0], - trough_loc[1], - z_pos+liquid_retract_dist), - speed = liquid_retract_speed) - cur_pos = await hw_api.current_position_ot3(MOUNT) - z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] - await hw_api.move_to(MOUNT, - Point(trough_loc[0], - trough_loc[1], - z_pos+retract_dist), - speed = retract_speed) - await hw_api.home_z(MOUNT, allow_home_other = False) - await countdown(count_time = leak_test_time) + # Retract from liquid with retract speed + await hw_api.move_to( + mount, + Point(trough_loc[0], trough_loc[1], z_pos + liquid_retract_dist), + speed=liquid_retract_speed, + critical_point=CriticalPoint.TIP, + ) + await hw_api.move_to( + MOUNT, + Point( + trough_loc[0], trough_loc[1], home_with_tip[OT3Axis.by_mount(mount)] + ), + critical_point=CriticalPoint.TIP, + ) + await countdown(count_time=leak_test_time) input("Check to see if the pipette is leaking") - await hw_api.move_to(MOUNT, Point(trough_loc[0], - trough_loc[1], - trough_loc[2]+10), - speed = 65) - await hw_api.move_to(MOUNT, Point(trough_loc[0], - trough_loc[1], - trough_loc[2]-aspirate_depth), - speed = 5) - await hw_api.dispense(MOUNT) - # await hw_api.blow_out(MOUNT) - #--------------------Drop Tip-------------------------------------- - await hw_api.home_z(MOUNT, allow_home_other = False) - cur_pos = await hw_api.current_position_ot3(MOUNT) - z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] - await hw_api.move_to(MOUNT, Point(home_position[OT3Axis.X]-70, - home_position[OT3Axis.Y]-50, z_pos), - speed = 120) - cur_pos = await hw_api.current_position_ot3(MOUNT) - z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] - await hw_api.move_to(MOUNT, Point(cur_pos[OT3Axis.X], - cur_pos[OT3Axis.Y], z_pos-150), speed = 65) - await hw_api.drop_tip(MOUNT) - await hw_api.remove_tip(MOUNT) - await hw_api.home_z(MOUNT, allow_home_other = False) + await hw_api.move_to( + mount, + Point(trough_loc[0], trough_loc[1], trough_loc[2]), + critical_point=CriticalPoint.TIP, + ) + await hw_api.move_to( + mount, + Point(trough_loc[0], trough_loc[1], trough_loc[2] - aspirate_depth), + speed=5, + critical_point=CriticalPoint.TIP, + ) + await hw_api.dispense(mount) + await hw_api.blow_out(mount) + # --------------------Drop Tip-------------------------------------- + current_position = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + await hw_api.move_to( + mount, + Point( + trough_loc[0], trough_loc[1], home_with_tip[OT3Axis.by_mount(mount)] + ), + critical_point=CriticalPoint.TIP, + ) + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1] - 20, + home_with_tip[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + await hw_api.drop_tip(mount) await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) except KeyboardInterrupt: @@ -372,16 +443,21 @@ def force_record(motor_current, location): global stop_threads global motion encoder_end = None - file_name = "/results/force_pu_test_%s-%s-%s.csv" %(motor_current, - datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), - location) - print(dir+file_name) - with open(dir+file_name, 'w', newline='') as f: - test_data = {'Time(s)':None, - 'Force(N)':None, - 'M_current(amps)':None, - 'encoder_pos(mm)': None, - 'end_enc_pos(mm)': None} + file_name = "/results/force_pu_test_%s-%s-%s.csv" % ( + motor_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + location, + ) + print(dir + file_name) + with open(dir + file_name, "w", newline="") as f: + test_data = { + "Time(s)": None, + "Force(N)": None, + "M_current(amps)": None, + "encoder_pos(mm)": None, + "end_enc_pos(mm)": None, + "pipette_model": None, + } log_file = csv.DictWriter(f, test_data) log_file.writeheader() start_time = time.perf_counter() @@ -390,11 +466,11 @@ def force_record(motor_current, location): stop_threads = False while motion: reading = float(fg.read_force()) - test_data['Time(s)'] = (time.perf_counter()-start_time) - test_data['Force(N)'] = reading - test_data['M_current(amps)'] = motor_current - test_data['encoder_pos(mm)'] = encoder_position - test_data['end_enc_pos(mm)'] = encoder_end + test_data["Time(s)"] = time.perf_counter() - start_time + test_data["Force(N)"] = reading + test_data["M_current(amps)"] = motor_current + test_data["encoder_pos(mm)"] = encoder_position + test_data["end_enc_pos(mm)"] = encoder_end log_file.writerow(test_data) print(test_data) f.flush() @@ -402,17 +478,18 @@ def force_record(motor_current, location): break except KeyboardInterrupt: print("Test Cancelled") - test_data['Errors'] = "Test Cancelled" + test_data["Errors"] = "Test Cancelled" f.flush() except Exception as e: print("ERROR OCCURED") - test_data['Errors'] = e + test_data["Errors"] = e f.flush() raise e print("Test done") f.flush() f.close() + def enc_record(motor_current, location): dir = os.getcwd() global encoder_position @@ -420,15 +497,15 @@ def enc_record(motor_current, location): global stop_threads global motion encoder_end = None - file_name = "/results/enc_pu_test_%s-%s-%s.csv" %(motor_current, - datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), - location) + file_name = "/results/enc_pu_test_%s-%s-%s.csv" % ( + motor_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + location, + ) print(file_name) - print(dir+file_name) - with open(dir+file_name, 'wb', newline='') as f: - test_data = {'time(s)':None, - 'start_enc_pos':None, - 'end_enc_pos(mm)': None} + print(dir + file_name) + with open(dir + file_name, "wb", newline="") as f: + test_data = {"time(s)": None, "start_enc_pos": None, "end_enc_pos(mm)": None} log_file = csv.DictWriter(f, test_data) log_file.writeheader() start_time = time.perf_counter() @@ -436,9 +513,9 @@ def enc_record(motor_current, location): motion = True stop_threads = False while motion: - test_data['time(s)'] = (time.perf_counter()-start_time) - test_data['start_enc_pos(mm)'] = encoder_position - test_data['end_enc_pos(mm)'] = encoder_end + test_data["time(s)"] = time.perf_counter() - start_time + test_data["start_enc_pos(mm)"] = encoder_position + test_data["end_enc_pos(mm)"] = encoder_end log_file.writerow(test_data) print(test_data) f.flush() @@ -446,29 +523,65 @@ def enc_record(motor_current, location): break except KeyboardInterrupt: print("Test Cancelled") - test_data['Errors'] = "Test Cancelled" + test_data["Errors"] = "Test Cancelled" f.flush() except Exception as e: print("ERROR OCCURED") - test_data['Errors'] = e + test_data["Errors"] = e f.flush() raise e print("Test done") f.flush() f.close() + if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] parser = argparse.ArgumentParser() parser.add_argument("--simulate", action="store_true") parser.add_argument("--fg_jog", action="store_true") parser.add_argument("--trough", action="store_true") parser.add_argument("--tiprack", action="store_true") - parser.add_argument("--cycles", type=int, - default = 1000, help = "Number of Cycles to run") - parser.add_argument("--port", type=str, - default = '/dev/ttyUSB0', help = "Force Gauge Port") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C2") + parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") + parser.add_argument("--fg", action="store_true", default=True) + parser.add_argument("--tip_size", type=str, default="T50", help="Tip Size") + parser.add_argument("--max_z_distance", type=float, default=40) + parser.add_argument("--min_z_distance", type=float, default=5) + parser.add_argument("--mount_speed", type=float, default=5) + parser.add_argument("--plunger_speed", type=float, default=25) + parser.add_argument( + "--sensor_threshold", type=float, default=160, help="Threshold in Pascals" + ) + parser.add_argument("--expected_liquid_height", type=int, default=0) + parser.add_argument( + "--port", type=str, default="/dev/ttyUSB0", help="Force Gauge Port" + ) args = parser.parse_args() + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + if args.fg: + fg = Mark10.create(port=args.port) + fg.connect() - # fg = Mark10.create(port=args.port) - # fg.connect() + if args.dial_indicator: + gauge = dial_indicator_setup() + test_n , test_f = file_setup(test_data, details) asyncio.run(_main()) diff --git a/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py b/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py index 533d253b955..f8d846de7f7 100644 --- a/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py +++ b/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py @@ -6,7 +6,13 @@ import sys, tty, os, time from opentrons.hardware_control.motion_utilities import target_position_from_plunger -from hardware_testing.opentrons_api.types import GantryLoad, OT3Mount, OT3Axis, Point, Axis +from hardware_testing.opentrons_api.types import ( + GantryLoad, + OT3Mount, + OT3Axis, + Point, + Axis, +) from hardware_testing.opentrons_api.helpers_ot3 import ( OT3API, build_async_ot3_hardware_api, @@ -16,7 +22,7 @@ get_endstop_position_ot3, move_plunger_absolute_ot3, update_pick_up_current, - update_pick_up_distance + update_pick_up_distance, ) from hardware_testing import data @@ -24,34 +30,41 @@ def dict_values_to_line(dict): - return str.join(",", list(dict.values()))+"\n" + return str.join(",", list(dict.values())) + "\n" + def dict_keys_to_line(dict): - return str.join(",", list(dict.keys()))+"\n" + return str.join(",", list(dict.keys())) + "\n" + def file_setup(test_data): - current_val = float(input("Enter Motor Current to be Tested:")) - test_name = "Tip_Attachment_Test" - test_header = dict_keys_to_line(test_data) - test_tag = "{}Amps-start-time-{}".format(current_val, int(time.time())) - test_id = data.create_run_id() - test_path = data.create_folder_for_test_data(test_name) - test_file = data.create_file_name(test_name, test_id, test_tag) - data.append_data_to_file(test_name, test_file, test_header) - print("FILE PATH = ", test_path) - print("FILE NAME = ", test_file) - return test_name, test_file + current_val = float(input("Enter Motor Current to be Tested:")) + test_name = "Tip_Attachment_Test" + test_header = dict_keys_to_line(test_data) + test_tag = "{}Amps-start-time-{}".format(current_val, int(time.time())) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + def dial_indicator_setup(): - gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator(port='/dev/ttyUSB0') + gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator( + port="/dev/ttyUSB0" + ) gauge.connect() return gauge + def getch(): """ - fd: file descriptor stdout, stdin, stderr - This functions gets a single input keyboard character from the user + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user """ + def _getch(): fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) @@ -61,8 +74,10 @@ def _getch(): finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return ch + return _getch() + async def _jog_axis(api, position) -> None: step_size = [0.05, 0.1, 0.5, 1, 10, 20, 50] step_length_index = 3 @@ -84,53 +99,47 @@ async def _jog_axis(api, position) -> None: print(information_str) while True: input = getch() - if input == 'a': + if input == "a": # minus x direction sys.stdout.flush() - await api.move_rel(mount, - Point( - -step_size[step_length_index],0,0), - speed = xy_speed) + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) - elif input == 'd': - #plus x direction + elif input == "d": + # plus x direction sys.stdout.flush() - await api.move_rel(mount, - Point( - step_size[step_length_index],0,0), - speed = xy_speed) + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) - elif input == 'w': - #minus y direction + elif input == "w": + # minus y direction sys.stdout.flush() - await api.move_rel(mount, - Point( - 0,step_size[step_length_index],0), - speed = xy_speed) + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) - elif input == 's': - #plus y direction + elif input == "s": + # plus y direction sys.stdout.flush() - await api.move_rel(mount, - Point( - 0,-step_size[step_length_index],0), - speed = xy_speed) + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) - elif input == 'i': + elif input == "i": sys.stdout.flush() - await api.move_rel(mount, - Point( - 0,0,step_size[step_length_index]), - speed = za_speed) + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) - elif input == 'k': + elif input == "k": sys.stdout.flush() - await api.move_rel(mount, - Point( - 0,0,-step_size[step_length_index]), - speed = za_speed) + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) - elif input == 'r': + elif input == "r": sys.stdout.flush() position = await api.current_position_ot3(mount) gauge_reading = gauge.read_stable(timeout=20) @@ -145,46 +154,56 @@ async def _jog_axis(api, position) -> None: {gauge.read_stable(timeout=20)}, {gauge_reading}\n" data.append_data_to_file(test_n, test_f, d_str) - elif input == 'q': + elif input == "q": sys.stdout.flush() print("TEST CANCELLED") quit() - elif input == '+': + elif input == "+": sys.stdout.flush() step_length_index = step_length_index + 1 if step_length_index >= 6: step_length_index = 6 step = step_size[step_length_index] - elif input == '-': + elif input == "-": sys.stdout.flush() - step_length_index = step_length_index -1 + step_length_index = step_length_index - 1 if step_length_index <= 0: step_length_index = 0 step = step_size[step_length_index] - elif input == '\r': + elif input == "\r": sys.stdout.flush() return position position = await api.current_position_ot3(mount) - print('Coordinates: ', round(position[OT3Axis.X], 2), ',', - round(position[OT3Axis.Y], 2), ',', - round(position[OT3Axis.by_mount(mount)], 2), ' Motor Step: ', - step_size[step_length_index], - end = '') - print('\r', end='') + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + async def _main() -> None: - hw_api = await build_async_ot3_hardware_api(is_simulating=args.simulate, - use_defaults=True) + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) # await set_default_current_settings(hw_api, load=None) await home_ot3(hw_api, [OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) home_pos = await hw_api.current_position_ot3(mount) - start_loc = {OT3Axis.X: 150, - OT3Axis.Y: 200, - OT3Axis.by_mount(mount): home_pos[OT3Axis.by_mount(mount)]} + start_loc = { + OT3Axis.X: 150, + OT3Axis.Y: 200, + OT3Axis.by_mount(mount): home_pos[OT3Axis.by_mount(mount)], + } # await hw_api.current_position_ot3(mount await hw_api.cache_instruments() trash_loc = () @@ -192,18 +211,23 @@ async def _main() -> None: tip_column = 0 columns_to_use = 12 try: - if args.test == 'pick_up_tip': - start_time = time.time() - await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], - start_loc[OT3Axis.Y], - home_pos[OT3Axis.by_mount(mount)])) + if args.test == "pick_up_tip": + start_time = time.time() + await hw_api.move_to( + mount, + Point( + start_loc[OT3Axis.X], + start_loc[OT3Axis.Y], + home_pos[OT3Axis.by_mount(mount)], + ), + ) # await hw_api.move_to(mount, Point(home_pos[OT3Axis.X], # start_loc[OT3Axis.Y], # home_pos[OT3Axis.by_mount(mount)])) # await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], # start_loc[OT3Axis.Y], # home_pos[OT3Axis.by_mount(mount)])) - for cycle in range(1, columns_to_use+1): + for cycle in range(1, columns_to_use + 1): if cycle <= 1: print("Move to Tiprack") home_position = await hw_api.current_position_ot3(mount) @@ -213,51 +237,69 @@ async def _main() -> None: # await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X], # tiprack_loc[OT3Axis.Y], # home_pos[OT3Axis.by_mount(mount)])) - await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X] + tip_column, - tiprack_loc[OT3Axis.Y], - tiprack_loc[OT3Axis.by_mount(mount)])) - #await hw_api.pick_up_tip(mount, tip_length = 58.5, presses = 2, increment = 1) + await hw_api.move_to( + mount, + Point( + tiprack_loc[OT3Axis.X] + tip_column, + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)], + ), + ) + # await hw_api.pick_up_tip(mount, tip_length = 58.5, presses = 2, increment = 1) current_val = float(input("Enter Current Val: ")) print(f"Current Val: {current_val}") await update_pick_up_current(hw_api, mount, current_val) - await hw_api.pick_up_tip(mount, tip_length = 58.5) + await hw_api.pick_up_tip(mount, tip_length=58.5) current_pos = await hw_api.current_position_ot3(mount) # Move to Block - await hw_api.move_to(mount, Point(207.2, - 72.7, - current_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to( + mount, Point(207.2, 72.7, current_pos[OT3Axis.by_mount(mount)]) + ) current_pos = await hw_api.current_position_ot3(mount) block = await _jog_axis(hw_api, current_pos) - await hw_api.home_z(mount, allow_home_other = False) + await hw_api.home_z(mount, allow_home_other=False) - trash_loc = [432.7 , 393.3 , 100.0] - await hw_api.home_z(mount, allow_home_other = False) + trash_loc = [432.7, 393.3, 100.0] + await hw_api.home_z(mount, allow_home_other=False) current_pos = await hw_api.current_position_ot3(mount) - await hw_api.move_to(mount, Point(trash_loc[0], - trash_loc[1], - current_pos[OT3Axis.by_mount(mount)])) - await hw_api.move_to(mount, Point(trash_loc[0], - trash_loc[1], - trash_loc[2])) + await hw_api.move_to( + mount, + Point( + trash_loc[0], trash_loc[1], current_pos[OT3Axis.by_mount(mount)] + ), + ) + await hw_api.move_to( + mount, Point(trash_loc[0], trash_loc[1], trash_loc[2]) + ) await hw_api.drop_tip(mount) - await hw_api.home_z(mount, allow_home_other = False) + await hw_api.home_z(mount, allow_home_other=False) current_pos = await hw_api.current_position_ot3(mount) # tip_column += 9 - await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X] + tip_column, - tiprack_loc[OT3Axis.Y], - current_pos[OT3Axis.by_mount(mount)])) - elif args.test == 'tip_height_test': - start_time = time.time() - await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], - start_loc[OT3Axis.Y], - home_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to( + mount, + Point( + tiprack_loc[OT3Axis.X] + tip_column, + tiprack_loc[OT3Axis.Y], + current_pos[OT3Axis.by_mount(mount)], + ), + ) + elif args.test == "tip_height_test": + start_time = time.time() + await hw_api.move_to( + mount, + Point( + start_loc[OT3Axis.X], + start_loc[OT3Axis.Y], + home_pos[OT3Axis.by_mount(mount)], + ), + ) # await hw_api.move_to(mount, Point(home_pos[OT3Axis.X], # start_loc[OT3Axis.Y], # home_pos[OT3Axis.by_mount(mount)])) # await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], # start_loc[OT3Axis.Y], # home_pos[OT3Axis.by_mount(mount)])) - for cycle in range(1, columns_to_use+1): + for cycle in range(1, columns_to_use + 1): if cycle <= 1: print("Move to Tiprack") home_position = await hw_api.current_position_ot3(mount) @@ -267,22 +309,27 @@ async def _main() -> None: # await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X], # tiprack_loc[OT3Axis.Y], # home_pos[OT3Axis.by_mount(mount)])) - await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X] + tip_column, - tiprack_loc[OT3Axis.Y], - tiprack_loc[OT3Axis.by_mount(mount)])) - #await hw_api.pick_up_tip(mount, tip_length = 58.5, presses = 2, increment = 1) + await hw_api.move_to( + mount, + Point( + tiprack_loc[OT3Axis.X] + tip_column, + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)], + ), + ) + # await hw_api.pick_up_tip(mount, tip_length = 58.5, presses = 2, increment = 1) current_val = float(input("Enter Current Val: ")) print(f"Current Val: {current_val}") await update_pick_up_current(hw_api, mount, current_val) - await hw_api.pick_up_tip(mount, tip_length = 58.5) + await hw_api.pick_up_tip(mount, tip_length=58.5) current_pos = await hw_api.current_position_ot3(mount) # Move to Block - await hw_api.move_to(mount, Point(207.2, - 72.7, - current_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to( + mount, Point(207.2, 72.7, current_pos[OT3Axis.by_mount(mount)]) + ) current_pos = await hw_api.current_position_ot3(mount) block = await _jog_axis(hw_api, current_pos) - await hw_api.home_z(mount, allow_home_other = False) + await hw_api.home_z(mount, allow_home_other=False) if cycle <= 1: print("Move to Tiprack") @@ -290,23 +337,38 @@ async def _main() -> None: dial_pos = await _jog_axis(hw_api, home_position) await hw_api.home_z(mount) current_pos = await hw_api.current_position_ot3(mount) - await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], - dial_pos[OT3Axis.Y], - current_pos[OT3Axis.by_mount(mount)]-3)) + await hw_api.move_to( + mount, + Point( + dial_pos[OT3Axis.X], + dial_pos[OT3Axis.Y], + current_pos[OT3Axis.by_mount(mount)] - 3, + ), + ) current_pos = await hw_api.current_position_ot3(mount) - await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], - dial_pos[OT3Axis.Y], - current_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to( + mount, + Point( + dial_pos[OT3Axis.X], + dial_pos[OT3Axis.Y], + current_pos[OT3Axis.by_mount(mount)], + ), + ) tip_increment = 0 tips = 8 z_distance_press = 6 - for tip in range(1, tips+1): + for tip in range(1, tips + 1): # Press onto the dial indicator - await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], - dial_pos[OT3Axis.Y]-tip_increment, - dial_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to( + mount, + Point( + dial_pos[OT3Axis.X], + dial_pos[OT3Axis.Y] - tip_increment, + dial_pos[OT3Axis.by_mount(mount)], + ), + ) await asyncio.sleep(2) - elasped_time = (time.time() - start_time)/60 + elasped_time = (time.time() - start_time) / 60 test_data["Time"] = round(elasped_time, 3) test_data["Tip Height(mm)"] = gauge.read_stable(timeout=20) test_data["Tip"] = tip @@ -315,100 +377,131 @@ async def _main() -> None: data.append_data_to_file(test_n, test_f, d_str) await asyncio.sleep(1) # Retract from the dial indicator by 6mm - await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], - dial_pos[OT3Axis.Y]-tip_increment, - dial_pos[OT3Axis.by_mount(mount)]+z_distance_press)) + await hw_api.move_to( + mount, + Point( + dial_pos[OT3Axis.X], + dial_pos[OT3Axis.Y] - tip_increment, + dial_pos[OT3Axis.by_mount(mount)] + z_distance_press, + ), + ) # backlash compensation - await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], - dial_pos[OT3Axis.Y]-tip_increment, - dial_pos[OT3Axis.by_mount(mount)]+z_distance_press-3)) + await hw_api.move_to( + mount, + Point( + dial_pos[OT3Axis.X], + dial_pos[OT3Axis.Y] - tip_increment, + dial_pos[OT3Axis.by_mount(mount)] + z_distance_press - 3, + ), + ) # move to the next nozzle tip_increment += 9 current_pos = await hw_api.current_position_ot3(mount) - await hw_api.move_to(mount, Point(dial_pos[OT3Axis.X], - dial_pos[OT3Axis.Y]-tip_increment, - current_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to( + mount, + Point( + dial_pos[OT3Axis.X], + dial_pos[OT3Axis.Y] - tip_increment, + current_pos[OT3Axis.by_mount(mount)], + ), + ) - trash_loc = [432.7 , 393.3 , 100.0] - await hw_api.home_z(mount, allow_home_other = False) + trash_loc = [432.7, 393.3, 100.0] + await hw_api.home_z(mount, allow_home_other=False) current_pos = await hw_api.current_position_ot3(mount) - await hw_api.move_to(mount, Point(trash_loc[0], - trash_loc[1], - current_pos[OT3Axis.by_mount(mount)])) - await hw_api.move_to(mount, Point(trash_loc[0], - trash_loc[1], - trash_loc[2])) + await hw_api.move_to( + mount, + Point( + trash_loc[0], trash_loc[1], current_pos[OT3Axis.by_mount(mount)] + ), + ) + await hw_api.move_to( + mount, Point(trash_loc[0], trash_loc[1], trash_loc[2]) + ) await hw_api.drop_tip(mount) - await hw_api.home_z(mount, allow_home_other = False) + await hw_api.home_z(mount, allow_home_other=False) current_pos = await hw_api.current_position_ot3(mount) # tip_column += 9 - await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X] + tip_column, - tiprack_loc[OT3Axis.Y], - current_pos[OT3Axis.by_mount(mount)])) - elif args.test == 'flatness': + await hw_api.move_to( + mount, + Point( + tiprack_loc[OT3Axis.X] + tip_column, + tiprack_loc[OT3Axis.Y], + current_pos[OT3Axis.by_mount(mount)], + ), + ) + elif args.test == "flatness": home_position = await hw_api.current_position_ot3(mount) - await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], - start_loc[OT3Axis.Y], - home_pos[OT3Axis.by_mount(mount)])) + await hw_api.move_to( + mount, + Point( + start_loc[OT3Axis.X], + start_loc[OT3Axis.Y], + home_pos[OT3Axis.by_mount(mount)], + ), + ) # we don't need this, this is just for a placeholder flatness = await _jog_axis(hw_api, home_position) - elif args.test == 'flatness_with_move': - coordinates = [(515, 29, 294.16), - (465, 29, 294.16), - (415, 29, 294.16), - (317.23, 29, 294.16), - (317.23, 96.72, 294.16), - (326.73, 96.72, 294.16), - (326.73, 143.32, 294.16), - (415, 143.32, 294.16), - (465, 143.32, 294.16), - (515, 143.32, 294.16), - (317.24, 203.12, 294.16), - (326.24, 250.11, 294.16), - (415, 250.11, 294.16), - (465, 250.11, 294.16), - (515, 250.11, 294.16), - (317.22, 303.62, 294.16), - (326.25, 357.62, 294.16), - (415, 357.62, 294.16), - (465, 357.62, 294.16), - (515, 357.62, 294.16), - (317.22, 411.11, 294.16), - (256.22, 29, 294.16), - (268.28, 29, 294.16), - (218.28, 29, 294.16), - (154.29, 29, 294.16), - (154.29, 96.72, 294.16), - (162.29, 143.32, 294.16), - (218.29, 143.32, 294.16), - (268.29, 143.32, 294.16), - (154.8, 203.12, 294.16), - (162.3, 250.11, 294.16), - (218.3, 250.11, 294.16), - (268.3, 250.11, 294.16), - (154.81, 303.62, 294.16), - (162.31, 357.62, 294.16), - (218.3, 357.62, 294.16), - (268.31, 357.62, 294.16), - (154.86, 411.11, 294.16), - (-21.85, 29, 294.16), - (62.85, 29, 294.16), - (-21.85, 143.32, 294.16), - (62.85, 143.32, 294.16), - (-21.85, 250.11, 294.16), - (62.85, 250.11, 294.16)] + elif args.test == "flatness_with_move": + coordinates = [ + (515, 29, 294.16), + (465, 29, 294.16), + (415, 29, 294.16), + (317.23, 29, 294.16), + (317.23, 96.72, 294.16), + (326.73, 96.72, 294.16), + (326.73, 143.32, 294.16), + (415, 143.32, 294.16), + (465, 143.32, 294.16), + (515, 143.32, 294.16), + (317.24, 203.12, 294.16), + (326.24, 250.11, 294.16), + (415, 250.11, 294.16), + (465, 250.11, 294.16), + (515, 250.11, 294.16), + (317.22, 303.62, 294.16), + (326.25, 357.62, 294.16), + (415, 357.62, 294.16), + (465, 357.62, 294.16), + (515, 357.62, 294.16), + (317.22, 411.11, 294.16), + (256.22, 29, 294.16), + (268.28, 29, 294.16), + (218.28, 29, 294.16), + (154.29, 29, 294.16), + (154.29, 96.72, 294.16), + (162.29, 143.32, 294.16), + (218.29, 143.32, 294.16), + (268.29, 143.32, 294.16), + (154.8, 203.12, 294.16), + (162.3, 250.11, 294.16), + (218.3, 250.11, 294.16), + (268.3, 250.11, 294.16), + (154.81, 303.62, 294.16), + (162.31, 357.62, 294.16), + (218.3, 357.62, 294.16), + (268.31, 357.62, 294.16), + (154.86, 411.11, 294.16), + (-21.85, 29, 294.16), + (62.85, 29, 294.16), + (-21.85, 143.32, 294.16), + (62.85, 143.32, 294.16), + (-21.85, 250.11, 294.16), + (62.85, 250.11, 294.16), + ] home_position = await hw_api.current_position_ot3(mount) array_num = 0 for coord in coordinates: - await hw_api.move_to(mount, Point(coord[0], - coord[1], - home_pos[OT3Axis.by_mount(mount)]), - speed = 60) + await hw_api.move_to( + mount, + Point(coord[0], coord[1], home_pos[OT3Axis.by_mount(mount)]), + speed=60, + ) await asyncio.sleep(1) - await hw_api.move_to(mount, Point(coord[0], - coord[1], - coord[2]), speed = 65) + await hw_api.move_to( + mount, Point(coord[0], coord[1], coord[2]), speed=65 + ) await asyncio.sleep(3) gauge_reading = gauge.read_stable(timeout=20) position = await hw_api.current_position_ot3(mount) @@ -422,10 +515,11 @@ async def _main() -> None: {round(position[OT3Axis.by_mount(mount)], 2)}, \ {gauge.read_stable(timeout=20)}, {gauge_reading}\n" data.append_data_to_file(test_n, test_f, d_str) - await hw_api.move_to(mount, Point(coord[0], - coord[1], - home_pos[OT3Axis.by_mount(mount)]), - speed = 65) + await hw_api.move_to( + mount, + Point(coord[0], coord[1], home_pos[OT3Axis.by_mount(mount)]), + speed=65, + ) array_num += 1 # we don't need this, this is just for a placeholder flatness = await _jog_axis(hw_api, home_position) @@ -437,7 +531,6 @@ async def _main() -> None: await hw_api.clean_up() - if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--simulate", action="store_true") @@ -459,20 +552,16 @@ async def _main() -> None: speed_z = 60 if args.dial_indicator: if args.flatness: - test_data ={ - "X-Coordinate": None, - "Y-Coordinate": None, - "Z-Coordinate": None, - "Deck Height(mm)": None, - } + test_data = { + "X-Coordinate": None, + "Y-Coordinate": None, + "Z-Coordinate": None, + "Deck Height(mm)": None, + } else: - test_data ={ - "Time": None, - "Tip Height(mm)": None, - "Tip": None - } + test_data = {"Time": None, "Tip Height(mm)": None, "Tip": None} gauge = dial_indicator_setup() - test_n , test_f = file_setup(test_data) + test_n, test_f = file_setup(test_data) pick_up_speed = 5 press_distance = 15 PIPETTE_SPEED = 10 From 10d900ca0a0ca41f9c7bc322e46f33d0fdeffbb5 Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 14 Mar 2023 12:53:04 -0400 Subject: [PATCH 09/53] pick up tip test --- .../hardware_testing/scripts/pick_up_tip.py | 388 ++++++++++++------ 1 file changed, 259 insertions(+), 129 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/pick_up_tip.py b/hardware-testing/hardware_testing/scripts/pick_up_tip.py index 0c001da7572..035ea60658f 100644 --- a/hardware-testing/hardware_testing/scripts/pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/pick_up_tip.py @@ -8,22 +8,30 @@ from threading import Thread import datetime import os +import sys +import termios +import tty +import json from opentrons.hardware_control.motion_utilities import target_position_from_plunger from hardware_testing.opentrons_api.types import ( OT3Mount, OT3Axis, Point, + CriticalPoint, ) from hardware_testing.opentrons_api.helpers_ot3 import ( build_async_ot3_hardware_api, home_ot3, move_plunger_absolute_ot3, move_plunger_relative_ot3, + get_plunger_positions_ot3, update_pick_up_current, update_pick_up_distance, ) +from opentrons.config.types import LiquidProbeSettings + from hardware_testing import data from hardware_testing.drivers.mark10 import Mark10 from hardware_testing.drivers import mitutoyo_digimatic_indicator @@ -36,18 +44,16 @@ leak_test_time = 30 + def dict_keys_to_line(dict): return str.join(",", list(dict.keys())) + "\n" -def file_setup(test_data, details, pipette_model): +def file_setup(test_data, details): today = datetime.date.today() - test_name = "{}-LSD-Z-{}-P-{}-Threshold-{}".format( + test_name = "{}-pick_up-up-test".format( details[0], # Pipette model - details[1], # mount_speed - details[2], # plunger_speed - details[3], - ) # sensor threshold + ) test_header = dict_keys_to_line(test_data) test_tag = "-{}".format(today.strftime("%b-%d-%Y")) test_id = data.create_run_id() @@ -60,11 +66,33 @@ def file_setup(test_data, details, pipette_model): def dial_indicator_setup(): - gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator(port='/dev/ttyUSB0') + gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator( + port="/dev/ttyUSB1" + ) gauge.connect() return gauge -async def _jog_axis(api, position) -> Dict[OT3Axis, float]: + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[OT3Axis, float]: step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] step_length_index = 3 step = step_size[step_length_index] @@ -146,9 +174,14 @@ async def _jog_axis(api, position) -> Dict[OT3Axis, float]: elif input == "\r": sys.stdout.flush() - position = await api.current_position_ot3(mount, refresh=True) + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") return position - position = await api.current_position_ot3(mount, refresh=True) + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) print( "Coordinates: ", @@ -197,52 +230,117 @@ async def _main() -> None: is_simulating=args.simulate, use_defaults=True ) tip_length = {"T1K": 85.7, "T50": 57.9} - pipette_model = hw_api._pipette_handler.hardware_instruments[mount] - await home_ot3(hw_api, [OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + pipette_model = hw_api._pipette_handler.hardware_instruments[mount].name + dial_data = {"Tip": None, "Tip Height": None} + details = [pipette_model] + test_n, test_f = file_setup(dial_data, details) + print(test_n) + print(test_f) + await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) await hw_api.home_plunger(mount) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) home_position = await hw_api.current_position_ot3(mount) - global encoder_position - global encoder_end global stop_threads global motion - encoder_end = None if args.fg_jog: + cp = CriticalPoint.NOZZLE + await hw_api.move_to( + mount, + Point( + slot_loc["D2"][0], + slot_loc["D2"][1], + home_position[OT3Axis.by_mount(mount)], + ), + ) + current_position = await hw_api.current_position_ot3(mount) print("Move to Force Gauge") - fg_loc = await jog(hw_api) - fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(MOUNT)]] - await hw_api.home_z(MOUNT, allow_home_other=False) - # fg_loc = [-4.0, 87.25, 410.0] # 96 Pipette Channel - # fg_loc = [186.0, 34.0, 125.0] - # fg_loc = [22.5 , 34, 125] # P1KS Coordinate - - # fg_loc = [24.0, 64, 65.0] - # fg_loc = [24.0, 64, 125.0] # Multi channel coord + fg_loc = await jog(hw_api, current_position, cp) + fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(mount)]] + await hw_api.home_z(mount, allow_home_other=False) + if args.tiprack: - # print("Move to Tiprack") - # tiprack_loc = await jog(hw_api) - # tiprack_loc = [tiprack_loc[OT3Axis.X], tiprack_loc[OT3Axis.Y], tiprack_loc[OT3Axis.by_mount(MOUNT)]] - # await hw_api.home_z(MOUNT, allow_home_other = False) - tiprack_loc = [135.7, 63.1, 80.0] - # tiprack_loc = [136.5, 63.3, 78.0] # Oolong - # tiprack_loc = [136.5, 62.6, 80.0] #Mr T robot - # tiprack_loc = [157.25, 84.25, 366.0] # 96 Channel - # tiprack_loc = [136.0, 62.3, 80] # P1K Single Channel - # tiprack_loc = [138.0, 61.5, 80.0] # 1KS Multi Channel, P50M + await hw_api.move_to( + mount, + Point( + slot_loc["B2"][0], + slot_loc["B2"][1], + home_position[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.NOZZLE + print("Move to Tiprack") + current_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await jog(hw_api, current_position, cp) + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + home_with_tip = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + tiprack_loc = [ + tiprack_loc[OT3Axis.X], + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)], + ] + + if args.dial_indicator: + await hw_api.move_to( + mount, + Point( + slot_loc["C2"][0], + slot_loc["C2"][1], + home_with_tip[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.TIP + print("Move to Tiprack") + current_position = await hw_api.current_position_ot3(mount) + dial_loc = await jog(hw_api, current_position, cp) + dial_loc = [ + dial_loc[OT3Axis.X], + dial_loc[OT3Axis.Y], + dial_loc[OT3Axis.by_mount(mount)], + ] + if args.trough: - # print("Move to Trough") - # await hw_api.add_tip(MOUNT, 58.5) - # trough_loc = await jog(hw_api) - # trough_loc = [trough_loc[OT3Axis.X], trough_loc[OT3Axis.Y], trough_loc[OT3Axis.by_mount(MOUNT)]] - # await hw_api.home_z(MOUNT, allow_home_other = False) - # await hw_api.remove_tip(MOUNT) - trough_loc = [310.0, 40.0, -8.5] - # trough_loc = [310.0, 40.0, 24.0] - # trough_loc = [299.5, 40.0, 30.0] # Mr T - # trough_loc = [301.5, 61.5, 24.0] # P1K Multi Channel Coord - # trough_loc = [301.5, 61.5, -10.0] # P50 Multi Channel coord - # trough_loc = [299.0, 40.0, 30.0] # P1KS Coord - # trough_loc = [300, 40, 85-78.5] - # X: 300.0, Y: 40.0, Z: 85.0 + await hw_api.move_to( + mount, + Point( + slot_loc["B3"][0], + slot_loc["B3"][1], + home_with_tip[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.TIP + print("Move to Trough") + current_position = await hw_api.current_position_ot3(mount) + trough_loc = await jog(hw_api, current_position, cp) + trough_loc = [ + trough_loc[OT3Axis.X], + trough_loc[OT3Axis.Y], + trough_loc[OT3Axis.by_mount(mount)], + ] + await hw_api.move_to( + mount, + Point(trough_loc[0], trough_loc[1], home_with_tip[OT3Axis.by_mount(mount)]), + critical_point=CriticalPoint.TIP, + ) + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1] - 20, + home_with_tip[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + await hw_api.drop_tip(mount) + lp_file_name = "/var/pressure_sensor_data_P-{}_Z-{}-{}.csv".format( args.plunger_speed, args.mount_speed, today.strftime("%b-%d-%Y") ) @@ -254,7 +352,7 @@ async def _main() -> None: sensor_threshold_pascals=args.sensor_threshold, expected_liquid_height=args.expected_liquid_height, log_pressure=args.log_pressure, - aspirate_while_sensing=True, + aspirate_while_sensing=False, data_file=lp_file_name, ) try: @@ -268,31 +366,42 @@ async def _main() -> None: ) # # Move pipette to Force Gauge calibrated location await hw_api.move_to(mount, Point(fg_loc[0], fg_loc[1], fg_loc[2])) - init_fg_loc = await encoder_current_position_ot3(mount, CriticalPoint.NONE)) - init_fg_loc = encoder_position[OT3Axis.by_mount(mount)] + init_fg_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + init_fg_loc = init_fg_loc[OT3Axis.by_mount(mount)] location = "Force_Gauge" force_thread = Thread( target=force_record, args=( m_current, location, + pipette_model, ), ) force_thread.start() await update_pick_up_current(hw_api, mount, m_current) + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) # Move pipette to Force Gauge press location - await pick_up_tip(mount, tip_length=tip_length[args.tip_size]) + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + home_with_tip = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) await asyncio.sleep(2) - final_fg_loc = await encoder_current_position_ot3(mount, CriticalPoint.NONE) - final_fg_loc = encoder_position[OT3Axis.by_mount(mount)] - print(encoder_position) + final_fg_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + final_fg_loc = final_fg_loc[OT3Axis.by_mount(mount)] + print(final_fg_loc) motion = False stop_threads = True force_thread.join() # Thread Finished - await remove_tip(mount) + await hw_api.remove_tip(mount) await hw_api.home_z(mount, allow_home_other=False) # -----------------------Tiprack------------------------------------ @@ -300,57 +409,80 @@ async def _main() -> None: await hw_api.move_to( mount, Point( - tiprack_loc[0], tiprack_loc[1], home_pos[OT3Axis.by_mount(mount)] + tiprack_loc[0], + tiprack_loc[1], + home_position[OT3Axis.by_mount(mount)], ), ) # Move Pipette to top of Tip Rack Location await hw_api.move_to( - MOUNT, Point(tiprack_loc[0], tiprack_loc[1], tiprack_loc[2]), speed=65 + mount, Point(tiprack_loc[0], tiprack_loc[1], tiprack_loc[2]), speed=65 ) location = "Tiprack" + global encoder_end + encoder_end = None # Start recording the encoder - init_tip_loc = await encoder_current_position_ot3(mount, CriticalPoint.NONE) - print(f"Start encoder: {init_tip_loc}") - init_tip_loc = encoder_position[OT3Axis.by_mount(mount)] - enc_thread = Thread( - target=force_record, - args=( - m_current, - location, - ), + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE ) - enc_thread.start() + print(f"Start encoder: {init_tip_loc}") + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc # Press Pipette into the tip await update_pick_up_current(hw_api, mount, m_current) # Move pipette to Force Gauge press location - await pick_up_tip(mount, tip_length=tip_length[args.tip_size]) + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) home_with_tip = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) - await asyncio.sleep(2) - final_tip_loc = await encoder_current_position_ot3(mount, CriticalPoint.NONE) - print(f"End Encoder: {encoder_end}") - final_tip_loc = encoder_position[OT3Axis.by_mount(mount)] - stop_threads = True - enc_thread.join() # Thread Finished + encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] + # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + print(f"End Encoder: {final_tip_loc}") + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + enc_record(m_current, location, init_tip_loc, final_tip_loc) # Home Z await hw_api.home([OT3Axis.by_mount(mount)]) - input("Feel the Tip") + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point(dial_loc[0], dial_loc[1], home_with_tip[OT3Axis.by_mount(mount)]), + ) + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point(dial_loc[0], dial_loc[1], dial_loc[2]), + ) + await asyncio.sleep(1) + tip_measurement = gauge.read() + d_str = f"{pipette_model}, {tip_measurement}, Tip \n" + data.append_data_to_file(test_n, test_f, d_str) + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point(dial_loc[0], dial_loc[1], home_with_tip[OT3Axis.by_mount(mount)]), + ) # -----------------------Aspirate----------------------------------- await hw_api.move_to( mount, - Point(trough_loc[0], trough_loc[1], home_pos[OT3Axis.by_mount(mount)]), + Point( + trough_loc[0], trough_loc[1], home_with_tip[OT3Axis.by_mount(mount)] + ), ) # Move to offset from trough await hw_api.move_to( mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) ) - - # Prepare to aspirate before descending to trough well - await hw_api.prepare_for_aspirate(mount) + # Move the plunger to the top position + await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[0]) # Liquid Probe - await hw_api.liquid_probe(mount, probe_settings=liquid_probe_settings) + liquid_height = await hw_api.liquid_probe( + mount, probe_settings=liquid_probe_settings + ) + print("I'm here") + liquid_height = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) @@ -359,6 +491,8 @@ async def _main() -> None: await hw_api.move_to( mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) ) + # Prepare to aspirate before descending to trough well + await hw_api.prepare_for_aspirate(mount) # Descend to aspirate depth await hw_api.move_to( mount, @@ -375,7 +509,7 @@ async def _main() -> None: cur_pos = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) - z_pos = cur_pos[OT3Axis.by_mount(MOUNT)] + z_pos = cur_pos[OT3Axis.by_mount(mount)] # Retract from liquid with retract speed await hw_api.move_to( mount, @@ -384,7 +518,7 @@ async def _main() -> None: critical_point=CriticalPoint.TIP, ) await hw_api.move_to( - MOUNT, + mount, Point( trough_loc[0], trough_loc[1], home_with_tip[OT3Axis.by_mount(mount)] ), @@ -416,6 +550,7 @@ async def _main() -> None: ), critical_point=CriticalPoint.TIP, ) + input("Feel the Tip") # Move to trash slot await hw_api.move_to( mount, @@ -436,26 +571,23 @@ async def _main() -> None: await hw_api.clean_up() -def force_record(motor_current, location): - dir = os.getcwd() - global encoder_position - global encoder_end +def force_record(motor_current, location, pipette_model): global stop_threads global motion - encoder_end = None - file_name = "/results/force_pu_test_%s-%s-%s.csv" % ( - motor_current, - datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), - location, + file_name = ( + "/home/root/.opentrons/testing_data/force_data/force_pu_test_%s-%s-%s.csv" + % ( + motor_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + location, + ) ) - print(dir + file_name) - with open(dir + file_name, "w", newline="") as f: + print(file_name) + with open(file_name, "w", newline="") as f: test_data = { "Time(s)": None, "Force(N)": None, "M_current(amps)": None, - "encoder_pos(mm)": None, - "end_enc_pos(mm)": None, "pipette_model": None, } log_file = csv.DictWriter(f, test_data) @@ -469,8 +601,7 @@ def force_record(motor_current, location): test_data["Time(s)"] = time.perf_counter() - start_time test_data["Force(N)"] = reading test_data["M_current(amps)"] = motor_current - test_data["encoder_pos(mm)"] = encoder_position - test_data["end_enc_pos(mm)"] = encoder_end + test_data["pipette_model"] = pipette_model log_file.writerow(test_data) print(test_data) f.flush() @@ -490,37 +621,34 @@ def force_record(motor_current, location): f.close() -def enc_record(motor_current, location): - dir = os.getcwd() - global encoder_position - global encoder_end - global stop_threads - global motion - encoder_end = None - file_name = "/results/enc_pu_test_%s-%s-%s.csv" % ( - motor_current, - datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), - location, +def enc_record(motor_current, location, init_enc_pos, final_enc_pos): + file_name = ( + "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s-%s.csv" + % ( + motor_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + location, + ) ) print(file_name) - print(dir + file_name) - with open(dir + file_name, "wb", newline="") as f: - test_data = {"time(s)": None, "start_enc_pos": None, "end_enc_pos(mm)": None} + with open(file_name, "w", newline="") as f: + test_data = { + "time(s)": None, + "motor_current": None, + "start_enc_pos": None, + "end_enc_pos(mm)": None, + } log_file = csv.DictWriter(f, test_data) log_file.writeheader() start_time = time.perf_counter() try: - motion = True - stop_threads = False - while motion: - test_data["time(s)"] = time.perf_counter() - start_time - test_data["start_enc_pos(mm)"] = encoder_position - test_data["end_enc_pos(mm)"] = encoder_end - log_file.writerow(test_data) - print(test_data) - f.flush() - if stop_threads: - break + test_data["time(s)"] = time.perf_counter() - start_time + test_data["motor_current"] = motor_current + test_data["start_enc_pos(mm)"] = init_enc_pos + test_data["end_enc_pos(mm)"] = final_enc_pos + log_file.writerow(test_data) + print(test_data) + f.flush() except KeyboardInterrupt: print("Test Cancelled") test_data["Errors"] = "Test Cancelled" @@ -552,23 +680,25 @@ def enc_record(motor_current, location): ] parser = argparse.ArgumentParser() parser.add_argument("--simulate", action="store_true") - parser.add_argument("--fg_jog", action="store_true") - parser.add_argument("--trough", action="store_true") - parser.add_argument("--tiprack", action="store_true") + parser.add_argument("--fg_jog", action="store_true", default=True) + parser.add_argument("--trough", action="store_true", default=True) + parser.add_argument("--tiprack", action="store_true", default=True) parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C2") parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") parser.add_argument("--fg", action="store_true", default=True) - parser.add_argument("--tip_size", type=str, default="T50", help="Tip Size") + parser.add_argument("--dial_indicator", action="store_true", default=True) + parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") parser.add_argument("--max_z_distance", type=float, default=40) parser.add_argument("--min_z_distance", type=float, default=5) parser.add_argument("--mount_speed", type=float, default=5) - parser.add_argument("--plunger_speed", type=float, default=25) + parser.add_argument("--plunger_speed", type=float, default=11) parser.add_argument( "--sensor_threshold", type=float, default=160, help="Threshold in Pascals" ) parser.add_argument("--expected_liquid_height", type=int, default=0) + parser.add_argument("--log_pressure", action="store_true") parser.add_argument( "--port", type=str, default="/dev/ttyUSB0", help="Force Gauge Port" ) @@ -582,6 +712,6 @@ def enc_record(motor_current, location): fg.connect() if args.dial_indicator: + gauge = dial_indicator_setup() - test_n , test_f = file_setup(test_data, details) asyncio.run(_main()) From 4b799852fa998301b829c30639633a31250431f4 Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 14 Mar 2023 12:53:43 -0400 Subject: [PATCH 10/53] added json functions --- .../hardware_testing/data/__init__.py | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/hardware-testing/hardware_testing/data/__init__.py b/hardware-testing/hardware_testing/data/__init__.py index 8a37f9bea09..ff447b1cd7d 100644 --- a/hardware-testing/hardware_testing/data/__init__.py +++ b/hardware-testing/hardware_testing/data/__init__.py @@ -3,7 +3,7 @@ import os from pathlib import Path from time import time -from typing import Tuple +from typing import Tuple, Dict from opentrons.config import infer_config_base_dir @@ -84,3 +84,37 @@ def insert_data_to_file(test_name: str, file_name: str, data: str, line: int) -> contents.insert(line, data) with open(data_path, "w") as f: f.write("".join(contents)) + + +def load_config_(filename: str) -> Dict: + """This function loads a given config file""" + dir = os.getcwd() + filename = r"{}\{}".format(dir, filename) + # print(filename) + try: + with open(filename, "r") as file: + data = json.load(file) + except FileNotFoundError: + print("Warning: {0} not found".format(filename)) + data = {} + except json.decoder.JSONDecodeError: + print("Error: {0} is corrupt".format(filename)) + data = {} + return data + + +def save_config_(filename: str, data: str) -> Dict: + """This function saves a given config file with data""" + dir = os.getcwd() + filename = r"{}\{}".format(dir, filename) + # print(filename) + try: + with open(filename, "w") as file: + json.dump(data, file, sort_keys=False, indent=4, separators=(",", ": ")) + except FileNotFoundError: + print("Warning: {0} not found".format(filename)) + data = {} + except json.decoder.JSONDecodeError: + print("Error: {0} is corrupt".format(filename)) + data = {} + return data From 605746d338d90b171bf349038f99d0f7cbc8c2b3 Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 27 Mar 2023 09:05:31 -0400 Subject: [PATCH 11/53] pick up tip test script updates --- .../hardware_testing/scripts/pick_up_tip.py | 629 +++++++++++------- 1 file changed, 384 insertions(+), 245 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/pick_up_tip.py b/hardware-testing/hardware_testing/scripts/pick_up_tip.py index 035ea60658f..282c73a26ff 100644 --- a/hardware-testing/hardware_testing/scripts/pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/pick_up_tip.py @@ -28,6 +28,7 @@ get_plunger_positions_ot3, update_pick_up_current, update_pick_up_distance, + _get_pipette_from_mount, ) from opentrons.config.types import LiquidProbeSettings @@ -37,6 +38,7 @@ from hardware_testing.drivers import mitutoyo_digimatic_indicator aspirate_depth = 7 +dispense_depth = 3 liquid_retract_dist = 12 liquid_retract_speed = 5 retract_dist = 100 @@ -51,8 +53,9 @@ def dict_keys_to_line(dict): def file_setup(test_data, details): today = datetime.date.today() - test_name = "{}-pick_up-up-test".format( + test_name = "{}-pick_up-up-test-{}Amps".format( details[0], # Pipette model + details[1], # Motor Current ) test_header = dict_keys_to_line(test_data) test_tag = "-{}".format(today.strftime("%b-%d-%Y")) @@ -210,8 +213,18 @@ async def countdown(count_time: float): print("") +async def update_pickup_tip_speed(api, mount, speed) -> None: + """Update drop-tip current.""" + pipette = _get_pipette_from_mount(api, mount) + config_model = pipette.pick_up_configurations + config_model.speed = speed + pipette.pick_up_configurations = config_model + print(pipette.pick_up_configurations) + + async def _main() -> None: today = datetime.date.today() + tips_to_use = 8 slot_loc = { "A1": (13.42, 394.92, 110), "A2": (177.32, 394.92, 110), @@ -229,19 +242,37 @@ async def _main() -> None: hw_api = await build_async_ot3_hardware_api( is_simulating=args.simulate, use_defaults=True ) - tip_length = {"T1K": 85.7, "T50": 57.9} + tip_length = {"T1K": 85.7, "T200": 48.35, "T50": 47.9} pipette_model = hw_api._pipette_handler.hardware_instruments[mount].name - dial_data = {"Tip": None, "Tip Height": None} - details = [pipette_model] + dial_data = {"Tip": None, "Tip Height": None, "Motor Current": None} + m_current = float(input("motor_current in amps: ")) + pick_up_speed = float(input("pick up tip speed in mm/s: ")) + details = [pipette_model, m_current] test_n, test_f = file_setup(dial_data, details) + file_name = ( + "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s.csv" + % ( + m_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M") + ) + ) + print(file_name) print(test_n) print(test_f) - await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) + await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) await hw_api.home_plunger(mount) plunger_pos = get_plunger_positions_ot3(hw_api, mount) home_position = await hw_api.current_position_ot3(mount) + start_time = time.perf_counter() global stop_threads global motion + if args.tip_size == "T1K": + home_with_tip_position = 164.3 # T1K + if args.tip_size == "T200": + home_with_tip_position = 201.64999999999998 # T1K + elif args.tip_size == "T50": + home_with_tip_position = 192.1 # T50 + if args.fg_jog: cp = CriticalPoint.NOZZLE await hw_api.move_to( @@ -256,6 +287,13 @@ async def _main() -> None: print("Move to Force Gauge") fg_loc = await jog(hw_api, current_position, cp) fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(mount)]] + await hw_api.move_to( + mount, + Point( + fg_loc[0], fg_loc[1], home_position[OT3Axis.by_mount(mount)] + ), + critical_point=CriticalPoint.TIP, + ) await hw_api.home_z(mount, allow_home_other=False) if args.tiprack: @@ -271,16 +309,42 @@ async def _main() -> None: print("Move to Tiprack") current_position = await hw_api.current_position_ot3(mount) tiprack_loc = await jog(hw_api, current_position, cp) + # Start recording the encoder init_tip_loc = await hw_api.encoder_current_position_ot3( mount, CriticalPoint.NOZZLE ) + print(f"Start encoder: {init_tip_loc}") + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + await update_pick_up_current(hw_api, mount, m_current) + await update_pickup_tip_speed(hw_api, mount, pick_up_speed) # Move pipette to Force Gauge press location final_tip_loc = await hw_api.pick_up_tip( mount, tip_length=tip_length[args.tip_size] ) - home_with_tip = await hw_api.current_position_ot3( - mount, critical_point=CriticalPoint.TIP + await home_ot3(hw_api, [OT3Axis.by_mount(mount)]) + home_with_tip_position = await hw_api.current_position_ot3(mount, critical_point= CriticalPoint.TIP) + await hw_api.move_to( + mount, + Point( + current_position[OT3Axis.X], + current_position[OT3Axis.Y], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, ) + + encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] + # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + print(f"End Encoder: {final_tip_loc}") + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + location = "Tiprack" + tip_count = 1 + test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + enc_record(file_name, test_details) tiprack_loc = [ tiprack_loc[OT3Axis.X], tiprack_loc[OT3Axis.Y], @@ -293,18 +357,31 @@ async def _main() -> None: Point( slot_loc["C2"][0], slot_loc["C2"][1], - home_with_tip[OT3Axis.by_mount(mount)], + home_with_tip_position[OT3Axis.by_mount(mount)], ), ) cp = CriticalPoint.TIP print("Move to Tiprack") current_position = await hw_api.current_position_ot3(mount) dial_loc = await jog(hw_api, current_position, cp) + await asyncio.sleep(1) + tip_measurement = gauge.read() + await asyncio.sleep(2) + d_str = f"{m_current}, {pipette_model}, {tip_measurement}, Tip \n" + data.append_data_to_file(test_n, test_f, d_str) dial_loc = [ dial_loc[OT3Axis.X], dial_loc[OT3Axis.Y], dial_loc[OT3Axis.by_mount(mount)], ] + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[2], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) if args.trough: await hw_api.move_to( @@ -312,7 +389,7 @@ async def _main() -> None: Point( slot_loc["B3"][0], slot_loc["B3"][1], - home_with_tip[OT3Axis.by_mount(mount)], + home_with_tip_position[OT3Axis.by_mount(mount)], ), ) cp = CriticalPoint.TIP @@ -326,7 +403,11 @@ async def _main() -> None: ] await hw_api.move_to( mount, - Point(trough_loc[0], trough_loc[1], home_with_tip[OT3Axis.by_mount(mount)]), + Point( + trough_loc[0], + trough_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), critical_point=CriticalPoint.TIP, ) # Move to trash slot @@ -335,10 +416,11 @@ async def _main() -> None: Point( slot_loc["A3"][0] + 50, slot_loc["A3"][1] - 20, - home_with_tip[OT3Axis.by_mount(mount)], + home_with_tip_position[OT3Axis.by_mount(mount)], ), critical_point=CriticalPoint.TIP, ) + input("Feel the Tip!") await hw_api.drop_tip(mount) lp_file_name = "/var/pressure_sensor_data_P-{}_Z-{}-{}.csv".format( @@ -355,213 +437,273 @@ async def _main() -> None: aspirate_while_sensing=False, data_file=lp_file_name, ) + tip_count = 0 + x_offset = 0 + y_offset = 0 try: - while True: + + for tip in range(2, tips_to_use + 1): + tip_count += 1 + y_offset -= 9 + if tip_count % 8 == 0: + y_offset = 0 + if tip_count % 8 == 0: + x_offset += 9 # #-----------------------Force Gauge----------------------------------- - # Set Motor current by tester - m_current = float(input("motor_current in amps: ")) - await hw_api.move_to( - mount, - Point(fg_loc[0], fg_loc[1], home_position[OT3Axis.by_mount(mount)]), - ) - # # Move pipette to Force Gauge calibrated location - await hw_api.move_to(mount, Point(fg_loc[0], fg_loc[1], fg_loc[2])) - init_fg_loc = await hw_api.encoder_current_position_ot3( - mount, CriticalPoint.NOZZLE - ) - init_fg_loc = init_fg_loc[OT3Axis.by_mount(mount)] - location = "Force_Gauge" - force_thread = Thread( - target=force_record, - args=( - m_current, - location, - pipette_model, - ), - ) - force_thread.start() - await update_pick_up_current(hw_api, mount, m_current) - init_tip_loc = await hw_api.encoder_current_position_ot3( - mount, CriticalPoint.NOZZLE - ) - # Move pipette to Force Gauge press location - final_tip_loc = await hw_api.pick_up_tip( - mount, tip_length=tip_length[args.tip_size] - ) + if args.fg: + # Set Motor current by tester + await hw_api.move_to( + mount, + Point( + fg_loc[0], + fg_loc[1], + home_position[OT3Axis.by_mount(mount)], + ), + ) + # # Move pipette to Force Gauge calibrated location + await hw_api.move_to(mount, Point(fg_loc[0], fg_loc[1], fg_loc[2])) + init_fg_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + init_fg_loc = init_fg_loc[OT3Axis.by_mount(mount)] + location = "Force_Gauge" + force_thread = Thread( + target=force_record, + args=( + m_current, + location, + pipette_model, + ), + ) + motion = True + # stop_threads = False + force_thread.start() + await update_pick_up_current(hw_api, mount, m_current) + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + await update_pickup_tip_speed(hw_api, mount, pick_up_speed) + print("I'm performing pick_up_tip") + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + print(final_tip_loc) + await asyncio.sleep(1) + motion = False + # stop_threads = True + await asyncio.sleep(1) + force_thread.join() # Thread Finished + await hw_api.remove_tip(mount) + await hw_api.home_z(mount, allow_home_other=False) + print("I'm here") + # await hw_api.move_to( + # mount, + # Point( + # fg_loc[0], + # fg_loc[1], + # home_with_tip_position[OT3Axis.by_mount(mount)], + # ), + # critical_point=CriticalPoint.TIP, + # ) + await asyncio.sleep(2) + final_fg_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + final_fg_loc = final_fg_loc[OT3Axis.by_mount(mount)] - home_with_tip = await hw_api.current_position_ot3( - mount, critical_point=CriticalPoint.TIP - ) - await asyncio.sleep(2) - final_fg_loc = await hw_api.encoder_current_position_ot3( - mount, CriticalPoint.NOZZLE - ) - final_fg_loc = final_fg_loc[OT3Axis.by_mount(mount)] - print(final_fg_loc) - motion = False - stop_threads = True - force_thread.join() # Thread Finished - await hw_api.remove_tip(mount) - await hw_api.home_z(mount, allow_home_other=False) - # -----------------------Tiprack------------------------------------ - # Move over to the TipRack location and - await hw_api.move_to( - mount, - Point( - tiprack_loc[0], - tiprack_loc[1], - home_position[OT3Axis.by_mount(mount)], - ), - ) - # Move Pipette to top of Tip Rack Location - await hw_api.move_to( - mount, Point(tiprack_loc[0], tiprack_loc[1], tiprack_loc[2]), speed=65 - ) - location = "Tiprack" - global encoder_end - encoder_end = None - # Start recording the encoder - init_tip_loc = await hw_api.encoder_current_position_ot3( - mount, CriticalPoint.NOZZLE - ) - print(f"Start encoder: {init_tip_loc}") - init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] - encoder_position = init_tip_loc - # Press Pipette into the tip - await update_pick_up_current(hw_api, mount, m_current) - # Move pipette to Force Gauge press location - final_tip_loc = await hw_api.pick_up_tip( - mount, tip_length=tip_length[args.tip_size] - ) - home_with_tip = await hw_api.current_position_ot3( - mount, critical_point=CriticalPoint.TIP - ) - encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] - # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) - print(f"End Encoder: {final_tip_loc}") - final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] - enc_record(m_current, location, init_tip_loc, final_tip_loc) - # Home Z - await hw_api.home([OT3Axis.by_mount(mount)]) - # Move over to the dial indicator - await hw_api.move_to( - mount, - Point(dial_loc[0], dial_loc[1], home_with_tip[OT3Axis.by_mount(mount)]), - ) - # Move over to the dial indicator - await hw_api.move_to( - mount, - Point(dial_loc[0], dial_loc[1], dial_loc[2]), - ) - await asyncio.sleep(1) - tip_measurement = gauge.read() - d_str = f"{pipette_model}, {tip_measurement}, Tip \n" - data.append_data_to_file(test_n, test_f, d_str) - # Move over to the dial indicator - await hw_api.move_to( - mount, - Point(dial_loc[0], dial_loc[1], home_with_tip[OT3Axis.by_mount(mount)]), - ) - # -----------------------Aspirate----------------------------------- - await hw_api.move_to( - mount, - Point( - trough_loc[0], trough_loc[1], home_with_tip[OT3Axis.by_mount(mount)] - ), - ) - # Move to offset from trough - await hw_api.move_to( - mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) - ) - # Move the plunger to the top position - await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[0]) - # Liquid Probe - liquid_height = await hw_api.liquid_probe( - mount, probe_settings=liquid_probe_settings - ) - print("I'm here") - liquid_height = await hw_api.current_position_ot3( - mount, critical_point=CriticalPoint.TIP - ) - # await move_plunger_relative_ot3(hw_api, mount, 1.5, None, speed = 2) # P50S - await move_plunger_relative_ot3(hw_api, mount, 0.25, None, speed=2) # P1KS - await hw_api.move_to( - mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) - ) - # Prepare to aspirate before descending to trough well - await hw_api.prepare_for_aspirate(mount) - # Descend to aspirate depth - await hw_api.move_to( - mount, - Point( - liquid_height[OT3Axis.X], - liquid_height[OT3Axis.Y], - liquid_height[OT3Axis.by_mount(mount)] - aspirate_depth, - ), - speed=5, - critical_point=CriticalPoint.TIP, - ) - # Aspirate - await hw_api.aspirate(mount) - cur_pos = await hw_api.current_position_ot3( - mount, critical_point=CriticalPoint.TIP - ) - z_pos = cur_pos[OT3Axis.by_mount(mount)] - # Retract from liquid with retract speed - await hw_api.move_to( - mount, - Point(trough_loc[0], trough_loc[1], z_pos + liquid_retract_dist), - speed=liquid_retract_speed, - critical_point=CriticalPoint.TIP, - ) - await hw_api.move_to( - mount, - Point( - trough_loc[0], trough_loc[1], home_with_tip[OT3Axis.by_mount(mount)] - ), - critical_point=CriticalPoint.TIP, - ) - await countdown(count_time=leak_test_time) - input("Check to see if the pipette is leaking") - await hw_api.move_to( - mount, - Point(trough_loc[0], trough_loc[1], trough_loc[2]), - critical_point=CriticalPoint.TIP, - ) - await hw_api.move_to( - mount, - Point(trough_loc[0], trough_loc[1], trough_loc[2] - aspirate_depth), - speed=5, - critical_point=CriticalPoint.TIP, - ) - await hw_api.dispense(mount) - await hw_api.blow_out(mount) + # -----------------------Tiprack------------------------------------ + if args.tiprack: + # Move over to the TipRack location and + await hw_api.move_to( + mount, + Point( + tiprack_loc[0] + x_offset, + tiprack_loc[1] + y_offset, + home_position[OT3Axis.by_mount(mount)] , + ), + ) + + # Move Pipette to top of Tip Rack Location + await hw_api.move_to( + mount, + Point( + tiprack_loc[0] + x_offset, tiprack_loc[1] + y_offset, tiprack_loc[2] + ), + speed=65, + ) + location = "Tiprack" + + # Start recording the encoder + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + print(f"Start encoder: {init_tip_loc}") + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc + # Press Pipette into the tip + await update_pick_up_current(hw_api, mount, m_current) + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + await hw_api.move_to( + mount, + Point( + tiprack_loc[0] + x_offset, + tiprack_loc[1] + y_offset, + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + # home_with_tip_position = await hw_api.current_position_ot3( + # mount, critical_point=CriticalPoint.TIP + # ) + encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] + # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + print(f"End Encoder: {final_tip_loc}") + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + enc_record(file_name, test_details) + # Home Z + await hw_api.home([OT3Axis.by_mount(mount)]) + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)] , + ), + ) + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point(dial_loc[0], dial_loc[1], dial_loc[2]), + ) + await asyncio.sleep(3) + tip_measurement = gauge.read() + d_str = f"{m_current}, {pipette_model}, {tip_measurement}, Tip \n" + data.append_data_to_file(test_n, test_f, d_str) + await asyncio.sleep(1) + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)] , + ), + ) + # -----------------------Aspirate----------------------------------- + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + # Move to offset from trough + await hw_api.move_to( + mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) + ) + # Move the plunger to the top position + await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[0]) + # Liquid Probe + liquid_height = await hw_api.liquid_probe( + mount, probe_settings=liquid_probe_settings + ) + + liquid_height = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + # await move_plunger_relative_ot3(hw_api, mount, 1.5, None, speed = 2) # P50S + await move_plunger_relative_ot3(hw_api, mount, 0.25, None, speed=2) # P1KS + await hw_api.move_to( + mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) + ) + # Prepare to aspirate before descending to trough well + await hw_api.prepare_for_aspirate(mount) + # Descend to aspirate depth + await hw_api.move_to( + mount, + Point( + liquid_height[OT3Axis.X], + liquid_height[OT3Axis.Y], + liquid_height[OT3Axis.by_mount(mount)] - aspirate_depth, + ), + speed=5, + critical_point=CriticalPoint.TIP, + ) + # Aspirate + await hw_api.aspirate(mount, 200) + cur_pos = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + z_pos = cur_pos[OT3Axis.by_mount(mount)] + # Retract from liquid with retract speed + await hw_api.move_to( + mount, + Point(trough_loc[0], trough_loc[1], z_pos + liquid_retract_dist), + speed=liquid_retract_speed, + critical_point=CriticalPoint.TIP, + ) + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)] , + ), + critical_point=CriticalPoint.TIP, + ) + await countdown(count_time=leak_test_time) + # input("Check to see if the pipette is leaking") + await hw_api.move_to( + mount, + Point(trough_loc[0], trough_loc[1], trough_loc[2]), + critical_point=CriticalPoint.TIP, + ) + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + liquid_height[OT3Axis.by_mount(mount)] - dispense_depth, + ), + speed=5, + critical_point=CriticalPoint.TIP, + ) + await hw_api.dispense(mount) + await hw_api.blow_out(mount) # --------------------Drop Tip-------------------------------------- - current_position = await hw_api.current_position_ot3( - mount, critical_point=CriticalPoint.TIP - ) - await hw_api.move_to( - mount, - Point( - trough_loc[0], trough_loc[1], home_with_tip[OT3Axis.by_mount(mount)] - ), - critical_point=CriticalPoint.TIP, - ) - input("Feel the Tip") - # Move to trash slot - await hw_api.move_to( - mount, - Point( - slot_loc["A3"][0] + 50, - slot_loc["A3"][1] - 20, - home_with_tip[OT3Axis.by_mount(mount)], - ), - critical_point=CriticalPoint.TIP, - ) - await hw_api.drop_tip(mount) + current_position = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1] - 20, + home_with_tip_position[OT3Axis.by_mount(mount)] , + ), + critical_point=CriticalPoint.TIP, + ) + await hw_api.drop_tip(mount) await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) except KeyboardInterrupt: @@ -578,7 +720,7 @@ def force_record(motor_current, location, pipette_model): "/home/root/.opentrons/testing_data/force_data/force_pu_test_%s-%s-%s.csv" % ( motor_current, - datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + datetime.datetime.now().strftime("%m-%d-%y_%H-%M-%S"), location, ) ) @@ -595,18 +737,22 @@ def force_record(motor_current, location, pipette_model): start_time = time.perf_counter() try: motion = True - stop_threads = False + force_data = [] while motion: reading = float(fg.read_force()) - test_data["Time(s)"] = time.perf_counter() - start_time - test_data["Force(N)"] = reading + time_elasped = time.perf_counter() - start_time + force_data.append((time_elasped, reading)) + print(f"time(s): {time_elasped}, Force: {reading}") + + for t, force in force_data: + test_data["Time(s)"] = t + test_data["Force(N)"] = force test_data["M_current(amps)"] = motor_current test_data["pipette_model"] = pipette_model log_file.writerow(test_data) print(test_data) f.flush() - if stop_threads: - break + except KeyboardInterrupt: print("Test Cancelled") test_data["Errors"] = "Test Cancelled" @@ -621,31 +767,25 @@ def force_record(motor_current, location, pipette_model): f.close() -def enc_record(motor_current, location, init_enc_pos, final_enc_pos): - file_name = ( - "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s-%s.csv" - % ( - motor_current, - datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), - location, - ) - ) - print(file_name) - with open(file_name, "w", newline="") as f: +def enc_record(f_name, t_data): + #test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + with open(f_name, "a", newline="") as f: test_data = { "time(s)": None, "motor_current": None, - "start_enc_pos": None, - "end_enc_pos(mm)": None, + "location": None, + "start_enc_pos(mm)": None, + "end_enc_pos(mm)": None } log_file = csv.DictWriter(f, test_data) - log_file.writeheader() - start_time = time.perf_counter() + if t_data[5] < 1: + log_file.writeheader() try: - test_data["time(s)"] = time.perf_counter() - start_time - test_data["motor_current"] = motor_current - test_data["start_enc_pos(mm)"] = init_enc_pos - test_data["end_enc_pos(mm)"] = final_enc_pos + test_data["time(s)"] = time.perf_counter() - t_data[0] + test_data["motor_current"] = t_data[1] + test_data["location"] = t_data[2] + test_data["start_enc_pos(mm)"] = t_data[3] + test_data["end_enc_pos(mm)"] = t_data[4] log_file.writerow(test_data) print(test_data) f.flush() @@ -680,20 +820,20 @@ def enc_record(motor_current, location, init_enc_pos, final_enc_pos): ] parser = argparse.ArgumentParser() parser.add_argument("--simulate", action="store_true") - parser.add_argument("--fg_jog", action="store_true", default=True) - parser.add_argument("--trough", action="store_true", default=True) - parser.add_argument("--tiprack", action="store_true", default=True) + parser.add_argument("--fg_jog", action="store_true") + parser.add_argument("--trough", action="store_true") + parser.add_argument("--tiprack", action="store_true") parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") - parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C2") + parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C1") parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") parser.add_argument("--fg", action="store_true", default=True) - parser.add_argument("--dial_indicator", action="store_true", default=True) - parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") + parser.add_argument("--dial_indicator", action="store_true") + parser.add_argument("--tip_size", type=str, default="T200", help="Tip Size") parser.add_argument("--max_z_distance", type=float, default=40) parser.add_argument("--min_z_distance", type=float, default=5) - parser.add_argument("--mount_speed", type=float, default=5) - parser.add_argument("--plunger_speed", type=float, default=11) + parser.add_argument("--mount_speed", type=float, default=11) + parser.add_argument("--plunger_speed", type=float, default=21) parser.add_argument( "--sensor_threshold", type=float, default=160, help="Threshold in Pascals" ) @@ -712,6 +852,5 @@ def enc_record(motor_current, location, init_enc_pos, final_enc_pos): fg.connect() if args.dial_indicator: - gauge = dial_indicator_setup() asyncio.run(_main()) From 674af3f12c9773e6bc92a842fe918f403c5aeb79 Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 29 Mar 2023 09:45:12 -0400 Subject: [PATCH 12/53] remove volume --- hardware-testing/hardware_testing/scripts/pick_up_tip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/pick_up_tip.py b/hardware-testing/hardware_testing/scripts/pick_up_tip.py index 282c73a26ff..5ff0bd84cdb 100644 --- a/hardware-testing/hardware_testing/scripts/pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/pick_up_tip.py @@ -640,7 +640,7 @@ async def _main() -> None: critical_point=CriticalPoint.TIP, ) # Aspirate - await hw_api.aspirate(mount, 200) + await hw_api.aspirate(mount) cur_pos = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) @@ -829,7 +829,7 @@ def enc_record(f_name, t_data): parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") parser.add_argument("--fg", action="store_true", default=True) parser.add_argument("--dial_indicator", action="store_true") - parser.add_argument("--tip_size", type=str, default="T200", help="Tip Size") + parser.add_argument("--tip_size", type=str, default="T50", help="Tip Size") parser.add_argument("--max_z_distance", type=float, default=40) parser.add_argument("--min_z_distance", type=float, default=5) parser.add_argument("--mount_speed", type=float, default=11) From 7fe57a0027605a15ebff167ad27045b1b96095bb Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 29 Mar 2023 09:45:50 -0400 Subject: [PATCH 13/53] drop tip test scripts --- .../scripts/drop_tip_force_test.py | 389 +++++++++++++ .../hardware_testing/scripts/drop_tip_test.py | 519 ++++++++++++++++++ 2 files changed, 908 insertions(+) create mode 100644 hardware-testing/hardware_testing/scripts/drop_tip_force_test.py create mode 100644 hardware-testing/hardware_testing/scripts/drop_tip_test.py diff --git a/hardware-testing/hardware_testing/scripts/drop_tip_force_test.py b/hardware-testing/hardware_testing/scripts/drop_tip_force_test.py new file mode 100644 index 00000000000..f42e5d4aa72 --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/drop_tip_force_test.py @@ -0,0 +1,389 @@ +"""Demo OT3 Gantry Functionality.""" +import argparse +import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os +import sys +import termios +import tty +import json + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + OT3Mount, + OT3Axis, + Point, + CriticalPoint, +) +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + move_plunger_absolute_ot3, + move_plunger_relative_ot3, + get_plunger_positions_ot3, + update_pick_up_current, + update_pick_up_distance, + update_drop_tip_current, + _get_pipette_from_mount +) + +from hardware_testing import data +from hardware_testing.drivers.mark10 import Mark10 + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + +def file_setup(test_data, details): + today = datetime.date.today() + test_name = "{}-drop_tip-force-test-{}Amps".format( + details[0], # Pipette model + details[1], # Motor Current + ) + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[OT3Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") + return position + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + +async def update_drop_tip_distance(api, mount, position) -> None: + pipette = _get_pipette_from_mount(api, mount) + pipette.plunger_positions.drop_tip = position + print(pipette.plunger_positions) + +async def update_drop_tip_speed(api, mount, speed) -> None: + """Update drop-tip current.""" + pipette = _get_pipette_from_mount(api, mount) + config_model = pipette._drop_configurations + config_model.speed = speed + pipette._drop_configurations = config_model + print(pipette._drop_configurations) + +async def _main() -> None: + today = datetime.date.today() + tips_to_use = 96 + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.92, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + tip_length = {"T1K": 85.7, "T50": 57.9} + pipette_model = hw_api._pipette_handler.hardware_instruments[mount].name + pipette = _get_pipette_from_mount(hw_api, mount) + dial_data = {"Tip": None, "Tip Height": None, "Motor Current": None} + m_current = 0.2 + await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) + await hw_api.home_plunger(mount) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + home_position = await hw_api.current_position_ot3(mount) + global motion + tip_length = {"T1K": 85.7, "T50": 57.9} + if args.tip_size == "T1K": + home_with_tip_position = 164.3 # T1K + elif args.tip_size == "T50": + home_with_tip_position = 192.1 # T50 + start_time = time.perf_counter() + if args.fg_jog: + cp = CriticalPoint.NOZZLE + await hw_api.move_to( + mount, + Point( + slot_loc["D2"][0], + slot_loc["D2"][1], + home_position[OT3Axis.by_mount(mount)], + ), + ) + current_position = await hw_api.current_position_ot3(mount) + print("Move to Force Gauge") + fg_loc = await jog(hw_api, current_position, cp) + fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(mount)]] + + + try: + cycle = 1 + drop_tip_distance = pipette.plunger_positions.drop_tip + 12 + await update_drop_tip_distance(hw_api, mount, drop_tip_distance) + await update_drop_tip_speed(hw_api, mount, 10) + await hw_api._backend.set_hold_current({OT3Axis.X: 1.0, + OT3Axis.Y: 1.0, + OT3Axis.Z_L: 1.0, + OT3Axis.Z_R: 1.0}) + + while True: + d_current = float(input("Enter Drop Tip Current: ")) + await hw_api.add_tip(mount, tip_length[args.tip_size]) + # --------------------Drop Tip-------------------------------------- + details = [pipette_model, d_current, stop_event = Event()] + # Move the plunger to the bottom position + await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[1]) + if cycle == 1: + headers = True + else: + header = False + force_thread = Thread( + target=force_record, + args=( + details, + headers, + ), + ) + force_thread.start() + # update the drop tip current + await update_drop_tip_current(hw_api, mount, d_current) + + # obtain the encoder position + final_drop_tip_position = await hw_api.drop_tip(mount, home_after = False) + details = [ + pipette_model, + d_current + ] + await asyncio.sleep(1) + motion = False + await hw_api.home_plunger(mount) + await asyncio.sleep(1) + force_thread.join() # Thread Finished + print(f"drop tip current: {pipette._drop_configurations.current}") + cycle += 1 + + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + + +def force_record(t_data, header): + global motion + file_name = ( + "/home/root/.opentrons/testing_data/drop_tip_force_data/dt_force_test_%s_%s-%s-amps.csv" + % ( + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + t_data[0], + t_data[1], + ) + ) + print(file_name) + with open(file_name,"w", newline="") as f: + test_data = { + "Time(s)": None, + "Pipette Model": None, + "Plunger Force(N)": None, + "Dropt Current(Amps)": None, + } + log_file = csv.DictWriter(f, test_data) + if header == True: + log_file.writeheader() + start_time = time.perf_counter() + try: + force_data = [] + motion = True + while motion: + reading = float(fg.read_force()) + time_elasped = time.perf_counter() - start_time + force_data.append((reading, time_elasped)) + print(f"time(s): {time_elasped}, Force: {reading}") + + for force, t in force_data: + test_data["Time(s)"] = t + test_data["Pipette Model"] = t_data[0] + test_data["Plunger Force(N)"] = force + test_data["Dropt Current(Amps)"] = t_data[1] + log_file.writerow(test_data) + print(test_data) + f.flush() + print(f"Peak Force: {max(force_data)}") + + except KeyboardInterrupt: + print("Test Cancelled") + test_data["Errors"] = "Test Cancelled" + f.flush() + except Exception as e: + print("ERROR OCCURED") + test_data["Errors"] = e + f.flush() + raise e + print("Test done") + f.flush() + f.close() + + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--tiprack", action="store_true", default=True) + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--fg", action="store_true", default=True) + parser.add_argument("--fg_jog", action="store_true", default=True) + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") + parser.add_argument( + "--port", type=str, default="/dev/ttyUSB0", help="Force Gauge Port" + ) + args = parser.parse_args() + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + if args.fg: + fg = Mark10.create(port=args.port) + fg.connect() + asyncio.run(_main()) diff --git a/hardware-testing/hardware_testing/scripts/drop_tip_test.py b/hardware-testing/hardware_testing/scripts/drop_tip_test.py new file mode 100644 index 00000000000..4870969b84a --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/drop_tip_test.py @@ -0,0 +1,519 @@ +"""Demo OT3 Gantry Functionality.""" +import argparse +import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os +import sys +import termios +import tty +import json + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + OT3Mount, + OT3Axis, + Point, + CriticalPoint, +) +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + move_plunger_absolute_ot3, + move_plunger_relative_ot3, + get_plunger_positions_ot3, + update_pick_up_current, + update_pick_up_distance, + update_drop_tip_current, + _get_pipette_from_mount +) + +from hardware_testing import data + + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + + +def file_setup(test_data, details): + today = datetime.date.today() + test_name = "{}-pick_up-up-test-{}Amps".format( + details[0], # Pipette model + details[1], # Motor Current + ) + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[OT3Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") + return position + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + + +async def _main() -> None: + today = datetime.date.today() + tips_to_use = 96 + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.92, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + tip_length = {"T1K": 85.7, "T50": 57.9} + pipette_model = hw_api._pipette_handler.hardware_instruments[mount].name + pipette = _get_pipette_from_mount(hw_api, mount) + dial_data = {"Tip": None, "Tip Height": None, "Motor Current": None} + m_current = 0.2 + await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) + await hw_api.home_plunger(mount) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + home_position = await hw_api.current_position_ot3(mount) + + file_name = ( + "/home/root/.opentrons/testing_data/drop_tip_test/enc_drop_tip_test_%s-%s-%s.csv" + % ( + m_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + pipette_model, + ) + ) + print(file_name) + if args.tip_size == "T1K": + home_with_tip_position = 164.3 # T1K + elif args.tip_size == "T50": + home_with_tip_position = 192.1 # T50 + start_time = time.perf_counter() + if args.tiprack: + await hw_api.move_to( + mount, + Point( + slot_loc["B2"][0], + slot_loc["B2"][1], + home_position[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.NOZZLE + print("Move to Tiprack") + current_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await jog(hw_api, current_position, cp) + # Start recording the encoder + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + print(f"Start encoder: {init_tip_loc}") + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + await update_pick_up_current(hw_api, mount, m_current) + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + await home_ot3(hw_api, [OT3Axis.by_mount(mount)]) + home_with_tip = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + print(f"home position with tip:{home_with_tip}") + await hw_api.move_to( + mount, + Point( + current_position[OT3Axis.X], + current_position[OT3Axis.Y], + home_with_tip_position, + ), + critical_point=CriticalPoint.TIP, + ) + + encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] + # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + print(f"End Encoder: {final_tip_loc}") + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + location = "Calibration" + details = [ + start_time, + pipette_model, + m_current, + location, + init_tip_loc, + final_tip_loc, + None + ] + enc_record(file_name, details, True) + tiprack_loc = [ + tiprack_loc[OT3Axis.X], + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)], + ] + # --------------------Drop Tip-------------------------------------- + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1] - 20, + home_with_tip[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + # await update_drop_tip_current(hw_api, mount, d_current) + # obtain the encoder position + init_drop_tip_position = await hw_api.encoder_current_position_ot3(mount) + # obtain the encoder position + final_drop_tip_position = await hw_api.drop_tip(mount) + location = 'Trash' + details = [ + start_time, + pipette_model, + m_current, + location, + init_tip_loc, + final_tip_loc, + None + ] + enc_record(file_name, details, False) + print(f"drop tip current: {pipette._drop_configurations.current}") + + tip_count = 0 + x_offset = 0 + y_offset = 0 + + try: + for tip in range(2, tips_to_use + 1): + d_current = 0.3 + # d_current = float(input("Enter Drop Tip Current: ")) + tip_count += 1 + y_offset -= 9 + if tip_count % 8 == 0: + y_offset = 0 + if tip_count % 8 == 0: + x_offset += 9 + await hw_api.home_z(mount, allow_home_other=False) + # -----------------------Tiprack------------------------------------ + # Move over to the TipRack location and + await hw_api.move_to( + mount, + Point( + tiprack_loc[0] + x_offset, + tiprack_loc[1] + y_offset, + home_position[OT3Axis.by_mount(mount)], + ), + ) + + # Move Pipette to top of Tip Rack Location + await hw_api.move_to( + mount, + Point( + tiprack_loc[0] + x_offset, tiprack_loc[1] + y_offset, tiprack_loc[2] + ), + ) + + # Start recording the encoder + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + print(f"Start encoder: {init_tip_loc[OT3Axis.by_mount(mount)]}") + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc + # Press Pipette into the tip + await update_pick_up_current(hw_api, mount, m_current) + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + await hw_api.move_to( + mount, + Point( + tiprack_loc[0] + x_offset, + tiprack_loc[1] + y_offset, + home_with_tip[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + print(f"End Encoder: {final_tip_loc}") + location = "Tiprack" + details = [ + start_time, + pipette_model, + m_current, + location, + init_tip_loc, + final_tip_loc, + d_current + ] + enc_record(file_name, details, False) + # Home Z + await hw_api.home([OT3Axis.by_mount(mount)]) + + # --------------------Drop Tip-------------------------------------- + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1] - 20, + home_with_tip[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + # Move the plunger to the top position + await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[1]) + # update the drop tip current + await update_drop_tip_current(hw_api, mount, d_current) + # obtain the encoder position + init_drop_tip_position = await hw_api.encoder_current_position_ot3(mount) + # obtain the encoder position + final_drop_tip_position = await hw_api.drop_tip(mount, home_after = False) + location = 'Trash' + details = [ + start_time, + pipette_model, + m_current, + location, + init_drop_tip_position[OT3Axis.of_main_tool_actuator(mount)], + final_drop_tip_position[OT3Axis.of_main_tool_actuator(mount)], + d_current + ] + enc_record(file_name, details, False) + await hw_api.home_plunger(mount) + safety_margin = 1 + drop_tip_distance_target = 19 - safety_margin + delta = final_drop_tip_position[OT3Axis.of_main_tool_actuator(mount)] \ + - init_drop_tip_position[OT3Axis.of_main_tool_actuator(mount)] + if delta < drop_tip_distance_target: + print("Test Fail") + break + print(f"drop tip current: {pipette._drop_configurations.current}") + + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + + +def enc_record(file_name, t_data, header): + + with open(file_name, "a", newline="") as f: + test_data = { + "Time(s)": None, + "Pipette Model": None, + "Location": None, + "Motor Current(Amps)":+ None, + "Initial Enc Pos(mm)": None, + "Final Enc Pos(mm)": None, + "Dropt Current(Amps)": None, + "Remaining Distance(mm)": None, + "Pass/Fail": None + } + log_file = csv.DictWriter(f, test_data) + if header == True: + log_file.writeheader() + try: + test_data["Time(s)"] = time.perf_counter() - t_data[0] + test_data["Pipette Model"] = t_data[1] + test_data["Motor Current(Amps)"] = t_data[2] + test_data["Location"] = t_data[3] + test_data["Initial Enc Pos(mm)"] = t_data[4] + test_data["Final Enc Pos(mm)"] = t_data[5] + test_data["Dropt Current(Amps)"] = t_data[6] + test_data["Remaining Distance(mm)"] = t_data[5]-t_data[4] + + if t_data[3] == 'Trash': + print(f"initial P: {t_data[4]}, final P: {t_data[5]}") + delta = t_data[5] - t_data[4] + if delta < 0.1: + test_data["Pass/Fail"] = 'PASS' + else: + test_data["Pass/Fail"] = 'FAIL' + else: + print(f"initial Z: {t_data[4]}, final Z: {t_data[5]}") + test_data["Pass/Fail"] = None + log_file.writerow(test_data) + f.flush() + except KeyboardInterrupt: + print("Test Cancelled") + test_data["Errors"] = "Test Cancelled" + f.flush() + except Exception as e: + print("ERROR OCCURED") + test_data["Errors"] = e + f.flush() + raise e + print("Test done") + f.flush() + f.close() + + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--tiprack", action="store_true", default=True) + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") + args = parser.parse_args() + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + + asyncio.run(_main()) From 2c33de5421664faa5fb21c25fa17f00223c8c907 Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 29 Mar 2023 09:46:01 -0400 Subject: [PATCH 14/53] jogging test script --- .../hardware_testing/scripts/jog.py | 275 ++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 hardware-testing/hardware_testing/scripts/jog.py diff --git a/hardware-testing/hardware_testing/scripts/jog.py b/hardware-testing/hardware_testing/scripts/jog.py new file mode 100644 index 00000000000..35f67510080 --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/jog.py @@ -0,0 +1,275 @@ +"""Demo OT3 Gantry Functionality.""" +import argparse +import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os +import sys +import termios +import tty +import json + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + OT3Mount, + OT3Axis, + Point, + CriticalPoint, +) +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + move_plunger_absolute_ot3, + move_plunger_relative_ot3, + get_plunger_positions_ot3, + update_pick_up_current, + update_pick_up_distance, + update_drop_tip_current, + _get_pipette_from_mount +) + +from hardware_testing import data +from hardware_testing.drivers.mark10 import Mark10 + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + +def file_setup(test_data, details): + today = datetime.date.today() + test_name = "{}-drop_tip-force-test-{}Amps".format( + details[0], # Pipette model + details[1], # Motor Current + ) + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[OT3Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") + return position + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + +async def update_drop_tip_distance(api, mount, position) -> None: + pipette = _get_pipette_from_mount(api, mount) + pipette.plunger_positions.drop_tip = position + + +async def _main() -> None: + today = datetime.date.today() + tips_to_use = 96 + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.92, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + tip_length = {"T1K": 85.7, "T50": 57.9} + pipette_model = hw_api._pipette_handler.hardware_instruments[mount].name + pipette = _get_pipette_from_mount(hw_api, mount) + dial_data = {"Tip": None, "Tip Height": None, "Motor Current": None} + m_current = 0.2 + await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) + await hw_api.home_plunger(mount) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + home_position = await hw_api.current_position_ot3(mount) + tip_length = {"T1K": 85.7, "T50": 57.9} + if args.tip_size == "T1K": + home_with_tip_position = 164.3 # T1K + elif args.tip_size == "T50": + home_with_tip_position = 192.1 # T50 + start_time = time.perf_counter() + + try: + await hw_api.move_to( + mount, + Point( + slot_loc[args.slot][0], + slot_loc[args.slot][1], + home_position[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.NOZZLE + current_position = await hw_api.current_position_ot3(mount) + jog_loc = await jog(hw_api, current_position, cp) + + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--tiprack", action="store_true", default=True) + parser.add_argument("--slot", default = "D2") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--fg", action="store_true", default=True) + parser.add_argument("--fg_jog", action="store_true", default=True) + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") + parser.add_argument( + "--port", type=str, default="/dev/ttyUSB0", help="Force Gauge Port" + ) + args = parser.parse_args() + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + asyncio.run(_main()) From 3590635242607b05140d8437c8f04c52d9bc5888 Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 29 Mar 2023 09:46:22 -0400 Subject: [PATCH 15/53] add drop tip current to ot3_helpers --- .../hardware_testing/opentrons_api/helpers_ot3.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py index 949359841e3..1d8fb9722e0 100644 --- a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py +++ b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py @@ -20,6 +20,9 @@ from opentrons.hardware_control.instruments.ot3.pipette import Pipette as PipetteOT3 from opentrons.hardware_control.motion_utilities import deck_from_machine from opentrons.hardware_control.ot3api import OT3API +from opentrons_hardware.scripts.sensor_utils import SensorRun +from opentrons_hardware.firmware_bindings.constants import NodeId, SensorType +from opentrons_hardware.scripts.sensors import send_sensor_command from .types import ( GantryLoad, @@ -354,6 +357,7 @@ def get_plunger_positions_ot3( ) + async def update_pick_up_current( api: OT3API, mount: OT3Mount, current: float = 0.125 ) -> None: @@ -364,6 +368,16 @@ async def update_pick_up_current( pipette.pick_up_configurations = config_model +async def update_drop_tip_current( + api: OT3API, mount: OT3Mount, current: float = 1.0 +) -> None: + """Update drop-tip current.""" + pipette = _get_pipette_from_mount(api, mount) + config_model = pipette._drop_configurations + config_model.current = current + pipette._drop_configurations = config_model + + async def update_pick_up_distance( api: OT3API, mount: OT3Mount, distance: float = 17.0 ) -> None: From ed260f10c7ccf92733e5059e8d01f70bb9a7bed4 Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 6 Apr 2023 10:05:58 -0400 Subject: [PATCH 16/53] rename scripts --- api/src/opentrons/hardware_control/ot3api.py | 14 +- .../scripts/multi_channel_pick_up_tip.py | 863 ++++++++++++++++++ ...p_tip.py => single_channel_pick_up_tip.py} | 0 3 files changed, 872 insertions(+), 5 deletions(-) create mode 100644 hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py rename hardware-testing/hardware_testing/scripts/{pick_up_tip.py => single_channel_pick_up_tip.py} (100%) diff --git a/api/src/opentrons/hardware_control/ot3api.py b/api/src/opentrons/hardware_control/ot3api.py index 552de967207..bc428150a3d 100644 --- a/api/src/opentrons/hardware_control/ot3api.py +++ b/api/src/opentrons/hardware_control/ot3api.py @@ -1537,7 +1537,7 @@ async def blow_out(self, mount: Union[top_types.Mount, OT3Mount]) -> None: async def _force_pick_up_tip( self, mount: OT3Mount, pipette_spec: PickUpTipSpec - ) -> None: + ) -> Dict[OT3Axis, float]: for press in pipette_spec.presses: async with self._backend.restore_current(): await self._backend.set_active_current( @@ -1550,7 +1550,10 @@ async def _force_pick_up_tip( target_up = target_position_from_relative( mount, press.relative_up, self._current_position ) + await asyncio.sleep(1) + enc_pos = await self.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) await self._move(target_up) + return enc_pos async def _motor_pick_up_tip( self, mount: OT3Mount, pipette_spec: TipMotorPickUpTipSpec @@ -1588,7 +1591,7 @@ async def pick_up_tip( presses: Optional[int] = None, increment: Optional[float] = None, prep_after: bool = True, - ) -> None: + ) -> Dict[OT3Axis, float]: """Pick up tip from current location.""" realmount = OT3Mount.from_mount(mount) spec, _add_tip_to_instrs = self._pipette_handler.plan_check_pick_up_tip( @@ -1598,7 +1601,7 @@ async def pick_up_tip( if spec.pick_up_motor_actions: await self._motor_pick_up_tip(realmount, spec.pick_up_motor_actions) else: - await self._force_pick_up_tip(realmount, spec) + enc_pos = await self._force_pick_up_tip(realmount, spec) # we expect a stall has happened during pick up, so we want to # update the motor estimation @@ -1607,13 +1610,14 @@ async def pick_up_tip( # neighboring tips tend to get stuck in the space between # the volume chamber and the drop-tip sleeve on p1000. # This extra shake ensures those tips are removed - for rel_point, speed in spec.shake_off_list: - await self.move_rel(realmount, rel_point, speed=speed) + # for rel_point, speed in spec.shake_off_list: + # await self.move_rel(realmount, rel_point, speed=speed) _add_tip_to_instrs() if prep_after: await self.prepare_for_aspirate(realmount) + return enc_pos def set_current_tiprack_diameter( self, mount: Union[top_types.Mount, OT3Mount], tiprack_diameter: float diff --git a/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py new file mode 100644 index 00000000000..d916f95d692 --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py @@ -0,0 +1,863 @@ +"""Demo OT3 Gantry Functionality.""" +import argparse +import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os +import sys +import termios +import tty +import json + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + OT3Mount, + OT3Axis, + Point, + CriticalPoint, +) +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + move_plunger_absolute_ot3, + move_plunger_relative_ot3, + get_plunger_positions_ot3, + update_pick_up_current, + update_pick_up_distance, + _get_pipette_from_mount, +) + +from opentrons.config.types import LiquidProbeSettings + +from hardware_testing import data +from hardware_testing.drivers.mark10 import Mark10 +from hardware_testing.drivers import mitutoyo_digimatic_indicator + +aspirate_depth = 7 +dispense_depth = 3 +liquid_retract_dist = 12 +liquid_retract_speed = 5 +retract_dist = 100 +retract_speed = 60 + +leak_test_time = 30 + + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + + +def file_setup(test_data, details): + today = datetime.date.today() + test_name = "{}-pick_up-up-test-{}Amps".format( + details[0], # Pipette model + details[1], # Motor Current + ) + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + + +def dial_indicator_setup(port): + gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator( + port=port + ) + gauge.connect() + return gauge + + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[OT3Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") + return position + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + + +async def countdown(count_time: float): + """ + This function loops through a countdown before checking the leak visually + """ + time_suspend = 0 + while time_suspend < count_time: + await asyncio.sleep(1) + time_suspend += 1 + print(f"Remaining: {count_time-time_suspend} (s)", end="") + print("\r", end="") + print("") + + +async def update_pickup_tip_speed(api, mount, speed) -> None: + """Update drop-tip current.""" + pipette = _get_pipette_from_mount(api, mount) + config_model = pipette.pick_up_configurations + config_model.speed = speed + pipette.pick_up_configurations = config_model + print(pipette.pick_up_configurations) + + +async def _main() -> None: + today = datetime.date.today() + tips_to_use = 8 + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.92, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + tip_length = {"T1K": 85.7, "T200": 48.35, "T50": 47.9} + pipette_model = hw_api._pipette_handler.hardware_instruments[mount].name + dial_data = {"Tip": None, "Tip Height": None, "Motor Current": None} + m_current = float(input("motor_current in amps: ")) + pick_up_speed = float(input("pick up tip speed in mm/s: ")) + details = [pipette_model, m_current] + test_n, test_f = file_setup(dial_data, details) + file_name = ( + "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s.csv" + % ( + m_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M") + ) + ) + print(file_name) + print(test_n) + print(test_f) + await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) + await hw_api.home_plunger(mount) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + home_position = await hw_api.current_position_ot3(mount) + start_time = time.perf_counter() + global stop_threads + global motion + if args.tip_size == "T1K": + home_with_tip_position = 164.3 # T1K + if args.tip_size == "T200": + home_with_tip_position = 201.64999999999998 # T1K + elif args.tip_size == "T50": + home_with_tip_position = 192.1 # T50 + + if args.fg_jog: + cp = CriticalPoint.NOZZLE + await hw_api.move_to( + mount, + Point( + slot_loc["D2"][0], + slot_loc["D2"][1], + home_position[OT3Axis.by_mount(mount)], + ), + ) + current_position = await hw_api.current_position_ot3(mount) + print("Move to Force Gauge") + fg_loc = await jog(hw_api, current_position, cp) + fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(mount)]] + await hw_api.move_to( + mount, + Point( + fg_loc[0], fg_loc[1], home_position[OT3Axis.by_mount(mount)] + ), + critical_point=CriticalPoint.TIP, + ) + await hw_api.home_z(mount, allow_home_other=False) + + if args.tiprack: + await hw_api.move_to( + mount, + Point( + slot_loc["B2"][0], + slot_loc["B2"][1], + home_position[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.NOZZLE + print("Move to Tiprack") + current_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await jog(hw_api, current_position, cp) + # Start recording the encoder + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + print(f"Start encoder: {init_tip_loc}") + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + await update_pick_up_current(hw_api, mount, m_current) + await update_pickup_tip_speed(hw_api, mount, pick_up_speed) + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + await home_ot3(hw_api, [OT3Axis.by_mount(mount)]) + home_with_tip_position = await hw_api.current_position_ot3(mount, critical_point= CriticalPoint.TIP) + await hw_api.move_to( + mount, + Point( + current_position[OT3Axis.X], + current_position[OT3Axis.Y], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + + encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] + # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + print(f"End Encoder: {final_tip_loc}") + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + location = "Tiprack" + tip_count = 1 + test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + enc_record(file_name, test_details) + tiprack_loc = [ + tiprack_loc[OT3Axis.X], + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)], + ] + + if args.dial_indicator: + await hw_api.move_to( + mount, + Point( + slot_loc["C2"][0], + slot_loc["C2"][1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.TIP + print("Move to Tiprack") + current_position = await hw_api.current_position_ot3(mount) + dial_loc = await jog(hw_api, current_position, cp) + tip_offset = 0 + + for tips in range(1, tips+1): + await asyncio.sleep(1) + tip_measurement = gauge.read() + await asyncio.sleep(2) + d_str = f"{m_current}, {pipette_model}, {tip_measurement}, Tip \n" + data.append_data_to_file(test_n, test_f, d_str) + dial_loc = [ + dial_loc[OT3Axis.X], + dial_loc[OT3Axis.Y], + dial_loc[OT3Axis.by_mount(mount)], + ] + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[2] + tip_offset, + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + tip_offset += 9 + + if args.trough: + await hw_api.move_to( + mount, + Point( + slot_loc["B3"][0], + slot_loc["B3"][1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.TIP + print("Move to Trough") + current_position = await hw_api.current_position_ot3(mount) + trough_loc = await jog(hw_api, current_position, cp) + trough_loc = [ + trough_loc[OT3Axis.X], + trough_loc[OT3Axis.Y], + trough_loc[OT3Axis.by_mount(mount)], + ] + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1] - 20, + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + input("Feel the Tip!") + await hw_api.drop_tip(mount) + + lp_file_name = "/var/pressure_sensor_data_P-{}_Z-{}-{}.csv".format( + args.plunger_speed, args.mount_speed, today.strftime("%b-%d-%Y") + ) + liquid_probe_settings = LiquidProbeSettings( + max_z_distance=args.max_z_distance, + min_z_distance=args.min_z_distance, + mount_speed=args.mount_speed, + plunger_speed=args.plunger_speed, + sensor_threshold_pascals=args.sensor_threshold, + expected_liquid_height=args.expected_liquid_height, + log_pressure=args.log_pressure, + aspirate_while_sensing=False, + data_file=lp_file_name, + ) + tip_count = 0 + x_offset = 0 + y_offset = 0 + try: + + for tip in range(2, tips_to_use + 1): + tip_count += 1 + y_offset -= 9 + if tip_count % 8 == 0: + y_offset = 0 + if tip_count % 8 == 0: + x_offset += 9 + # #-----------------------Force Gauge----------------------------------- + if args.fg: + # Set Motor current by tester + await hw_api.move_to( + mount, + Point( + fg_loc[0], + fg_loc[1], + home_position[OT3Axis.by_mount(mount)], + ), + ) + # # Move pipette to Force Gauge calibrated location + await hw_api.move_to(mount, Point(fg_loc[0], fg_loc[1], fg_loc[2])) + init_fg_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + init_fg_loc = init_fg_loc[OT3Axis.by_mount(mount)] + location = "Force_Gauge" + force_thread = Thread( + target=force_record, + args=( + m_current, + location, + pipette_model, + ), + ) + motion = True + # stop_threads = False + force_thread.start() + await update_pick_up_current(hw_api, mount, m_current) + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + await update_pickup_tip_speed(hw_api, mount, pick_up_speed) + print("I'm performing pick_up_tip") + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + print(final_tip_loc) + await asyncio.sleep(1) + motion = False + # stop_threads = True + await asyncio.sleep(1) + force_thread.join() # Thread Finished + await hw_api.remove_tip(mount) + await hw_api.home_z(mount, allow_home_other=False) + print("I'm here") + # await hw_api.move_to( + # mount, + # Point( + # fg_loc[0], + # fg_loc[1], + # home_with_tip_position[OT3Axis.by_mount(mount)], + # ), + # critical_point=CriticalPoint.TIP, + # ) + await asyncio.sleep(2) + final_fg_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + final_fg_loc = final_fg_loc[OT3Axis.by_mount(mount)] + + + + + # -----------------------Tiprack------------------------------------ + if args.tiprack: + # Move over to the TipRack location and + await hw_api.move_to( + mount, + Point( + tiprack_loc[0] + x_offset, + tiprack_loc[1] + y_offset, + home_position[OT3Axis.by_mount(mount)] , + ), + ) + + # Move Pipette to top of Tip Rack Location + await hw_api.move_to( + mount, + Point( + tiprack_loc[0] + x_offset, tiprack_loc[1] + y_offset, tiprack_loc[2] + ), + speed=65, + ) + location = "Tiprack" + + # Start recording the encoder + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + print(f"Start encoder: {init_tip_loc}") + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc + # Press Pipette into the tip + await update_pick_up_current(hw_api, mount, m_current) + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + await hw_api.move_to( + mount, + Point( + tiprack_loc[0] + x_offset, + tiprack_loc[1] + y_offset, + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + # home_with_tip_position = await hw_api.current_position_ot3( + # mount, critical_point=CriticalPoint.TIP + # ) + encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] + # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + print(f"End Encoder: {final_tip_loc}") + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + enc_record(file_name, test_details) + # Home Z + await hw_api.home([OT3Axis.by_mount(mount)]) + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)] , + ), + ) + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point(dial_loc[0], dial_loc[1], dial_loc[2]), + ) + await asyncio.sleep(3) + tip_measurement = gauge.read() + d_str = f"{m_current}, {pipette_model}, {tip_measurement}, Tip \n" + data.append_data_to_file(test_n, test_f, d_str) + await asyncio.sleep(1) + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)] , + ), + ) + # -----------------------Aspirate----------------------------------- + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + # Move to offset from trough + await hw_api.move_to( + mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) + ) + # Move the plunger to the top position + await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[0]) + # Liquid Probe + liquid_height = await hw_api.liquid_probe( + mount, probe_settings=liquid_probe_settings + ) + + liquid_height = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + # await move_plunger_relative_ot3(hw_api, mount, 1.5, None, speed = 2) # P50S + await move_plunger_relative_ot3(hw_api, mount, 0.25, None, speed=2) # P1KS + await hw_api.move_to( + mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) + ) + # Prepare to aspirate before descending to trough well + await hw_api.prepare_for_aspirate(mount) + # Descend to aspirate depth + await hw_api.move_to( + mount, + Point( + liquid_height[OT3Axis.X], + liquid_height[OT3Axis.Y], + liquid_height[OT3Axis.by_mount(mount)] - aspirate_depth, + ), + speed=5, + critical_point=CriticalPoint.TIP, + ) + # Aspirate + await hw_api.aspirate(mount) + cur_pos = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + z_pos = cur_pos[OT3Axis.by_mount(mount)] + # Retract from liquid with retract speed + await hw_api.move_to( + mount, + Point(trough_loc[0], trough_loc[1], z_pos + liquid_retract_dist), + speed=liquid_retract_speed, + critical_point=CriticalPoint.TIP, + ) + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)] , + ), + critical_point=CriticalPoint.TIP, + ) + await countdown(count_time=leak_test_time) + # input("Check to see if the pipette is leaking") + await hw_api.move_to( + mount, + Point(trough_loc[0], trough_loc[1], trough_loc[2]), + critical_point=CriticalPoint.TIP, + ) + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + liquid_height[OT3Axis.by_mount(mount)] - dispense_depth, + ), + speed=5, + critical_point=CriticalPoint.TIP, + ) + await hw_api.dispense(mount) + await hw_api.blow_out(mount) + # --------------------Drop Tip-------------------------------------- + current_position = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1] - 20, + home_with_tip_position[OT3Axis.by_mount(mount)] , + ), + critical_point=CriticalPoint.TIP, + ) + await hw_api.drop_tip(mount) + + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + + +def force_record(motor_current, location, pipette_model): + global stop_threads + global motion + file_name = ( + "/home/root/.opentrons/testing_data/force_data/force_pu_test_%s-%s-%s.csv" + % ( + motor_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M-%S"), + location, + ) + ) + print(file_name) + with open(file_name, "w", newline="") as f: + test_data = { + "Time(s)": None, + "Force(N)": None, + "M_current(amps)": None, + "pipette_model": None, + } + log_file = csv.DictWriter(f, test_data) + log_file.writeheader() + start_time = time.perf_counter() + try: + motion = True + force_data = [] + while motion: + reading = float(fg.read_force()) + time_elasped = time.perf_counter() - start_time + force_data.append((time_elasped, reading)) + print(f"time(s): {time_elasped}, Force: {reading}") + + for t, force in force_data: + test_data["Time(s)"] = t + test_data["Force(N)"] = force + test_data["M_current(amps)"] = motor_current + test_data["pipette_model"] = pipette_model + log_file.writerow(test_data) + print(test_data) + f.flush() + + except KeyboardInterrupt: + print("Test Cancelled") + test_data["Errors"] = "Test Cancelled" + f.flush() + except Exception as e: + print("ERROR OCCURED") + test_data["Errors"] = e + f.flush() + raise e + print("Test done") + f.flush() + f.close() + + +def enc_record(f_name, t_data): + #test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + with open(f_name, "a", newline="") as f: + test_data = { + "time(s)": None, + "motor_current": None, + "location": None, + "start_enc_pos(mm)": None, + "end_enc_pos(mm)": None + } + log_file = csv.DictWriter(f, test_data) + if t_data[5] < 1: + log_file.writeheader() + try: + test_data["time(s)"] = time.perf_counter() - t_data[0] + test_data["motor_current"] = t_data[1] + test_data["location"] = t_data[2] + test_data["start_enc_pos(mm)"] = t_data[3] + test_data["end_enc_pos(mm)"] = t_data[4] + log_file.writerow(test_data) + print(test_data) + f.flush() + except KeyboardInterrupt: + print("Test Cancelled") + test_data["Errors"] = "Test Cancelled" + f.flush() + except Exception as e: + print("ERROR OCCURED") + test_data["Errors"] = e + f.flush() + raise e + print("Test done") + f.flush() + f.close() + + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--fg_jog", action="store_true") + parser.add_argument("--trough", action="store_true") + parser.add_argument("--tiprack", action="store_true") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C1") + parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") + parser.add_argument("--fg", action="store_true", default=True) + parser.add_argument("--dial_indicator", action="store_true") + parser.add_argument("--tip_size", type=str, default="T50", help="Tip Size") + parser.add_argument("--max_z_distance", type=float, default=40) + parser.add_argument("--min_z_distance", type=float, default=5) + parser.add_argument("--mount_speed", type=float, default=11) + parser.add_argument("--plunger_speed", type=float, default=21) + parser.add_argument( + "--sensor_threshold", type=float, default=160, help="Threshold in Pascals" + ) + parser.add_argument("--expected_liquid_height", type=int, default=0) + parser.add_argument("--log_pressure", action="store_true") + parser.add_argument( + "--fg_port", type=str, default="/dev/ttyUSB0", help="Force Gauge Port" + ) + parser.add_argument( + "--dial_port", type=str, default="/dev/ttyUSB0", help="Dial indicator Port" + ) + args = parser.parse_args() + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + if args.fg: + fg = Mark10.create(port=args.fg_port) + fg.connect() + + if args.dial_indicator: + gauge = dial_indicator_setup(port = args.dial_port) + asyncio.run(_main()) diff --git a/hardware-testing/hardware_testing/scripts/pick_up_tip.py b/hardware-testing/hardware_testing/scripts/single_channel_pick_up_tip.py similarity index 100% rename from hardware-testing/hardware_testing/scripts/pick_up_tip.py rename to hardware-testing/hardware_testing/scripts/single_channel_pick_up_tip.py From b842c32ff98953478c133e783f41f7cdbd4632c7 Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 18 Apr 2023 09:20:08 -0400 Subject: [PATCH 17/53] more changes for multi-channel testing. --- api/src/opentrons/config/defaults_ot3.py | 4 - api/src/opentrons/config/types.py | 1 - api/src/opentrons/hardware_control/ot3api.py | 56 ++++---- .../hardware_testing/scripts/jog.py | 2 +- .../scripts/multi_channel_pick_up_tip.py | 135 ++++++++++++------ 5 files changed, 120 insertions(+), 78 deletions(-) diff --git a/api/src/opentrons/config/defaults_ot3.py b/api/src/opentrons/config/defaults_ot3.py index c8a81d54903..b0951918cd8 100644 --- a/api/src/opentrons/config/defaults_ot3.py +++ b/api/src/opentrons/config/defaults_ot3.py @@ -21,7 +21,6 @@ DEFAULT_MODULE_OFFSET = [0.0, 0.0, 0.0] DEFAULT_LIQUID_PROBE_SETTINGS: Final[LiquidProbeSettings] = LiquidProbeSettings( - starting_mount_height=100, max_z_distance=40, min_z_distance=5, mount_speed=10, @@ -275,9 +274,6 @@ def _build_default_liquid_probe( from_conf: Any, default: LiquidProbeSettings ) -> LiquidProbeSettings: return LiquidProbeSettings( - starting_mount_height=from_conf.get( - "starting_mount_height", default.starting_mount_height - ), max_z_distance=from_conf.get("max_z_distance", default.max_z_distance), min_z_distance=from_conf.get("min_z_distance", default.min_z_distance), mount_speed=from_conf.get("mount_speed", default.mount_speed), diff --git a/api/src/opentrons/config/types.py b/api/src/opentrons/config/types.py index 891d803255e..f91e753ab35 100644 --- a/api/src/opentrons/config/types.py +++ b/api/src/opentrons/config/types.py @@ -118,7 +118,6 @@ class ZSenseSettings: @dataclass class LiquidProbeSettings: - starting_mount_height: float max_z_distance: float min_z_distance: float mount_speed: float diff --git a/api/src/opentrons/hardware_control/ot3api.py b/api/src/opentrons/hardware_control/ot3api.py index bc428150a3d..63caf32b346 100644 --- a/api/src/opentrons/hardware_control/ot3api.py +++ b/api/src/opentrons/hardware_control/ot3api.py @@ -1553,7 +1553,7 @@ async def _force_pick_up_tip( await asyncio.sleep(1) enc_pos = await self.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) await self._move(target_up) - return enc_pos + return enc_pos async def _motor_pick_up_tip( self, mount: OT3Mount, pipette_spec: TipMotorPickUpTipSpec @@ -1891,16 +1891,13 @@ async def liquid_probe( probe_settings: Optional[LiquidProbeSettings] = None, ) -> float: """Search for and return liquid level height. - This function begins by moving the mount the distance specified by starting_mount_height in the LiquidProbeSettings. After this, the mount and plunger motors will move simultaneously while reading from the pressure sensor. - If the move is completed without the specified threshold being triggered, a LiquidNotFound error will be thrown. If the threshold is triggered before the minimum z distance has been traveled, a EarlyLiquidSenseTrigger error will be thrown. - Otherwise, the function will stop moving once the threshold is triggered, and return the position of the z axis in deck coordinates, as well as the encoder position, where @@ -1917,57 +1914,56 @@ async def liquid_probe( probe_settings = self.config.liquid_sense mount_axis = OT3Axis.by_mount(mount) - gantry_position = await self.gantry_position(mount, refresh=True) - - await self.move_to( - mount, - top_types.Point( - x=gantry_position.x, - y=gantry_position.y, - z=probe_settings.starting_mount_height, - ), - ) - + # homes plunger then moves it to bottom of axis if probe_settings.aspirate_while_sensing: await self.home_plunger(mount) + # if not aspirate_while_sensing, plunger should be at bottom, since + # this expects to be called after pick_up_tip + plunger_direction = -1 if probe_settings.aspirate_while_sensing else 1 - machine_pos_node_id = await self._backend.liquid_probe( + # get position before moving in deck coordinates + starting_position = await self.current_position_ot3( + mount=mount, critical_point=CriticalPoint.TIP, refresh=True + ) + await self._backend.liquid_probe( mount, probe_settings.max_z_distance, probe_settings.mount_speed, (probe_settings.plunger_speed * plunger_direction), probe_settings.sensor_threshold_pascals, probe_settings.log_pressure, - probe_settings.auto_zero_sensor, - probe_settings.num_baseline_reads, ) - machine_pos = axis_convert(machine_pos_node_id, 0.0) - position = deck_from_machine( - machine_pos, - self._transforms.deck_calibration.attitude, - self._transforms.carriage_offset, + + # get final position in deck coordinates + final_position = await self.current_position_ot3( + mount=mount, critical_point=CriticalPoint.TIP, refresh=True ) + z_distance_traveled = ( - position[mount_axis] - probe_settings.starting_mount_height + starting_position[mount_axis] - final_position[mount_axis] ) if z_distance_traveled < probe_settings.min_z_distance: - min_z_travel_pos = position + min_z_travel_pos = final_position min_z_travel_pos[mount_axis] = probe_settings.min_z_distance raise EarlyLiquidSenseTrigger( - triggered_at=position, + triggered_at=final_position, min_z_pos=min_z_travel_pos, ) - elif z_distance_traveled > probe_settings.max_z_distance: - max_z_travel_pos = position + elif z_distance_traveled >= probe_settings.max_z_distance: + max_z_travel_pos = final_position max_z_travel_pos[mount_axis] = probe_settings.max_z_distance raise LiquidNotFound( - position=position, + position=final_position, max_z_pos=max_z_travel_pos, ) - return position[mount_axis] + encoder_pos = await self.encoder_current_position_ot3( + mount=mount, critical_point=CriticalPoint.TIP + ) + + return final_position, encoder_pos async def capacitive_probe( self, diff --git a/hardware-testing/hardware_testing/scripts/jog.py b/hardware-testing/hardware_testing/scripts/jog.py index 35f67510080..aaa4bd64df4 100644 --- a/hardware-testing/hardware_testing/scripts/jog.py +++ b/hardware-testing/hardware_testing/scripts/jog.py @@ -204,7 +204,7 @@ async def _main() -> None: is_simulating=args.simulate, use_defaults=True ) tip_length = {"T1K": 85.7, "T50": 57.9} - pipette_model = hw_api._pipette_handler.hardware_instruments[mount].name + pipette_model = hw_api.get_all_attached_instr()[OT3Mount.RIGHT]['pipette_id'] pipette = _get_pipette_from_mount(hw_api, mount) dial_data = {"Tip": None, "Tip Height": None, "Motor Current": None} m_current = 0.2 diff --git a/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py index d916f95d692..47274a848b8 100644 --- a/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py @@ -224,11 +224,11 @@ async def update_pickup_tip_speed(api, mount, speed) -> None: async def _main() -> None: today = datetime.date.today() - tips_to_use = 8 + tips_to_use = 12 slot_loc = { "A1": (13.42, 394.92, 110), "A2": (177.32, 394.92, 110), - "A3": (341.03, 394.92, 110), + "A3": (341.03, 394.0, 110), "B1": (13.42, 288.42, 110), "B2": (177.32, 288.92, 110), "B3": (341.03, 288.92, 110), @@ -243,8 +243,18 @@ async def _main() -> None: is_simulating=args.simulate, use_defaults=True ) tip_length = {"T1K": 85.7, "T200": 48.35, "T50": 47.9} - pipette_model = hw_api._pipette_handler.hardware_instruments[mount].name - dial_data = {"Tip": None, "Tip Height": None, "Motor Current": None} + pipette_model = hw_api.get_all_attached_instr()[OT3Mount.RIGHT]['pipette_id'] + dial_data = {"Ch1": None, + "Ch2": None, + "Ch3": None, + "Ch4": None, + "Ch5": None, + "Ch6": None, + "Ch7": None, + "Ch8": None, + "Motor Current": None, + "Trial": None, + "pipette_model": None} m_current = float(input("motor_current in amps: ")) pick_up_speed = float(input("pick up tip speed in mm/s: ")) details = [pipette_model, m_current] @@ -261,6 +271,7 @@ async def _main() -> None: print(test_f) await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) await hw_api.home_plunger(mount) + await hw_api.set_lights(rails = True) plunger_pos = get_plunger_positions_ot3(hw_api, mount) home_position = await hw_api.current_position_ot3(mount) start_time = time.perf_counter() @@ -364,28 +375,49 @@ async def _main() -> None: print("Move to Tiprack") current_position = await hw_api.current_position_ot3(mount) dial_loc = await jog(hw_api, current_position, cp) + dial_loc = [ + dial_loc[OT3Axis.X], + dial_loc[OT3Axis.Y], + dial_loc[OT3Axis.by_mount(mount)], + ] tip_offset = 0 + measurements = '' + tips = 8 + trial = 1 + for tip in range(1, tips+1): - for tips in range(1, tips+1): + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1] + tip_offset, + dial_loc[2], + ), + ) await asyncio.sleep(1) tip_measurement = gauge.read() await asyncio.sleep(2) - d_str = f"{m_current}, {pipette_model}, {tip_measurement}, Tip \n" - data.append_data_to_file(test_n, test_f, d_str) - dial_loc = [ - dial_loc[OT3Axis.X], - dial_loc[OT3Axis.Y], - dial_loc[OT3Axis.by_mount(mount)], - ] + measurements += str(tip_measurement) + ',' + await hw_api.move_to( mount, Point( dial_loc[0], - dial_loc[2] + tip_offset, + dial_loc[1] + tip_offset, home_with_tip_position[OT3Axis.by_mount(mount)], ), ) tip_offset += 9 + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1] + tip_offset, + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" + data.append_data_to_file(test_n, test_f, d_str) if args.trough: await hw_api.move_to( @@ -419,7 +451,7 @@ async def _main() -> None: mount, Point( slot_loc["A3"][0] + 50, - slot_loc["A3"][1] - 20, + slot_loc["A3"][1], home_with_tip_position[OT3Axis.by_mount(mount)], ), critical_point=CriticalPoint.TIP, @@ -439,6 +471,8 @@ async def _main() -> None: expected_liquid_height=args.expected_liquid_height, log_pressure=args.log_pressure, aspirate_while_sensing=False, + auto_zero_sensor = True, + num_baseline_reads = 10, data_file=lp_file_name, ) tip_count = 0 @@ -447,7 +481,8 @@ async def _main() -> None: try: for tip in range(2, tips_to_use + 1): - tip_count += 1 + trial = tip + tip_count += 8 y_offset -= 9 if tip_count % 8 == 0: y_offset = 0 @@ -500,7 +535,6 @@ async def _main() -> None: force_thread.join() # Thread Finished await hw_api.remove_tip(mount) await hw_api.home_z(mount, allow_home_other=False) - print("I'm here") # await hw_api.move_to( # mount, # Point( @@ -574,6 +608,7 @@ async def _main() -> None: enc_record(file_name, test_details) # Home Z await hw_api.home([OT3Axis.by_mount(mount)]) + #--------------------------Dial Indicator----------------------- # Move over to the dial indicator await hw_api.move_to( mount, @@ -583,25 +618,41 @@ async def _main() -> None: home_with_tip_position[OT3Axis.by_mount(mount)] , ), ) - # Move over to the dial indicator - await hw_api.move_to( - mount, - Point(dial_loc[0], dial_loc[1], dial_loc[2]), - ) - await asyncio.sleep(3) - tip_measurement = gauge.read() - d_str = f"{m_current}, {pipette_model}, {tip_measurement}, Tip \n" + tip_offset = 0 + measurements = '' + tips = 8 + for t in range(1, tips+1): + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point(dial_loc[0], + dial_loc[1] + tip_offset, + dial_loc[2]), + ) + await asyncio.sleep(3) + tip_measurement = gauge.read() + measurements += str(tip_measurement) + ',' + await asyncio.sleep(1) + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1] + tip_offset, + home_with_tip_position[OT3Axis.by_mount(mount)] , + ), + ) + tip_offset += 9 + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1] + tip_offset, + home_with_tip_position[OT3Axis.by_mount(mount)] , + ), + ) + d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" data.append_data_to_file(test_n, test_f, d_str) - await asyncio.sleep(1) - # Move over to the dial indicator - await hw_api.move_to( - mount, - Point( - dial_loc[0], - dial_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)] , - ), - ) # -----------------------Aspirate----------------------------------- await hw_api.move_to( mount, @@ -618,7 +669,7 @@ async def _main() -> None: # Move the plunger to the top position await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[0]) # Liquid Probe - liquid_height = await hw_api.liquid_probe( + liquid_height, enc_liquid_height = await hw_api.liquid_probe( mount, probe_settings=liquid_probe_settings ) @@ -644,7 +695,7 @@ async def _main() -> None: critical_point=CriticalPoint.TIP, ) # Aspirate - await hw_api.aspirate(mount) + await hw_api.aspirate(mount, volume = 50) cur_pos = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) @@ -702,7 +753,7 @@ async def _main() -> None: mount, Point( slot_loc["A3"][0] + 50, - slot_loc["A3"][1] - 20, + slot_loc["A3"][1], home_with_tip_position[OT3Axis.by_mount(mount)] , ), critical_point=CriticalPoint.TIP, @@ -827,19 +878,19 @@ def enc_record(f_name, t_data): parser.add_argument("--fg_jog", action="store_true") parser.add_argument("--trough", action="store_true") parser.add_argument("--tiprack", action="store_true") - parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="right") parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C1") parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") - parser.add_argument("--fg", action="store_true", default=True) + parser.add_argument("--fg", action="store_true") parser.add_argument("--dial_indicator", action="store_true") parser.add_argument("--tip_size", type=str, default="T50", help="Tip Size") parser.add_argument("--max_z_distance", type=float, default=40) parser.add_argument("--min_z_distance", type=float, default=5) - parser.add_argument("--mount_speed", type=float, default=11) - parser.add_argument("--plunger_speed", type=float, default=21) + parser.add_argument("--mount_speed", type=float, default=5) + parser.add_argument("--plunger_speed", type=float, default=7) parser.add_argument( - "--sensor_threshold", type=float, default=160, help="Threshold in Pascals" + "--sensor_threshold", type=float, default=100, help="Threshold in Pascals" ) parser.add_argument("--expected_liquid_height", type=int, default=0) parser.add_argument("--log_pressure", action="store_true") From db98d7ccc3bd6fc625bc54c7d447c280c91555b3 Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 4 May 2023 18:21:56 -0400 Subject: [PATCH 18/53] changes to this branch --- api/src/opentrons/hardware_control/ot3api.py | 28 +- .../opentrons_api/helpers_ot3.py | 1 - .../scripts/drop_tip_force_test.py | 30 +- .../hardware_testing/scripts/drop_tip_test.py | 34 +- .../hardware_testing/scripts/jog.py | 9 +- .../scripts/multi_channel_pick_up_tip.py | 122 ++-- .../scripts/single_channel_pick_up_tip.py | 62 +- .../taylors_awesome_tip_straightness.py | 287 +++++++++ .../scripts/tip_straightness.py | 592 ++++++++++++++++++ 9 files changed, 1038 insertions(+), 127 deletions(-) create mode 100644 hardware-testing/hardware_testing/scripts/taylors_awesome_tip_straightness.py create mode 100644 hardware-testing/hardware_testing/scripts/tip_straightness.py diff --git a/api/src/opentrons/hardware_control/ot3api.py b/api/src/opentrons/hardware_control/ot3api.py index 344f65c7ba1..415c363c69f 100644 --- a/api/src/opentrons/hardware_control/ot3api.py +++ b/api/src/opentrons/hardware_control/ot3api.py @@ -1956,6 +1956,7 @@ async def capacitive_probe( mount: OT3Mount, moving_axis: OT3Axis, target_pos: float, + direction: float, pass_settings: CapacitivePassSettings, ) -> float: """Determine the position of something using the capacitive sensor. @@ -1992,24 +1993,29 @@ async def capacitive_probe( here = await self.gantry_position(mount, refresh=True) origin_pos = moving_axis.of_point(here) - if origin_pos < target_pos: - pass_start = target_pos - pass_settings.prep_distance_mm - pass_distance = ( - pass_settings.prep_distance_mm + pass_settings.max_overrun_distance_mm - ) - else: - pass_start = target_pos + pass_settings.prep_distance_mm - pass_distance = -1.0 * ( + # print(f"origin_pos: {origin_pos}") + # if origin_pos < target_pos: + # pass_start = target_pos - pass_settings.prep_distance_mm + # # print(f"pass_start: {pass_start}") + pass_distance = -direction * ( pass_settings.prep_distance_mm + pass_settings.max_overrun_distance_mm ) + # else: + # pass_start = target_pos + pass_settings.prep_distance_mm + # # print(f"pass_start: {pass_start}") + # pass_distance = 1.0 * ( + # pass_settings.prep_distance_mm + pass_settings.max_overrun_distance_mm + # ) machine_pass_distance = moving_axis.of_point( machine_vector_from_deck_vector( moving_axis.set_in_point(top_types.Point(0, 0, 0), pass_distance), self._transforms.deck_calibration.attitude, ) ) - pass_start_pos = moving_axis.set_in_point(here, pass_start) - await self.move_to(mount, pass_start_pos) + # print(f"machine_pass_distance: {machine_pass_distance}") + # pass_start_pos = moving_axis.set_in_point(here, pass_start) + # print(f"pass_start_pos: {pass_start_pos}") + # await self.move_to(mount, pass_start_pos) if mount == OT3Mount.GRIPPER: probe = self._gripper_handler.get_attached_probe() assert probe @@ -2031,7 +2037,7 @@ async def capacitive_probe( probe=InstrumentProbeType.PRIMARY, ) end_pos = await self.gantry_position(mount, refresh=True) - await self.move_to(mount, pass_start_pos) + # await self.move_to(mount, pass_start_pos) return moving_axis.of_point(end_pos) async def capacitive_sweep( diff --git a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py index 491c81d7f9d..ef89807a421 100644 --- a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py +++ b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py @@ -372,7 +372,6 @@ def get_plunger_positions_ot3( ) - async def update_pick_up_current( api: OT3API, mount: OT3Mount, current: float = 0.125 ) -> None: diff --git a/hardware-testing/hardware_testing/scripts/drop_tip_force_test.py b/hardware-testing/hardware_testing/scripts/drop_tip_force_test.py index f42e5d4aa72..1744adf7880 100644 --- a/hardware-testing/hardware_testing/scripts/drop_tip_force_test.py +++ b/hardware-testing/hardware_testing/scripts/drop_tip_force_test.py @@ -29,15 +29,17 @@ update_pick_up_current, update_pick_up_distance, update_drop_tip_current, - _get_pipette_from_mount + _get_pipette_from_mount, ) from hardware_testing import data from hardware_testing.drivers.mark10 import Mark10 + def dict_keys_to_line(dict): return str.join(",", list(dict.keys())) + "\n" + def file_setup(test_data, details): today = datetime.date.today() test_name = "{}-drop_tip-force-test-{}Amps".format( @@ -178,11 +180,13 @@ async def jog(api, position, cp) -> Dict[OT3Axis, float]: ) print("\r", end="") + async def update_drop_tip_distance(api, mount, position) -> None: pipette = _get_pipette_from_mount(api, mount) pipette.plunger_positions.drop_tip = position print(pipette.plunger_positions) + async def update_drop_tip_speed(api, mount, speed) -> None: """Update drop-tip current.""" pipette = _get_pipette_from_mount(api, mount) @@ -191,6 +195,7 @@ async def update_drop_tip_speed(api, mount, speed) -> None: pipette._drop_configurations = config_model print(pipette._drop_configurations) + async def _main() -> None: today = datetime.date.today() tips_to_use = 96 @@ -242,22 +247,20 @@ async def _main() -> None: fg_loc = await jog(hw_api, current_position, cp) fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(mount)]] - try: cycle = 1 drop_tip_distance = pipette.plunger_positions.drop_tip + 12 - await update_drop_tip_distance(hw_api, mount, drop_tip_distance) - await update_drop_tip_speed(hw_api, mount, 10) - await hw_api._backend.set_hold_current({OT3Axis.X: 1.0, - OT3Axis.Y: 1.0, - OT3Axis.Z_L: 1.0, - OT3Axis.Z_R: 1.0}) + await update_drop_tip_distance(hw_api, mount, drop_tip_distance) + await update_drop_tip_speed(hw_api, mount, 10) + await hw_api._backend.set_hold_current( + {OT3Axis.X: 1.0, OT3Axis.Y: 1.0, OT3Axis.Z_L: 1.0, OT3Axis.Z_R: 1.0} + ) while True: d_current = float(input("Enter Drop Tip Current: ")) await hw_api.add_tip(mount, tip_length[args.tip_size]) # --------------------Drop Tip-------------------------------------- - details = [pipette_model, d_current, stop_event = Event()] + details = [pipette_model, d_current] # Move the plunger to the bottom position await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[1]) if cycle == 1: @@ -276,11 +279,8 @@ async def _main() -> None: await update_drop_tip_current(hw_api, mount, d_current) # obtain the encoder position - final_drop_tip_position = await hw_api.drop_tip(mount, home_after = False) - details = [ - pipette_model, - d_current - ] + final_drop_tip_position = await hw_api.drop_tip(mount, home_after=False) + details = [pipette_model, d_current] await asyncio.sleep(1) motion = False await hw_api.home_plunger(mount) @@ -308,7 +308,7 @@ def force_record(t_data, header): ) ) print(file_name) - with open(file_name,"w", newline="") as f: + with open(file_name, "w", newline="") as f: test_data = { "Time(s)": None, "Pipette Model": None, diff --git a/hardware-testing/hardware_testing/scripts/drop_tip_test.py b/hardware-testing/hardware_testing/scripts/drop_tip_test.py index 4870969b84a..518447e4994 100644 --- a/hardware-testing/hardware_testing/scripts/drop_tip_test.py +++ b/hardware-testing/hardware_testing/scripts/drop_tip_test.py @@ -29,7 +29,7 @@ update_pick_up_current, update_pick_up_distance, update_drop_tip_current, - _get_pipette_from_mount + _get_pipette_from_mount, ) from hardware_testing import data @@ -279,7 +279,7 @@ async def _main() -> None: location, init_tip_loc, final_tip_loc, - None + None, ] enc_record(file_name, details, True) tiprack_loc = [ @@ -303,7 +303,7 @@ async def _main() -> None: init_drop_tip_position = await hw_api.encoder_current_position_ot3(mount) # obtain the encoder position final_drop_tip_position = await hw_api.drop_tip(mount) - location = 'Trash' + location = "Trash" details = [ start_time, pipette_model, @@ -311,7 +311,7 @@ async def _main() -> None: location, init_tip_loc, final_tip_loc, - None + None, ] enc_record(file_name, details, False) print(f"drop tip current: {pipette._drop_configurations.current}") @@ -382,7 +382,7 @@ async def _main() -> None: location, init_tip_loc, final_tip_loc, - d_current + d_current, ] enc_record(file_name, details, False) # Home Z @@ -406,8 +406,8 @@ async def _main() -> None: # obtain the encoder position init_drop_tip_position = await hw_api.encoder_current_position_ot3(mount) # obtain the encoder position - final_drop_tip_position = await hw_api.drop_tip(mount, home_after = False) - location = 'Trash' + final_drop_tip_position = await hw_api.drop_tip(mount, home_after=False) + location = "Trash" details = [ start_time, pipette_model, @@ -415,14 +415,16 @@ async def _main() -> None: location, init_drop_tip_position[OT3Axis.of_main_tool_actuator(mount)], final_drop_tip_position[OT3Axis.of_main_tool_actuator(mount)], - d_current + d_current, ] enc_record(file_name, details, False) await hw_api.home_plunger(mount) safety_margin = 1 drop_tip_distance_target = 19 - safety_margin - delta = final_drop_tip_position[OT3Axis.of_main_tool_actuator(mount)] \ - - init_drop_tip_position[OT3Axis.of_main_tool_actuator(mount)] + delta = ( + final_drop_tip_position[OT3Axis.of_main_tool_actuator(mount)] + - init_drop_tip_position[OT3Axis.of_main_tool_actuator(mount)] + ) if delta < drop_tip_distance_target: print("Test Fail") break @@ -443,12 +445,12 @@ def enc_record(file_name, t_data, header): "Time(s)": None, "Pipette Model": None, "Location": None, - "Motor Current(Amps)":+ None, + "Motor Current(Amps)": +None, "Initial Enc Pos(mm)": None, "Final Enc Pos(mm)": None, "Dropt Current(Amps)": None, "Remaining Distance(mm)": None, - "Pass/Fail": None + "Pass/Fail": None, } log_file = csv.DictWriter(f, test_data) if header == True: @@ -461,15 +463,15 @@ def enc_record(file_name, t_data, header): test_data["Initial Enc Pos(mm)"] = t_data[4] test_data["Final Enc Pos(mm)"] = t_data[5] test_data["Dropt Current(Amps)"] = t_data[6] - test_data["Remaining Distance(mm)"] = t_data[5]-t_data[4] + test_data["Remaining Distance(mm)"] = t_data[5] - t_data[4] - if t_data[3] == 'Trash': + if t_data[3] == "Trash": print(f"initial P: {t_data[4]}, final P: {t_data[5]}") delta = t_data[5] - t_data[4] if delta < 0.1: - test_data["Pass/Fail"] = 'PASS' + test_data["Pass/Fail"] = "PASS" else: - test_data["Pass/Fail"] = 'FAIL' + test_data["Pass/Fail"] = "FAIL" else: print(f"initial Z: {t_data[4]}, final Z: {t_data[5]}") test_data["Pass/Fail"] = None diff --git a/hardware-testing/hardware_testing/scripts/jog.py b/hardware-testing/hardware_testing/scripts/jog.py index aaa4bd64df4..8901ea865c2 100644 --- a/hardware-testing/hardware_testing/scripts/jog.py +++ b/hardware-testing/hardware_testing/scripts/jog.py @@ -29,15 +29,17 @@ update_pick_up_current, update_pick_up_distance, update_drop_tip_current, - _get_pipette_from_mount + _get_pipette_from_mount, ) from hardware_testing import data from hardware_testing.drivers.mark10 import Mark10 + def dict_keys_to_line(dict): return str.join(",", list(dict.keys())) + "\n" + def file_setup(test_data, details): today = datetime.date.today() test_name = "{}-drop_tip-force-test-{}Amps".format( @@ -178,6 +180,7 @@ async def jog(api, position, cp) -> Dict[OT3Axis, float]: ) print("\r", end="") + async def update_drop_tip_distance(api, mount, position) -> None: pipette = _get_pipette_from_mount(api, mount) pipette.plunger_positions.drop_tip = position @@ -204,7 +207,7 @@ async def _main() -> None: is_simulating=args.simulate, use_defaults=True ) tip_length = {"T1K": 85.7, "T50": 57.9} - pipette_model = hw_api.get_all_attached_instr()[OT3Mount.RIGHT]['pipette_id'] + pipette_model = hw_api.get_all_attached_instr()[OT3Mount.RIGHT]["pipette_id"] pipette = _get_pipette_from_mount(hw_api, mount) dial_data = {"Tip": None, "Tip Height": None, "Motor Current": None} m_current = 0.2 @@ -258,7 +261,7 @@ async def _main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--simulate", action="store_true") parser.add_argument("--tiprack", action="store_true", default=True) - parser.add_argument("--slot", default = "D2") + parser.add_argument("--slot", default="D2") parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") parser.add_argument("--fg", action="store_true", default=True) parser.add_argument("--fg_jog", action="store_true", default=True) diff --git a/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py index 47274a848b8..0ae56fe4b4d 100644 --- a/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py @@ -69,9 +69,7 @@ def file_setup(test_data, details): def dial_indicator_setup(port): - gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator( - port=port - ) + gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator(port=port) gauge.connect() return gauge @@ -243,35 +241,34 @@ async def _main() -> None: is_simulating=args.simulate, use_defaults=True ) tip_length = {"T1K": 85.7, "T200": 48.35, "T50": 47.9} - pipette_model = hw_api.get_all_attached_instr()[OT3Mount.RIGHT]['pipette_id'] - dial_data = {"Ch1": None, - "Ch2": None, - "Ch3": None, - "Ch4": None, - "Ch5": None, - "Ch6": None, - "Ch7": None, - "Ch8": None, - "Motor Current": None, - "Trial": None, - "pipette_model": None} + pipette_model = hw_api.get_all_attached_instr()[OT3Mount.RIGHT]["pipette_id"] + dial_data = { + "Ch1": None, + "Ch2": None, + "Ch3": None, + "Ch4": None, + "Ch5": None, + "Ch6": None, + "Ch7": None, + "Ch8": None, + "Motor Current": None, + "Trial": None, + "pipette_model": None, + } m_current = float(input("motor_current in amps: ")) pick_up_speed = float(input("pick up tip speed in mm/s: ")) details = [pipette_model, m_current] test_n, test_f = file_setup(dial_data, details) - file_name = ( - "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s.csv" - % ( - m_current, - datetime.datetime.now().strftime("%m-%d-%y_%H-%M") - ) + file_name = "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s.csv" % ( + m_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), ) print(file_name) print(test_n) print(test_f) await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) await hw_api.home_plunger(mount) - await hw_api.set_lights(rails = True) + await hw_api.set_lights(rails=True) plunger_pos = get_plunger_positions_ot3(hw_api, mount) home_position = await hw_api.current_position_ot3(mount) start_time = time.perf_counter() @@ -300,9 +297,7 @@ async def _main() -> None: fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(mount)]] await hw_api.move_to( mount, - Point( - fg_loc[0], fg_loc[1], home_position[OT3Axis.by_mount(mount)] - ), + Point(fg_loc[0], fg_loc[1], home_position[OT3Axis.by_mount(mount)]), critical_point=CriticalPoint.TIP, ) await hw_api.home_z(mount, allow_home_other=False) @@ -337,7 +332,9 @@ async def _main() -> None: mount, tip_length=tip_length[args.tip_size] ) await home_ot3(hw_api, [OT3Axis.by_mount(mount)]) - home_with_tip_position = await hw_api.current_position_ot3(mount, critical_point= CriticalPoint.TIP) + home_with_tip_position = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) await hw_api.move_to( mount, Point( @@ -354,7 +351,14 @@ async def _main() -> None: final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] location = "Tiprack" tip_count = 1 - test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + test_details = [ + start_time, + m_current, + location, + init_tip_loc, + final_tip_loc, + tip_count, + ] enc_record(file_name, test_details) tiprack_loc = [ tiprack_loc[OT3Axis.X], @@ -381,10 +385,10 @@ async def _main() -> None: dial_loc[OT3Axis.by_mount(mount)], ] tip_offset = 0 - measurements = '' + measurements = "" tips = 8 trial = 1 - for tip in range(1, tips+1): + for tip in range(1, tips + 1): await hw_api.move_to( mount, @@ -397,7 +401,7 @@ async def _main() -> None: await asyncio.sleep(1) tip_measurement = gauge.read() await asyncio.sleep(2) - measurements += str(tip_measurement) + ',' + measurements += str(tip_measurement) + "," await hw_api.move_to( mount, @@ -471,8 +475,8 @@ async def _main() -> None: expected_liquid_height=args.expected_liquid_height, log_pressure=args.log_pressure, aspirate_while_sensing=False, - auto_zero_sensor = True, - num_baseline_reads = 10, + auto_zero_sensor=True, + num_baseline_reads=10, data_file=lp_file_name, ) tip_count = 0 @@ -550,9 +554,6 @@ async def _main() -> None: ) final_fg_loc = final_fg_loc[OT3Axis.by_mount(mount)] - - - # -----------------------Tiprack------------------------------------ if args.tiprack: # Move over to the TipRack location and @@ -561,7 +562,7 @@ async def _main() -> None: Point( tiprack_loc[0] + x_offset, tiprack_loc[1] + y_offset, - home_position[OT3Axis.by_mount(mount)] , + home_position[OT3Axis.by_mount(mount)], ), ) @@ -569,7 +570,9 @@ async def _main() -> None: await hw_api.move_to( mount, Point( - tiprack_loc[0] + x_offset, tiprack_loc[1] + y_offset, tiprack_loc[2] + tiprack_loc[0] + x_offset, + tiprack_loc[1] + y_offset, + tiprack_loc[2], ), speed=65, ) @@ -604,34 +607,39 @@ async def _main() -> None: # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) print(f"End Encoder: {final_tip_loc}") final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] - test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + test_details = [ + start_time, + m_current, + location, + init_tip_loc, + final_tip_loc, + tip_count, + ] enc_record(file_name, test_details) # Home Z await hw_api.home([OT3Axis.by_mount(mount)]) - #--------------------------Dial Indicator----------------------- + # --------------------------Dial Indicator----------------------- # Move over to the dial indicator await hw_api.move_to( mount, Point( dial_loc[0], dial_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)] , + home_with_tip_position[OT3Axis.by_mount(mount)], ), ) tip_offset = 0 - measurements = '' + measurements = "" tips = 8 - for t in range(1, tips+1): + for t in range(1, tips + 1): # Move over to the dial indicator await hw_api.move_to( mount, - Point(dial_loc[0], - dial_loc[1] + tip_offset, - dial_loc[2]), + Point(dial_loc[0], dial_loc[1] + tip_offset, dial_loc[2]), ) await asyncio.sleep(3) tip_measurement = gauge.read() - measurements += str(tip_measurement) + ',' + measurements += str(tip_measurement) + "," await asyncio.sleep(1) # Move over to the dial indicator await hw_api.move_to( @@ -639,7 +647,7 @@ async def _main() -> None: Point( dial_loc[0], dial_loc[1] + tip_offset, - home_with_tip_position[OT3Axis.by_mount(mount)] , + home_with_tip_position[OT3Axis.by_mount(mount)], ), ) tip_offset += 9 @@ -648,7 +656,7 @@ async def _main() -> None: Point( dial_loc[0], dial_loc[1] + tip_offset, - home_with_tip_position[OT3Axis.by_mount(mount)] , + home_with_tip_position[OT3Axis.by_mount(mount)], ), ) d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" @@ -677,7 +685,9 @@ async def _main() -> None: mount, critical_point=CriticalPoint.TIP ) # await move_plunger_relative_ot3(hw_api, mount, 1.5, None, speed = 2) # P50S - await move_plunger_relative_ot3(hw_api, mount, 0.25, None, speed=2) # P1KS + await move_plunger_relative_ot3( + hw_api, mount, 0.25, None, speed=2 + ) # P1KS await hw_api.move_to( mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) ) @@ -695,7 +705,7 @@ async def _main() -> None: critical_point=CriticalPoint.TIP, ) # Aspirate - await hw_api.aspirate(mount, volume = 50) + await hw_api.aspirate(mount, volume=50) cur_pos = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) @@ -712,7 +722,7 @@ async def _main() -> None: Point( trough_loc[0], trough_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)] , + home_with_tip_position[OT3Axis.by_mount(mount)], ), critical_point=CriticalPoint.TIP, ) @@ -735,7 +745,7 @@ async def _main() -> None: ) await hw_api.dispense(mount) await hw_api.blow_out(mount) - # --------------------Drop Tip-------------------------------------- + # --------------------Drop Tip-------------------------------------- current_position = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) @@ -754,7 +764,7 @@ async def _main() -> None: Point( slot_loc["A3"][0] + 50, slot_loc["A3"][1], - home_with_tip_position[OT3Axis.by_mount(mount)] , + home_with_tip_position[OT3Axis.by_mount(mount)], ), critical_point=CriticalPoint.TIP, ) @@ -823,14 +833,14 @@ def force_record(motor_current, location, pipette_model): def enc_record(f_name, t_data): - #test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + # test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] with open(f_name, "a", newline="") as f: test_data = { "time(s)": None, "motor_current": None, "location": None, "start_enc_pos(mm)": None, - "end_enc_pos(mm)": None + "end_enc_pos(mm)": None, } log_file = csv.DictWriter(f, test_data) if t_data[5] < 1: @@ -910,5 +920,5 @@ def enc_record(f_name, t_data): fg.connect() if args.dial_indicator: - gauge = dial_indicator_setup(port = args.dial_port) + gauge = dial_indicator_setup(port=args.dial_port) asyncio.run(_main()) diff --git a/hardware-testing/hardware_testing/scripts/single_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/single_channel_pick_up_tip.py index 5ff0bd84cdb..c8d288ab205 100644 --- a/hardware-testing/hardware_testing/scripts/single_channel_pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/single_channel_pick_up_tip.py @@ -249,12 +249,9 @@ async def _main() -> None: pick_up_speed = float(input("pick up tip speed in mm/s: ")) details = [pipette_model, m_current] test_n, test_f = file_setup(dial_data, details) - file_name = ( - "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s.csv" - % ( - m_current, - datetime.datetime.now().strftime("%m-%d-%y_%H-%M") - ) + file_name = "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s.csv" % ( + m_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), ) print(file_name) print(test_n) @@ -289,9 +286,7 @@ async def _main() -> None: fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(mount)]] await hw_api.move_to( mount, - Point( - fg_loc[0], fg_loc[1], home_position[OT3Axis.by_mount(mount)] - ), + Point(fg_loc[0], fg_loc[1], home_position[OT3Axis.by_mount(mount)]), critical_point=CriticalPoint.TIP, ) await hw_api.home_z(mount, allow_home_other=False) @@ -326,7 +321,9 @@ async def _main() -> None: mount, tip_length=tip_length[args.tip_size] ) await home_ot3(hw_api, [OT3Axis.by_mount(mount)]) - home_with_tip_position = await hw_api.current_position_ot3(mount, critical_point= CriticalPoint.TIP) + home_with_tip_position = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) await hw_api.move_to( mount, Point( @@ -343,7 +340,14 @@ async def _main() -> None: final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] location = "Tiprack" tip_count = 1 - test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + test_details = [ + start_time, + m_current, + location, + init_tip_loc, + final_tip_loc, + tip_count, + ] enc_record(file_name, test_details) tiprack_loc = [ tiprack_loc[OT3Axis.X], @@ -512,9 +516,6 @@ async def _main() -> None: ) final_fg_loc = final_fg_loc[OT3Axis.by_mount(mount)] - - - # -----------------------Tiprack------------------------------------ if args.tiprack: # Move over to the TipRack location and @@ -523,7 +524,7 @@ async def _main() -> None: Point( tiprack_loc[0] + x_offset, tiprack_loc[1] + y_offset, - home_position[OT3Axis.by_mount(mount)] , + home_position[OT3Axis.by_mount(mount)], ), ) @@ -531,7 +532,9 @@ async def _main() -> None: await hw_api.move_to( mount, Point( - tiprack_loc[0] + x_offset, tiprack_loc[1] + y_offset, tiprack_loc[2] + tiprack_loc[0] + x_offset, + tiprack_loc[1] + y_offset, + tiprack_loc[2], ), speed=65, ) @@ -566,7 +569,14 @@ async def _main() -> None: # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) print(f"End Encoder: {final_tip_loc}") final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] - test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + test_details = [ + start_time, + m_current, + location, + init_tip_loc, + final_tip_loc, + tip_count, + ] enc_record(file_name, test_details) # Home Z await hw_api.home([OT3Axis.by_mount(mount)]) @@ -576,7 +586,7 @@ async def _main() -> None: Point( dial_loc[0], dial_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)] , + home_with_tip_position[OT3Axis.by_mount(mount)], ), ) # Move over to the dial indicator @@ -595,7 +605,7 @@ async def _main() -> None: Point( dial_loc[0], dial_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)] , + home_with_tip_position[OT3Axis.by_mount(mount)], ), ) # -----------------------Aspirate----------------------------------- @@ -622,7 +632,9 @@ async def _main() -> None: mount, critical_point=CriticalPoint.TIP ) # await move_plunger_relative_ot3(hw_api, mount, 1.5, None, speed = 2) # P50S - await move_plunger_relative_ot3(hw_api, mount, 0.25, None, speed=2) # P1KS + await move_plunger_relative_ot3( + hw_api, mount, 0.25, None, speed=2 + ) # P1KS await hw_api.move_to( mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) ) @@ -657,7 +669,7 @@ async def _main() -> None: Point( trough_loc[0], trough_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)] , + home_with_tip_position[OT3Axis.by_mount(mount)], ), critical_point=CriticalPoint.TIP, ) @@ -680,7 +692,7 @@ async def _main() -> None: ) await hw_api.dispense(mount) await hw_api.blow_out(mount) - # --------------------Drop Tip-------------------------------------- + # --------------------Drop Tip-------------------------------------- current_position = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) @@ -699,7 +711,7 @@ async def _main() -> None: Point( slot_loc["A3"][0] + 50, slot_loc["A3"][1] - 20, - home_with_tip_position[OT3Axis.by_mount(mount)] , + home_with_tip_position[OT3Axis.by_mount(mount)], ), critical_point=CriticalPoint.TIP, ) @@ -768,14 +780,14 @@ def force_record(motor_current, location, pipette_model): def enc_record(f_name, t_data): - #test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + # test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] with open(f_name, "a", newline="") as f: test_data = { "time(s)": None, "motor_current": None, "location": None, "start_enc_pos(mm)": None, - "end_enc_pos(mm)": None + "end_enc_pos(mm)": None, } log_file = csv.DictWriter(f, test_data) if t_data[5] < 1: diff --git a/hardware-testing/hardware_testing/scripts/taylors_awesome_tip_straightness.py b/hardware-testing/hardware_testing/scripts/taylors_awesome_tip_straightness.py new file mode 100644 index 00000000000..b74355bcafe --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/taylors_awesome_tip_straightness.py @@ -0,0 +1,287 @@ +import logging +import asyncio +import argparse +from numpy import float64 +import termios +import sys, tty, os, time + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + GantryLoad, + OT3Mount, + OT3Axis, + Point, + Axis, +) +from hardware_testing.opentrons_api.helpers_ot3 import ( + OT3API, + build_async_ot3_hardware_api, + GantryLoadSettings, + set_gantry_load_per_axis_settings_ot3, + home_ot3, + get_endstop_position_ot3, + move_plunger_absolute_ot3, + update_pick_up_current, + update_pick_up_distance, +) + + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def _jog_axis(api, position) -> None: + step_size = [0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 150 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 6: + step_length_index = 6 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + return position + position = await api.current_position_ot3(mount) + + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + + +async def _main() -> None: + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.0, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + # await set_default_current_settings(hw_api, load=None) + await hw_api.cache_instruments() + await home_ot3(hw_api, [OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.home_plunger(mount) + home_pos = await hw_api.current_position_ot3(mount) + tip_length = {"T1K": 85.7, "T200": 48.35, "T50": 47.9} + print(hw_api.get_all_attached_instr()[mount]) + pipette_model = hw_api.get_all_attached_instr()[mount]["pipette_id"] + tip_column = 0 + columns_to_use = 12 + try: + + await hw_api.move_to( + mount, + Point( + slot_loc["C2"][0], + slot_loc["C2"][1], + home_pos[OT3Axis.by_mount(mount)], + ), + ) + await hw_api.move_to( + mount, + Point( + slot_loc["C2"][0], + slot_loc["C2"][1], + home_pos[OT3Axis.by_mount(mount)] - 50, + ), + ) + + for cycle in range(1, columns_to_use + 1): + if cycle <= 1: + print("Move to Tiprack") + home_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await _jog_axis(hw_api, home_position) + + await hw_api.move_to( + mount, + Point( + tiprack_loc[OT3Axis.X] + tip_column, + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)], + ), + ) + # await hw_api.pick_up_tip(mount, tip_length = 58.5, presses = 2, increment = 1) + current_val = float(input("Enter Current Val: ")) + print(f"Current Val: {current_val}") + await update_pick_up_current(hw_api, mount, current_val) + await hw_api.pick_up_tip(mount, tip_length=58.5) + await hw_api.home_z(mount) + current_pos = await hw_api.current_position_ot3(mount) + + # Move to gage plate + await hw_api.move_to( + mount, + Point( + slot_loc["D2"][0], + slot_loc["D2"][1], + current_pos[OT3Axis.by_mount(mount)], + ), + ) + current_pos = await hw_api.current_position_ot3(mount) + gage_plate = await _jog_axis(hw_api, current_pos) + + await hw_api.home_z(mount, allow_home_other=False) + current_pos = await hw_api.current_position_ot3(mount) + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0], + slot_loc["A3"][1], + current_pos[OT3Axis.by_mount(mount)], + ), + ) + await hw_api.move_to( + mount, Point(slot_loc["A3"][0], slot_loc["A3"][1], 100) + ) + await hw_api.drop_tip(mount) + await hw_api.home_z(mount, allow_home_other=False) + current_pos = await hw_api.current_position_ot3(mount) + tip_column += 9 + await hw_api.move_to( + mount, + Point( + tiprack_loc[OT3Axis.X] + tip_column, + tiprack_loc[OT3Axis.Y], + current_pos[OT3Axis.by_mount(mount)], + ), + ) + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="C2") + args = parser.parse_args() + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + xy_speed = 250 + speed_z = 60 + asyncio.run(_main()) diff --git a/hardware-testing/hardware_testing/scripts/tip_straightness.py b/hardware-testing/hardware_testing/scripts/tip_straightness.py new file mode 100644 index 00000000000..4d2e4f2e41d --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/tip_straightness.py @@ -0,0 +1,592 @@ +"""Demo OT3 Gantry Functionality.""" +import argparse +import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os +import sys +import termios +import tty +import json + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + OT3Mount, + OT3Axis, + Point, + CriticalPoint, +) + +from opentrons_shared_data.deck import ( + get_calibration_square_position_in_slot, +) + +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + get_plunger_positions_ot3, + update_pick_up_current, + update_pick_up_distance, + _get_pipette_from_mount, +) + +from opentrons.config.types import ( + CapacitivePassSettings, +) + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[OT3Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") + return position + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + +async def update_pickup_tip_speed(api, mount, speed) -> None: + """Update drop-tip current.""" + pipette = _get_pipette_from_mount(api, mount) + config_model = pipette.pick_up_configurations + config_model.speed = speed + pipette.pick_up_configurations = config_model + print(pipette.pick_up_configurations) + +def replace_coordinates(coordinates, axis, new_value): + coord = list(coordinates) + coord[axis] = new_value + coord = tuple(coord) + return Point(coord[0], coord[1], coord[2]) + + +async def xy_calibrate_gage_block(api, mount, probe_axis, cp, capacitive_settings, gage_block) -> None: + """This function is used to calibrate the gage block with nozzle or probe""" + """ + gage_block = {'X': nominal_center.x, + 'Y': nominal_center.Y, + 'Z': nominal_center.Z, + 'offset': offset_from_ceneter} + """ + z_axis = 2 + retract_mm = 0.3 + z_retract_mm = 10 + current_position = await api.current_position_ot3(mount) + capacitive_loc = await jog(api, current_position, cp) + capacitive_loc = Point( x = capacitive_loc[OT3Axis.X], + y = capacitive_loc[OT3Axis.Y], + z = capacitive_loc[OT3Axis.by_mount(mount)]) + coordinates = replace_coordinates(capacitive_loc, + probe_axis.value, + gage_block[probe_axis.name] - gage_block['offset']) + print(coordinates) + # Probe one side of the probe + await api.move_to(mount, coordinates) + + # Probe the left side of the gage block + point = await api.capacitive_probe( + mount = mount, + moving_axis = probe_axis, + target_pos = gage_block[probe_axis.name] - gage_block['offset'], + direction = -1.0, + pass_settings = capacitive_settings + ) + measured_gage_location_1 = await api.encoder_current_position_ot3(mount, cp) + print(f"measured_gage_location_1: {measured_gage_location_1[probe_axis]}") + coordinates = await api.current_position_ot3(mount) + # Convert to Point + coordinates = Point( x = coordinates[OT3Axis.X], + y = coordinates[OT3Axis.Y], + z = coordinates[OT3Axis.by_mount(mount)]) + + # Retract away from the gage + coordinates = replace_coordinates(coordinates, + probe_axis.value, + coordinates[probe_axis.value] - retract_mm) + await api.move_to(mount, coordinates) + # Retract on Z Axis + coordinates = replace_coordinates(coordinates, + z_axis, + coordinates[z_axis] + z_retract_mm) + # Retract from Block and move to the right side of the block + await api.move_to(mount, coordinates) + + # Move to the other side of the gage block + coordinates = replace_coordinates(coordinates, + probe_axis.value, + gage_block[probe_axis.name] + gage_block['offset']) + # Move to the right + await api.move_to(mount, coordinates) + + # Move down on Z Axis + coordinates = replace_coordinates(coordinates, + z_axis, + coordinates[z_axis] - z_retract_mm) + # Move Down on Z Axis + await api.move_to(mount, coordinates) + # Probe the other side of the probe + point = await api.capacitive_probe( + mount = mount, + moving_axis = probe_axis, + target_pos = gage_block[probe_axis.name] + gage_block['offset'], + direction = 1.0, + pass_settings = capacitive_settings + ) + + measured_gage_location_2 = await api.encoder_current_position_ot3(mount, cp) + print(f"measured_gage_location_2: {measured_gage_location_2[probe_axis]}") + # Retract from Gage Block + coordinates = await api.current_position_ot3(mount) + # Convert to Point + coordinates = Point( x = coordinates[OT3Axis.X], + y = coordinates[OT3Axis.Y], + z = coordinates[OT3Axis.by_mount(mount)]) + # Retract away from the gage + coordinates = replace_coordinates(coordinates, + probe_axis.value, + coordinates[probe_axis.value] + retract_mm) + # Move to the right + await api.move_to(mount, coordinates) + + # Retract on Z Axis + coordinates = replace_coordinates(coordinates, + z_axis, + coordinates[z_axis] + z_retract_mm) + # Move to the right + await api.move_to(mount, coordinates) + + return measured_gage_location_1[probe_axis], measured_gage_location_2[probe_axis] + +async def _main() -> None: + today = datetime.date.today() + tips_to_use = 12 + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.0, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + tip_length = {"T1K": 85.7, "T200": 48.35, "T50": 47.9} + print(hw_api.get_all_attached_instr()[mount]) + pipette_model = hw_api.get_all_attached_instr()[mount]['pipette_id'] + print(pipette_model) + dial_data = {"Ch1": None, + "Ch2": None, + "Ch3": None, + "Ch4": None, + "Ch5": None, + "Ch6": None, + "Ch7": None, + "Ch8": None, + "Trial": None, + "pipette_model": None} + # m_current = float(input("motor_current in amps: ")) + # pick_up_speed = float(input("pick up tip speed in mm/s: ")) + # details = [pipette_model, m_current] + await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) + await hw_api.home_plunger(mount) + await hw_api.set_lights(rails = True) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + home_position = await hw_api.current_position_ot3(mount) + start_time = time.perf_counter() + + if args.tip_size == "T1K": + home_with_tip_position = 164.3 # T1K + if args.tip_size == "T200": + home_with_tip_position = 201.64999999999998 # 200 + elif args.tip_size == "T50": + home_with_tip_position = 192.1 # T50 + + if args.tiprack: + await hw_api.move_to( + mount, + Point( + slot_loc["B2"][0], + slot_loc["B2"][1], + home_position[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.NOZZLE + print("Move to Tiprack") + current_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await jog(hw_api, current_position, cp) + # Start recording the encoder + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + print(f"Start encoder: {init_tip_loc}") + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + await update_pick_up_current(hw_api, mount, m_current) + await update_pickup_tip_speed(hw_api, mount, pick_up_speed) + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + await home_ot3(hw_api, [OT3Axis.by_mount(mount)]) + home_with_tip_position = await hw_api.current_position_ot3(mount, critical_point= CriticalPoint.TIP) + await hw_api.move_to( + mount, + Point( + current_position[OT3Axis.X], + current_position[OT3Axis.Y], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + + encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] + # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + print(f"End Encoder: {final_tip_loc}") + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + location = "Tiprack" + tip_count = 1 + test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + tiprack_loc = [ + tiprack_loc[OT3Axis.X], + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)], + ] + + xy_probe_settings = CapacitivePassSettings( + prep_distance_mm= 0.5, + max_overrun_distance_mm= args.max_overrun_distance_mm, # 8 + speed_mm_per_s= args.probe_speed, + sensor_threshold_pf= args.sensor_threshold_pf, + ) + capacitive_probe_settings = CapacitivePassSettings( + prep_distance_mm = 0, + max_overrun_distance_mm = args.max_overrun_distance_mm, + speed_mm_per_s = args.probe_speed, + sensor_threshold_pf = args.sensor_threshold_pf + ) + nominal_center = get_calibration_square_position_in_slot(8) + print(f"Nominal Center: {nominal_center}") + # Retract distance + xy_retract_mm = 0.3 + z_retract_mm = 10 + tip_count = 0 + x_offset = 0 + y_offset = 0 + try: + await hw_api.move_to( + mount, + Point( + nominal_center.x, + nominal_center.y, + home_position[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.NOZZLE + print("Move to left edge of the gage block.") + current_position = await hw_api.current_position_ot3(mount) + # capacitive_loc = await jog(hw_api, current_position, cp) + x_gage_block_mm = 9.2 + nozzle_nominal_diameter = 5.15 + tolerance = 3 + x_offset_from_center = x_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance + gage_block_settings = {'X': nominal_center.x, + 'Y': nominal_center.y, + 'Z': nominal_center.z, + 'offset': x_offset_from_center} + + x_minus, x_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.X, cp, xy_probe_settings, gage_block_settings) + measured_gage = (x_plus - nozzle_nominal_diameter/2) - (x_minus + nozzle_nominal_diameter/2) + print("Measured Gage: ", measured_gage) + tolerance = 2 + y_gage_block_mm = 3.32 + y_offset_from_center = y_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance + gage_block_settings = {'X': nominal_center.x, + 'Y': nominal_center.y, + 'Z': nominal_center.z, + 'offset': y_offset_from_center} + + y_minus, y_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.Y, cp, xy_probe_settings, gage_block_settings) + measured_gage = (y_plus - nozzle_nominal_diameter/2) - (y_minus + nozzle_nominal_diameter/2) + print("Measured Gage: ", measured_gage) + # current_position = await hw_api.current_position_ot3(mount) + # api.move_to(mount, Point(nominal.X, nominal.Y, current_position)) + # + # await xy_calibration_gage + # # Probe the X axis left and right with the nozzle + # await hw_api.move_to(mount, + # Point(nominal_center.x - target, + # capacitive_loc[OT3Axis.Y], + # capacitive_loc[OT3Axis.by_mount(mount)])) + # # capacitive + # point = await hw_api.capacitive_probe( + # mount = mount, + # moving_axis =OT3Axis.X, + # target_pos = nominal_center.x - target, + # direction = -1.0, + # pass_settings = xy_probe_settings + # ) + # left_block_location = await hw_api.encoder_current_position_ot3(mount, cp) + # print(f"left_block_location: {left_block_location}") + # current_position = await hw_api.current_position_ot3(mount) + # # Retract from Block + # await hw_api.move_to(mount, + # Point(current_position[OT3Axis.X]-xy_retract_mm, + # capacitive_loc[OT3Axis.Y], + # current_position[OT3Axis.by_mount(mount)])) + # await hw_api.move_to(mount, + # Point(current_position[OT3Axis.X]-xy_retract_mm , + # capacitive_loc[OT3Axis.Y], + # current_position[OT3Axis.by_mount(mount)]+z_retract_mm)) + # await hw_api.move_to(mount, + # Point(nominal_center.x + target, + # capacitive_loc[OT3Axis.Y], + # current_position[OT3Axis.by_mount(mount)]+z_retract_mm)) + # await hw_api.move_to(mount, + # Point(nominal_center.x + target, + # capacitive_loc[OT3Axis.Y], + # capacitive_loc[OT3Axis.by_mount(mount)])) + # point = await hw_api.capacitive_probe( + # mount = mount, + # moving_axis =OT3Axis.X, + # target_pos = nominal_center.x + target, + # direction = 1.0, + # pass_settings = xy_probe_settings + # ) + # right_block_location = await hw_api.encoder_current_position_ot3(mount, cp) + # print(f"right_block_location: {right_block_location}") + # # Retract from Gage Block + # current_position = await hw_api.current_position_ot3(mount) + # await hw_api.move_to(mount, + # Point(current_position[OT3Axis.X] + xy_retract_mm, + # current_position[OT3Axis.Y], + # current_position[OT3Axis.by_mount(mount)])) + # Home Z + + + # #----------------------------Y Axis Probe Function -------------------- + # current_position = await hw_api.current_position_ot3(mount) + # y_gage_block_mm = 3.32 + # nozzle_nominal_diameter = 5.15 + # tolerance = 0.5 + # # The starting target position + # y_offset = y_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance + # + # await hw_api.move_to(mount, + # Point(nominal_center.x, + # nominal_center.y - y_offset, + # home_position[OT3Axis.by_mount(mount)])) + # + # capacitive_loc = await jog(hw_api, current_position, cp) + # # Probe the X axis left and right with the nozzle + # await hw_api.move_to(mount, + # Point(capacitive_loc[OT3Axis.X], + # nominal_center.y - y_offset, + # capacitive_loc[OT3Axis.by_mount(mount)])) + # # capacitive + # point = await hw_api.capacitive_probe( + # mount = mount, + # moving_axis =OT3Axis.Y, + # target_pos = nominal_center.y - y_offset - 3, + # direction = -1.0, + # pass_settings = xy_probe_settings + # ) + # front_block_location = await hw_api.encoder_current_position_ot3(mount, cp) + # print(f"front_block_location: {front_block_location}") + # current_position = await hw_api.current_position_ot3(mount) + # # Retract from the block by a small amount + # await hw_api.move_to(mount, + # Point(current_position[OT3Axis.X], + # current_position[OT3Axis.Y] - xy_retract_mm, + # current_position[OT3Axis.by_mount(mount)])) + # await hw_api.move_to(mount, + # Point(current_position[OT3Axis.X], + # current_position[OT3Axis.Y] - xy_retract_mm, + # current_position[OT3Axis.by_mount(mount)]+z_retract_mm)) + # await hw_api.move_to(mount, + # Point(current_position[OT3Axis.X], + # nominal_center.y + y_offset, + # current_position[OT3Axis.by_mount(mount)]+z_retract_mm)) + # await hw_api.move_to(mount, + # Point(current_position[OT3Axis.X], + # nominal_center.y + y_offset, + # capacitive_loc[OT3Axis.by_mount(mount)])) + # + # point = await hw_api.capacitive_probe( + # mount = mount, + # moving_axis =OT3Axis.Y, + # target_pos = nominal_center.y - y_offset, + # direction = 1.0, + # pass_settings = xy_probe_settings + # ) + # back_block_location = await hw_api.encoder_current_position_ot3(mount, cp) + # print(f"back_block_location: {back_block_location}") + + + + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--tiprack", action="store_true") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument('-o', '--slot', type=int, required=False, help='Deck slot number', default=2) + parser.add_argument("--tip_size", type=str, default="T50", help="Tip Size") + parser.add_argument("--prep_distance_mm", type = float, default = 5, help="Prep Probe Distance") + parser.add_argument("--max_overrun_distance_mm", type = float, default = 6, help = "Max Probe Distance") + parser.add_argument("--probe_speed", type = int, default = 0.5, help = "Probe Speed") + parser.add_argument("--sensor_threshold_pf", type = float, default = 1.5, help = "capacitive sensor threshold") + + args = parser.parse_args() + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + + asyncio.run(_main()) From 67c4e9ee186fd6235eebca896da8b79618ef959b Mon Sep 17 00:00:00 2001 From: Carlos Date: Fri, 5 May 2023 16:00:37 -0400 Subject: [PATCH 19/53] returning the capacitive probe function to orginal --- api/src/opentrons/hardware_control/ot3api.py | 88 +++++- .../scripts/tip_straightness.py | 253 +++++++++--------- 2 files changed, 214 insertions(+), 127 deletions(-) diff --git a/api/src/opentrons/hardware_control/ot3api.py b/api/src/opentrons/hardware_control/ot3api.py index 415c363c69f..994a15720de 100644 --- a/api/src/opentrons/hardware_control/ot3api.py +++ b/api/src/opentrons/hardware_control/ot3api.py @@ -1952,12 +1952,93 @@ async def liquid_probe( return final_position, encoder_pos async def capacitive_probe( + self, + mount: OT3Mount, + moving_axis: OT3Axis, + target_pos: float, + pass_settings: CapacitivePassSettings, + retract_after: bool = True, + ) -> float: + """Determine the position of something using the capacitive sensor. + This function orchestrates detecting the position of a collision between the + capacitive probe on the tool on the specified mount, and some fixed element + of the robot. + When calling this function, the mount's probe critical point should already + be aligned in the probe axis with the item to be probed. + It will move the mount's probe critical point to a small distance behind + the expected position of the element (which is target_pos, in deck coordinates, + in the axis to be probed) while running the tool's capacitive sensor. When the + sensor senses contact, the mount stops. + This function moves away and returns the sensed position. + This sensed position can be used in several ways, including + - To get an absolute position in deck coordinates of whatever was + targeted, if something was guaranteed to be physically present. + - To detect whether a collision occured at all. If this function + returns a value far enough past the anticipated position, then it indicates + there was no material there. + """ + if moving_axis not in [ + OT3Axis.X, + OT3Axis.Y, + ] and moving_axis != OT3Axis.by_mount(mount): + raise RuntimeError( + "Probing must be done with a gantry axis or the mount of the sensing" + " tool" + ) + + here = await self.gantry_position(mount, refresh=True) + origin_pos = moving_axis.of_point(here) + if origin_pos < target_pos: + pass_start = target_pos - pass_settings.prep_distance_mm + pass_distance = ( + pass_settings.prep_distance_mm + pass_settings.max_overrun_distance_mm + ) + else: + pass_start = target_pos + pass_settings.prep_distance_mm + pass_distance = -1.0 * ( + pass_settings.prep_distance_mm + pass_settings.max_overrun_distance_mm + ) + machine_pass_distance = moving_axis.of_point( + machine_vector_from_deck_vector( + moving_axis.set_in_point(top_types.Point(0, 0, 0), pass_distance), + self._transforms.deck_calibration.attitude, + ) + ) + pass_start_pos = moving_axis.set_in_point(here, pass_start) + await self.move_to(mount, pass_start_pos) + if mount == OT3Mount.GRIPPER: + probe = self._gripper_handler.get_attached_probe() + assert probe + await self._backend.capacitive_probe( + mount, + moving_axis, + machine_pass_distance, + pass_settings.speed_mm_per_s, + pass_settings.sensor_threshold_pf, + GripperProbe.to_type(probe), + ) + else: + await self._backend.capacitive_probe( + mount, + moving_axis, + machine_pass_distance, + pass_settings.speed_mm_per_s, + pass_settings.sensor_threshold_pf, + probe=InstrumentProbeType.PRIMARY, + ) + end_pos = await self.gantry_position(mount, refresh=True) + if retract_after: + await self.move_to(mount, pass_start_pos) + return moving_axis.of_point(end_pos) + + async def capacitive_nozzle( self, mount: OT3Mount, moving_axis: OT3Axis, target_pos: float, direction: float, pass_settings: CapacitivePassSettings, + probe_type: Optional[InstrumentProbeType] = InstrumentProbeType.PRIMARY, ) -> float: """Determine the position of something using the capacitive sensor. @@ -1997,7 +2078,7 @@ async def capacitive_probe( # if origin_pos < target_pos: # pass_start = target_pos - pass_settings.prep_distance_mm # # print(f"pass_start: {pass_start}") - pass_distance = -direction * ( + pass_distance = direction * ( pass_settings.prep_distance_mm + pass_settings.max_overrun_distance_mm ) # else: @@ -2034,12 +2115,15 @@ async def capacitive_probe( machine_pass_distance, pass_settings.speed_mm_per_s, pass_settings.sensor_threshold_pf, - probe=InstrumentProbeType.PRIMARY, + probe=probe_type, ) end_pos = await self.gantry_position(mount, refresh=True) # await self.move_to(mount, pass_start_pos) return moving_axis.of_point(end_pos) + + + async def capacitive_sweep( self, mount: OT3Mount, diff --git a/hardware-testing/hardware_testing/scripts/tip_straightness.py b/hardware-testing/hardware_testing/scripts/tip_straightness.py index 4d2e4f2e41d..4165dde3a94 100644 --- a/hardware-testing/hardware_testing/scripts/tip_straightness.py +++ b/hardware-testing/hardware_testing/scripts/tip_straightness.py @@ -25,6 +25,11 @@ get_calibration_square_position_in_slot, ) +from opentrons.hardware_control.ot3_calibration import ( + find_calibration_structure_height, + find_slot_center_binary, +) + from hardware_testing.opentrons_api.helpers_ot3 import ( build_async_ot3_hardware_api, home_ot3, @@ -32,6 +37,7 @@ update_pick_up_current, update_pick_up_distance, _get_pipette_from_mount, + get_slot_calibration_square_position_ot3 ) from opentrons.config.types import ( @@ -175,8 +181,41 @@ def replace_coordinates(coordinates, axis, new_value): coord = tuple(coord) return Point(coord[0], coord[1], coord[2]) +async def z_calibrate_gage_block(api, mount, probe_axis, cp, capacitive_settings, gage_block): + + z_axis = 2 + z_retract_mm = 10 + current_position = await api.current_position_ot3(mount) + capacitive_loc = await jog(api, current_position, cp) + capacitive_loc = Point( x = capacitive_loc[OT3Axis.X], + y = capacitive_loc[OT3Axis.Y], + z = capacitive_loc[OT3Axis.by_mount(mount)]) + # Probe the left side of the gage block + point = await api.capacitive_nozzle( + mount = mount, + moving_axis = probe_axis, + target_pos = gage_block['Z'] - gage_block['offset'], + direction = 1.0, + pass_settings = capacitive_settings + ) + z_measured_gage_location = await api.encoder_current_position_ot3(mount, cp) + print(f"Z measured_gage_location: {z_measured_gage_location[probe_axis]}") + + coordinates = await api.current_position_ot3(mount) + coordinates = Point( x = coordinates[OT3Axis.X], + y = coordinates[OT3Axis.Y], + z = coordinates[OT3Axis.by_mount(mount)]) + + # Retract away from the gage + coordinates = replace_coordinates(coordinates, + z_axis, + coordinates[z_axis] + z_retract_mm) + await api.move_to(mount, coordinates) + + return z_measured_gage_location -async def xy_calibrate_gage_block(api, mount, probe_axis, cp, capacitive_settings, gage_block) -> None: + +async def xy_calibrate_gage_block(api, mount, probe_axis, cp, capacitive_settings, gage_block): """This function is used to calibrate the gage block with nozzle or probe""" """ gage_block = {'X': nominal_center.x, @@ -200,7 +239,7 @@ async def xy_calibrate_gage_block(api, mount, probe_axis, cp, capacitive_setting await api.move_to(mount, coordinates) # Probe the left side of the gage block - point = await api.capacitive_probe( + point = await api.capacitive_nozzle( mount = mount, moving_axis = probe_axis, target_pos = gage_block[probe_axis.name] - gage_block['offset'], @@ -241,7 +280,7 @@ async def xy_calibrate_gage_block(api, mount, probe_axis, cp, capacitive_setting # Move Down on Z Axis await api.move_to(mount, coordinates) # Probe the other side of the probe - point = await api.capacitive_probe( + point = await api.capacitive_nozzle( mount = mount, moving_axis = probe_axis, target_pos = gage_block[probe_axis.name] + gage_block['offset'], @@ -384,8 +423,8 @@ async def _main() -> None: speed_mm_per_s= args.probe_speed, sensor_threshold_pf= args.sensor_threshold_pf, ) - capacitive_probe_settings = CapacitivePassSettings( - prep_distance_mm = 0, + z_probe_settings = CapacitivePassSettings( + prep_distance_mm = 0.5, max_overrun_distance_mm = args.max_overrun_distance_mm, speed_mm_per_s = args.probe_speed, sensor_threshold_pf = args.sensor_threshold_pf @@ -410,19 +449,68 @@ async def _main() -> None: cp = CriticalPoint.NOZZLE print("Move to left edge of the gage block.") current_position = await hw_api.current_position_ot3(mount) - # capacitive_loc = await jog(hw_api, current_position, cp) x_gage_block_mm = 9.2 nozzle_nominal_diameter = 5.15 - tolerance = 3 + tolerance = 2 x_offset_from_center = x_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance gage_block_settings = {'X': nominal_center.x, 'Y': nominal_center.y, 'Z': nominal_center.z, 'offset': x_offset_from_center} + # Find the X edges of the gage block + x_minus, x_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.X, cp, xy_probe_settings, gage_block_settings) + x_measured_gage = (x_plus - nozzle_nominal_diameter/2) - (x_minus + nozzle_nominal_diameter/2) + print("Measured Gage: ", x_measured_gage) + tolerance = 2 + y_gage_block_mm = 3.32 + y_offset_from_center = y_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance + gage_block_settings = {'X': nominal_center.x, + 'Y': nominal_center.y, + 'Z': nominal_center.z, + 'offset': y_offset_from_center} + # find the y edges of the gage block + y_minus, y_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.Y, cp, xy_probe_settings, gage_block_settings) + y_measured_gage = (y_plus - nozzle_nominal_diameter/2) - (y_minus + nozzle_nominal_diameter/2) + print("Measured Gage: ", y_measured_gage) + current_position = await hw_api.current_position_ot3(mount) + await hw_api.move_to(mount, + Point( current_position[OT3Axis.X], + current_position[OT3Axis.Y], + current_position[OT3Axis.by_mount(mount)] + z_retract_mm)) + center_x = (x_plus - nozzle_nominal_diameter/2) - x_measured_gage/2 + center_y = (y_plus - nozzle_nominal_diameter/2) - y_measured_gage/2 + await hw_api.move_to(mount, + Point( center_x, + center_y, + current_position[OT3Axis.by_mount(mount)] + z_retract_mm)) + tolerance = 2 + z_offset_from_center = z_retract_mm + tolerance + calibrated_height = 50 + gage_block_settings = {'X': center_x, + 'Y': center_y, + 'Z': calibrated_height, + 'offset': z_offset_from_center} + + z_measured_gage = await z_calibrate_gage_block(hw_api, mount, + OT3Axis.by_mount(mount), + cp, z_probe_settings, gage_block_settings) + + await hw_api.home_z(mount) + input("Insert the Probe Attachment") + # ------------------------------Probe attachment----------------------- + x_gage_block_mm = 9.2 + nozzle_nominal_diameter = 4.0 + tolerance = 3 + x_offset_from_center = x_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance + gage_block_settings = {'X': nominal_center.x, + 'Y': nominal_center.y, + 'Z': nominal_center.z, + 'offset': x_offset_from_center} + # Find the X edges of the gage block x_minus, x_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.X, cp, xy_probe_settings, gage_block_settings) measured_gage = (x_plus - nozzle_nominal_diameter/2) - (x_minus + nozzle_nominal_diameter/2) - print("Measured Gage: ", measured_gage) + print("Measured Gage probe: ", measured_gage) tolerance = 2 y_gage_block_mm = 3.32 y_offset_from_center = y_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance @@ -430,124 +518,39 @@ async def _main() -> None: 'Y': nominal_center.y, 'Z': nominal_center.z, 'offset': y_offset_from_center} - + # find the y edges of the gage block y_minus, y_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.Y, cp, xy_probe_settings, gage_block_settings) measured_gage = (y_plus - nozzle_nominal_diameter/2) - (y_minus + nozzle_nominal_diameter/2) - print("Measured Gage: ", measured_gage) - # current_position = await hw_api.current_position_ot3(mount) - # api.move_to(mount, Point(nominal.X, nominal.Y, current_position)) - # - # await xy_calibration_gage - # # Probe the X axis left and right with the nozzle - # await hw_api.move_to(mount, - # Point(nominal_center.x - target, - # capacitive_loc[OT3Axis.Y], - # capacitive_loc[OT3Axis.by_mount(mount)])) - # # capacitive - # point = await hw_api.capacitive_probe( - # mount = mount, - # moving_axis =OT3Axis.X, - # target_pos = nominal_center.x - target, - # direction = -1.0, - # pass_settings = xy_probe_settings - # ) - # left_block_location = await hw_api.encoder_current_position_ot3(mount, cp) - # print(f"left_block_location: {left_block_location}") - # current_position = await hw_api.current_position_ot3(mount) - # # Retract from Block - # await hw_api.move_to(mount, - # Point(current_position[OT3Axis.X]-xy_retract_mm, - # capacitive_loc[OT3Axis.Y], - # current_position[OT3Axis.by_mount(mount)])) - # await hw_api.move_to(mount, - # Point(current_position[OT3Axis.X]-xy_retract_mm , - # capacitive_loc[OT3Axis.Y], - # current_position[OT3Axis.by_mount(mount)]+z_retract_mm)) - # await hw_api.move_to(mount, - # Point(nominal_center.x + target, - # capacitive_loc[OT3Axis.Y], - # current_position[OT3Axis.by_mount(mount)]+z_retract_mm)) - # await hw_api.move_to(mount, - # Point(nominal_center.x + target, - # capacitive_loc[OT3Axis.Y], - # capacitive_loc[OT3Axis.by_mount(mount)])) - # point = await hw_api.capacitive_probe( - # mount = mount, - # moving_axis =OT3Axis.X, - # target_pos = nominal_center.x + target, - # direction = 1.0, - # pass_settings = xy_probe_settings - # ) - # right_block_location = await hw_api.encoder_current_position_ot3(mount, cp) - # print(f"right_block_location: {right_block_location}") - # # Retract from Gage Block - # current_position = await hw_api.current_position_ot3(mount) - # await hw_api.move_to(mount, - # Point(current_position[OT3Axis.X] + xy_retract_mm, - # current_position[OT3Axis.Y], - # current_position[OT3Axis.by_mount(mount)])) - # Home Z - - - # #----------------------------Y Axis Probe Function -------------------- - # current_position = await hw_api.current_position_ot3(mount) - # y_gage_block_mm = 3.32 - # nozzle_nominal_diameter = 5.15 - # tolerance = 0.5 - # # The starting target position - # y_offset = y_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance - # - # await hw_api.move_to(mount, - # Point(nominal_center.x, - # nominal_center.y - y_offset, - # home_position[OT3Axis.by_mount(mount)])) - # - # capacitive_loc = await jog(hw_api, current_position, cp) - # # Probe the X axis left and right with the nozzle - # await hw_api.move_to(mount, - # Point(capacitive_loc[OT3Axis.X], - # nominal_center.y - y_offset, - # capacitive_loc[OT3Axis.by_mount(mount)])) - # # capacitive - # point = await hw_api.capacitive_probe( - # mount = mount, - # moving_axis =OT3Axis.Y, - # target_pos = nominal_center.y - y_offset - 3, - # direction = -1.0, - # pass_settings = xy_probe_settings - # ) - # front_block_location = await hw_api.encoder_current_position_ot3(mount, cp) - # print(f"front_block_location: {front_block_location}") - # current_position = await hw_api.current_position_ot3(mount) - # # Retract from the block by a small amount - # await hw_api.move_to(mount, - # Point(current_position[OT3Axis.X], - # current_position[OT3Axis.Y] - xy_retract_mm, - # current_position[OT3Axis.by_mount(mount)])) - # await hw_api.move_to(mount, - # Point(current_position[OT3Axis.X], - # current_position[OT3Axis.Y] - xy_retract_mm, - # current_position[OT3Axis.by_mount(mount)]+z_retract_mm)) - # await hw_api.move_to(mount, - # Point(current_position[OT3Axis.X], - # nominal_center.y + y_offset, - # current_position[OT3Axis.by_mount(mount)]+z_retract_mm)) - # await hw_api.move_to(mount, - # Point(current_position[OT3Axis.X], - # nominal_center.y + y_offset, - # capacitive_loc[OT3Axis.by_mount(mount)])) - # - # point = await hw_api.capacitive_probe( - # mount = mount, - # moving_axis =OT3Axis.Y, - # target_pos = nominal_center.y - y_offset, - # direction = 1.0, - # pass_settings = xy_probe_settings - # ) - # back_block_location = await hw_api.encoder_current_position_ot3(mount, cp) - # print(f"back_block_location: {back_block_location}") - + print("Measured Gage probe: ", measured_gage) + current_position = await hw_api.current_position_ot3(mount) + await hw_api.move_to(mount, + Point( current_position[OT3Axis.X], + current_position[OT3Axis.Y], + current_position[OT3Axis.by_mount(mount)] + z_retract_mm)) + center_x = (x_plus - nozzle_nominal_diameter/2) - x_measured_gage/2 + center_y = (y_plus - nozzle_nominal_diameter/2) - y_measured_gage/2 + await hw_api.move_to(mount, + Point( center_x, + center_y, + current_position[OT3Axis.by_mount(mount)] + z_retract_mm)) + tolerance = 2 + z_offset_from_center = z_retract_mm + tolerance + calibrated_height = 50 + gage_block_settings = {'X': center_x, + 'Y': center_y, + 'Z': calibrated_height, + 'offset': z_offset_from_center} + + z_measured_gage = await z_calibrate_gage_block(hw_api, mount, + OT3Axis.by_mount(mount), + cp, z_probe_settings, gage_block_settings) + + # --------------------------Deck Slot Calibration ------------------------- + input("Press Enter to find the deck slot center") + expected = get_calibration_square_position_in_slot(slot=5) # or any other slot + found_z= await find_calibration_structure_height(hw_api, mount, expected) + found_xyz = await find_slot_center_binary(hw_api, mount, expected._replace(z=found_z)) await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) except KeyboardInterrupt: @@ -580,8 +583,8 @@ async def _main() -> None: parser.add_argument("--tip_size", type=str, default="T50", help="Tip Size") parser.add_argument("--prep_distance_mm", type = float, default = 5, help="Prep Probe Distance") parser.add_argument("--max_overrun_distance_mm", type = float, default = 6, help = "Max Probe Distance") - parser.add_argument("--probe_speed", type = int, default = 0.5, help = "Probe Speed") - parser.add_argument("--sensor_threshold_pf", type = float, default = 1.5, help = "capacitive sensor threshold") + parser.add_argument("--probe_speed", type = int, default = 0.3, help = "Probe Speed") + parser.add_argument("--sensor_threshold_pf", type = float, default = 0.9, help = "capacitive sensor threshold") args = parser.parse_args() if args.mount == "left": From 7fb8bba60cbb11cafc093f0b6003c4c767447a4e Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 9 May 2023 09:36:45 -0400 Subject: [PATCH 20/53] working test script --- .../scripts/tip_straightness.py | 471 +++++++++++------- 1 file changed, 298 insertions(+), 173 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/tip_straightness.py b/hardware-testing/hardware_testing/scripts/tip_straightness.py index 4165dde3a94..99d509c3603 100644 --- a/hardware-testing/hardware_testing/scripts/tip_straightness.py +++ b/hardware-testing/hardware_testing/scripts/tip_straightness.py @@ -44,6 +44,62 @@ CapacitivePassSettings, ) + +def plate_definition(target_hole): + starting_hole_mm_x = 14.33 + starting_hole_mm_y = 11.2 + columns = 12 + rows = 4 + x_spacing = 9 + y_spacing = 18 + lookup_holes = {'1':(0,0), + '2':(0,1), + '3':(0,2), + '4':(0,3), + '5':(0,4), + '6':(0,5), + '7':(0,6), + '8':(0,7), + '9':(0,8), + '10':(0,9), + '11':(0,10), + '12':(0,11), + '13':(1,0), + '14':(1,1), + '15':(1,2), + '16':(1,3), + '17':(1,4), + '18':(1,5), + '19':(1,6), + '20':(1,7), + '21':(1,8), + '22':(1,9), + '23':(1,10), + '24':(1,11), + '25':(2,0), + '26':(2,1), + '27':(2,2), + '28':(2,3), + '29':(2,4), + '30':(2,5), + '31':(2,6), + '32':(2,7), + '33':(2,8), + '34':(2,9), + '35':(2,10), + '36':(2,11), + '37':(3,0), + '39':(3,2), + '41':(3,4), + '43':(3,6), + '45':(3,8), + '47':(3,10), + } + + calculate_x_distance = lookup_holes[str(target_hole)][1]*x_spacing + starting_hole_mm_x + calculate_y_distance = lookup_holes[str(target_hole)][0]*y_spacing + starting_hole_mm_y + return Point(calculate_x_distance, calculate_y_distance, 0) + def getch(): """ fd: file descriptor stdout, stdin, stderr @@ -81,7 +137,7 @@ async def jog(api, position, cp) -> Dict[OT3Axis, float]: Click >> Enter << to save position Click >> q << to quit the test script """ - print(information_str) + # print(information_str) while True: input = getch() if input == "a": @@ -195,7 +251,7 @@ async def z_calibrate_gage_block(api, mount, probe_axis, cp, capacitive_settings mount = mount, moving_axis = probe_axis, target_pos = gage_block['Z'] - gage_block['offset'], - direction = 1.0, + direction = -1.0, pass_settings = capacitive_settings ) z_measured_gage_location = await api.encoder_current_position_ot3(mount, cp) @@ -243,7 +299,7 @@ async def xy_calibrate_gage_block(api, mount, probe_axis, cp, capacitive_setting mount = mount, moving_axis = probe_axis, target_pos = gage_block[probe_axis.name] - gage_block['offset'], - direction = -1.0, + direction = 1.0, pass_settings = capacitive_settings ) measured_gage_location_1 = await api.encoder_current_position_ot3(mount, cp) @@ -284,7 +340,7 @@ async def xy_calibrate_gage_block(api, mount, probe_axis, cp, capacitive_setting mount = mount, moving_axis = probe_axis, target_pos = gage_block[probe_axis.name] + gage_block['offset'], - direction = 1.0, + direction = -1.0, pass_settings = capacitive_settings ) @@ -312,6 +368,8 @@ async def xy_calibrate_gage_block(api, mount, probe_axis, cp, capacitive_setting return measured_gage_location_1[probe_axis], measured_gage_location_2[probe_axis] + + async def _main() -> None: today = datetime.date.today() tips_to_use = 12 @@ -348,6 +406,7 @@ async def _main() -> None: "pipette_model": None} # m_current = float(input("motor_current in amps: ")) # pick_up_speed = float(input("pick up tip speed in mm/s: ")) + pick_up_speed = 10 # details = [pipette_model, m_current] await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) await hw_api.home_plunger(mount) @@ -363,72 +422,32 @@ async def _main() -> None: elif args.tip_size == "T50": home_with_tip_position = 192.1 # T50 - if args.tiprack: - await hw_api.move_to( - mount, - Point( - slot_loc["B2"][0], - slot_loc["B2"][1], - home_position[OT3Axis.by_mount(mount)], - ), - ) - cp = CriticalPoint.NOZZLE - print("Move to Tiprack") - current_position = await hw_api.current_position_ot3(mount) - tiprack_loc = await jog(hw_api, current_position, cp) - # Start recording the encoder - init_tip_loc = await hw_api.encoder_current_position_ot3( - mount, CriticalPoint.NOZZLE - ) - print(f"Start encoder: {init_tip_loc}") - init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] - encoder_position = init_tip_loc - init_tip_loc = await hw_api.encoder_current_position_ot3( - mount, CriticalPoint.NOZZLE - ) - await update_pick_up_current(hw_api, mount, m_current) - await update_pickup_tip_speed(hw_api, mount, pick_up_speed) - # Move pipette to Force Gauge press location - final_tip_loc = await hw_api.pick_up_tip( - mount, tip_length=tip_length[args.tip_size] - ) - await home_ot3(hw_api, [OT3Axis.by_mount(mount)]) - home_with_tip_position = await hw_api.current_position_ot3(mount, critical_point= CriticalPoint.TIP) - await hw_api.move_to( - mount, - Point( - current_position[OT3Axis.X], - current_position[OT3Axis.Y], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - critical_point=CriticalPoint.TIP, - ) - - encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] - # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) - print(f"End Encoder: {final_tip_loc}") - final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] - location = "Tiprack" - tip_count = 1 - test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] - tiprack_loc = [ - tiprack_loc[OT3Axis.X], - tiprack_loc[OT3Axis.Y], - tiprack_loc[OT3Axis.by_mount(mount)], - ] - xy_probe_settings = CapacitivePassSettings( - prep_distance_mm= 0.5, + prep_distance_mm= 0.6, max_overrun_distance_mm= args.max_overrun_distance_mm, # 8 speed_mm_per_s= args.probe_speed, - sensor_threshold_pf= args.sensor_threshold_pf, + sensor_threshold_pf= args.probe_threshold_pf, ) z_probe_settings = CapacitivePassSettings( - prep_distance_mm = 0.5, + prep_distance_mm = 0.6, + max_overrun_distance_mm = args.max_overrun_distance_mm, + speed_mm_per_s = args.probe_speed, + sensor_threshold_pf = args.probe_threshold_pf + ) + + xy_nozzle_settings = CapacitivePassSettings( + prep_distance_mm= 0.6, + max_overrun_distance_mm= args.max_overrun_distance_mm, # 8 + speed_mm_per_s= args.probe_speed, + sensor_threshold_pf= args.nozzle_threshold_pf, + ) + z_nozzle_settings = CapacitivePassSettings( + prep_distance_mm = 0.6, max_overrun_distance_mm = args.max_overrun_distance_mm, speed_mm_per_s = args.probe_speed, - sensor_threshold_pf = args.sensor_threshold_pf + sensor_threshold_pf = args.nozzle_threshold_pf ) + nominal_center = get_calibration_square_position_in_slot(8) print(f"Nominal Center: {nominal_center}") # Retract distance @@ -438,119 +457,224 @@ async def _main() -> None: x_offset = 0 y_offset = 0 try: - await hw_api.move_to( - mount, - Point( - nominal_center.x, - nominal_center.y, - home_position[OT3Axis.by_mount(mount)], - ), - ) - cp = CriticalPoint.NOZZLE - print("Move to left edge of the gage block.") - current_position = await hw_api.current_position_ot3(mount) - x_gage_block_mm = 9.2 - nozzle_nominal_diameter = 5.15 - tolerance = 2 - x_offset_from_center = x_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance - gage_block_settings = {'X': nominal_center.x, - 'Y': nominal_center.y, - 'Z': nominal_center.z, - 'offset': x_offset_from_center} - # Find the X edges of the gage block - x_minus, x_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.X, cp, xy_probe_settings, gage_block_settings) - x_measured_gage = (x_plus - nozzle_nominal_diameter/2) - (x_minus + nozzle_nominal_diameter/2) - print("Measured Gage: ", x_measured_gage) - tolerance = 2 - y_gage_block_mm = 3.32 - y_offset_from_center = y_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance - gage_block_settings = {'X': nominal_center.x, - 'Y': nominal_center.y, - 'Z': nominal_center.z, - 'offset': y_offset_from_center} - # find the y edges of the gage block - y_minus, y_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.Y, cp, xy_probe_settings, gage_block_settings) - y_measured_gage = (y_plus - nozzle_nominal_diameter/2) - (y_minus + nozzle_nominal_diameter/2) - print("Measured Gage: ", y_measured_gage) - - current_position = await hw_api.current_position_ot3(mount) - await hw_api.move_to(mount, - Point( current_position[OT3Axis.X], - current_position[OT3Axis.Y], - current_position[OT3Axis.by_mount(mount)] + z_retract_mm)) - center_x = (x_plus - nozzle_nominal_diameter/2) - x_measured_gage/2 - center_y = (y_plus - nozzle_nominal_diameter/2) - y_measured_gage/2 - await hw_api.move_to(mount, - Point( center_x, - center_y, - current_position[OT3Axis.by_mount(mount)] + z_retract_mm)) - tolerance = 2 - z_offset_from_center = z_retract_mm + tolerance - calibrated_height = 50 - gage_block_settings = {'X': center_x, - 'Y': center_y, - 'Z': calibrated_height, - 'offset': z_offset_from_center} - - z_measured_gage = await z_calibrate_gage_block(hw_api, mount, - OT3Axis.by_mount(mount), - cp, z_probe_settings, gage_block_settings) - - await hw_api.home_z(mount) - input("Insert the Probe Attachment") - # ------------------------------Probe attachment----------------------- - x_gage_block_mm = 9.2 - nozzle_nominal_diameter = 4.0 - tolerance = 3 - x_offset_from_center = x_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance - gage_block_settings = {'X': nominal_center.x, - 'Y': nominal_center.y, - 'Z': nominal_center.z, - 'offset': x_offset_from_center} - # Find the X edges of the gage block - x_minus, x_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.X, cp, xy_probe_settings, gage_block_settings) - measured_gage = (x_plus - nozzle_nominal_diameter/2) - (x_minus + nozzle_nominal_diameter/2) - print("Measured Gage probe: ", measured_gage) - tolerance = 2 - y_gage_block_mm = 3.32 - y_offset_from_center = y_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance - gage_block_settings = {'X': nominal_center.x, - 'Y': nominal_center.y, - 'Z': nominal_center.z, - 'offset': y_offset_from_center} - # find the y edges of the gage block - y_minus, y_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.Y, cp, xy_probe_settings, gage_block_settings) - measured_gage = (y_plus - nozzle_nominal_diameter/2) - (y_minus + nozzle_nominal_diameter/2) - print("Measured Gage probe: ", measured_gage) - - current_position = await hw_api.current_position_ot3(mount) - await hw_api.move_to(mount, - Point( current_position[OT3Axis.X], - current_position[OT3Axis.Y], - current_position[OT3Axis.by_mount(mount)] + z_retract_mm)) - center_x = (x_plus - nozzle_nominal_diameter/2) - x_measured_gage/2 - center_y = (y_plus - nozzle_nominal_diameter/2) - y_measured_gage/2 - await hw_api.move_to(mount, - Point( center_x, - center_y, - current_position[OT3Axis.by_mount(mount)] + z_retract_mm)) - tolerance = 2 - z_offset_from_center = z_retract_mm + tolerance - calibrated_height = 50 - gage_block_settings = {'X': center_x, - 'Y': center_y, - 'Z': calibrated_height, - 'offset': z_offset_from_center} - - z_measured_gage = await z_calibrate_gage_block(hw_api, mount, - OT3Axis.by_mount(mount), - cp, z_probe_settings, gage_block_settings) - + # await hw_api.move_to( + # mount, + # Point( + # nominal_center.x, + # nominal_center.y, + # home_position[OT3Axis.by_mount(mount)], + # ), + # ) + # cp = CriticalPoint.NOZZLE + # print("Move to left edge of the gage block.") + # current_position = await hw_api.current_position_ot3(mount) + # x_gage_block_mm = 9.2 + # nozzle_nominal_diameter = 5.15 + # tolerance = 2 + # x_offset_from_center = x_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance + # gage_block_settings = {'X': nominal_center.x, + # 'Y': nominal_center.y, + # 'Z': nominal_center.z, + # 'offset': x_offset_from_center} + # # Find the X edges of the gage block + # x_minus, x_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.X, cp, xy_nozzle_settings, gage_block_settings) + # x_measured_gage = (x_plus - nozzle_nominal_diameter/2) - (x_minus + nozzle_nominal_diameter/2) + # print("X-Measured Gage: ", x_measured_gage) + # center_x = (x_plus - nozzle_nominal_diameter/2) - x_measured_gage/2 + # current_position = await hw_api.current_position_ot3(mount) + # await hw_api.move_to(mount, + # Point( center_x, + # current_position[OT3Axis.Y], + # current_position[OT3Axis.by_mount(mount)])) + # tolerance = 2 + # y_gage_block_mm = 3.32 + # y_offset_from_center = y_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance + # gage_block_settings = {'X': nominal_center.x, + # 'Y': nominal_center.y, + # 'Z': nominal_center.z, + # 'offset': y_offset_from_center} + # # find the y edges of the gage block + # y_minus, y_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.Y, cp, xy_nozzle_settings, gage_block_settings) + # y_measured_gage = (y_plus - nozzle_nominal_diameter/2) - (y_minus + nozzle_nominal_diameter/2) + # print("Y-Measured Gage: ", y_measured_gage) + # current_position = await hw_api.current_position_ot3(mount) + # await hw_api.move_to(mount, + # Point( current_position[OT3Axis.X], + # current_position[OT3Axis.Y], + # current_position[OT3Axis.by_mount(mount)] + z_retract_mm)) + # center_x = (x_plus - nozzle_nominal_diameter/2) - x_measured_gage/2 + # center_y = (y_plus - nozzle_nominal_diameter/2) - y_measured_gage/2 + # await hw_api.move_to(mount, + # Point( center_x, + # center_y, + # current_position[OT3Axis.by_mount(mount)] + z_retract_mm)) + # tolerance = 2 + # z_offset_from_center = z_retract_mm + tolerance + # calibrated_height = 50 + # gage_block_settings = {'X': center_x, + # 'Y': center_y, + # 'Z': calibrated_height, + # 'offset': z_offset_from_center} + # + # z_measured_gage = await z_calibrate_gage_block(hw_api, mount, + # OT3Axis.by_mount(mount), + # cp, z_nozzle_settings, gage_block_settings) + # print("Z measured gage: ", z_measured_gage) + # await hw_api.home_z(mount) + # input("Insert the Probe Attachment") + # # ------------------------------Probe attachment----------------------- + # x_gage_block_mm = 9.2 + # nozzle_nominal_diameter = 4.0 + # tolerance = 3 + # x_offset_from_center = x_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance + # gage_block_settings = {'X': nominal_center.x, + # 'Y': nominal_center.y, + # 'Z': nominal_center.z, + # 'offset': x_offset_from_center} + # # Find the X edges of the gage block + # x_minus, x_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.X, cp, xy_probe_settings, gage_block_settings) + # probe_x_measured_gage = (x_plus - nozzle_nominal_diameter/2) - (x_minus + nozzle_nominal_diameter/2) + # print("X-Measured Gage probe: ", probe_x_measured_gage) + # probe_x_center = (x_plus - nozzle_nominal_diameter/2) - probe_x_measured_gage/2 + # current_position = await hw_api.current_position_ot3(mount) + # await hw_api.move_to(mount, + # Point( probe_x_center, + # current_position[OT3Axis.Y], + # current_position[OT3Axis.by_mount(mount)])) + # await hw_api.move_to(mount, + # Point( probe_x_center, + # current_position[OT3Axis.Y] - 10, + # current_position[OT3Axis.by_mount(mount)])) + # tolerance = 2 + # y_gage_block_mm = 3.32 + # y_offset_from_center = y_gage_block_mm/2 + nozzle_nominal_diameter/2 + tolerance + # gage_block_settings = {'X': nominal_center.x, + # 'Y': nominal_center.y, + # 'Z': nominal_center.z, + # 'offset': y_offset_from_center} + # # find the y edges of the gage block + # y_minus, y_plus = await xy_calibrate_gage_block(hw_api, mount, OT3Axis.Y, cp, xy_probe_settings, gage_block_settings) + # probe_y_measured_gage = (y_plus - nozzle_nominal_diameter/2) - (y_minus + nozzle_nominal_diameter/2) + # print("Y-Measured Gage probe: ", probe_y_measured_gage) + # probe_y_center = (y_plus - nozzle_nominal_diameter/2) - probe_y_measured_gage/2 + # current_position = await hw_api.current_position_ot3(mount) + # await hw_api.move_to(mount, + # Point( current_position[OT3Axis.X], + # current_position[OT3Axis.Y], + # current_position[OT3Axis.by_mount(mount)] + z_retract_mm)) + # center_x = (x_plus - nozzle_nominal_diameter/2) - x_measured_gage/2 + # center_y = (y_plus - nozzle_nominal_diameter/2) - y_measured_gage/2 + # await hw_api.move_to(mount, + # Point( center_x, + # center_y, + # current_position[OT3Axis.by_mount(mount)] + z_retract_mm)) + # tolerance = 2 + # z_offset_from_center = z_retract_mm + tolerance + # calibrated_height = 50 + # gage_block_settings = {'X': center_x, + # 'Y': center_y, + # 'Z': calibrated_height, + # 'offset': z_offset_from_center} + # + # z_measured_gage = await z_calibrate_gage_block(hw_api, mount, + # OT3Axis.by_mount(mount), + # cp, z_probe_settings, gage_block_settings) + # print("Z-Measured Gage", z_measured_gage) # --------------------------Deck Slot Calibration ------------------------- - input("Press Enter to find the deck slot center") expected = get_calibration_square_position_in_slot(slot=5) # or any other slot + print(expected) + expected = Point(expected.x, expected.y, expected.z + 44.5) found_z= await find_calibration_structure_height(hw_api, mount, expected) found_xyz = await find_slot_center_binary(hw_api, mount, expected._replace(z=found_z)) + print(found_xyz) + await hw_api.home_z(mount) + input("remove probe attachment") + if args.tiprack: + await hw_api.move_to( + mount, + Point( + slot_loc["B1"][0], + slot_loc["B1"][1], + home_position[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.NOZZLE + print("Move to Tiprack") + current_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await jog(hw_api, current_position, cp) + # Start recording the encoder + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + print(f"Start encoder: {init_tip_loc}") + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + # await update_pick_up_current(hw_api, mount, m_current) + await update_pickup_tip_speed(hw_api, mount, pick_up_speed) + # pick up tip + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + await home_ot3(hw_api, [OT3Axis.by_mount(mount)]) + home_with_tip_position = await hw_api.current_position_ot3(mount, critical_point= CriticalPoint.TIP) + home_with_tip_position = home_with_tip_position[OT3Axis.by_mount(mount)] + await hw_api.move_to( + mount, + Point( + current_position[OT3Axis.X], + current_position[OT3Axis.Y], + home_with_tip_position, + ), + critical_point=CriticalPoint.TIP, + ) + + encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] + final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + print(f"End Encoder: {final_tip_loc}") + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + location = "Tiprack" + tip_count = 1 + # test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + tiprack_loc = [ + tiprack_loc[OT3Axis.X], + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)], + ] + cp = CriticalPoint.TIP + current_position = await hw_api.current_position_ot3(mount, cp) + hole_location = plate_definition(16) + plate_tolerance = 1 + plate_thickness = 3.0 + plate_tolerance + dimensions_plate = Point(127.75, 85.5, tip_length[args.tip_size] + plate_thickness) + edge_location = Point(found_xyz.x - dimensions_plate.x/2, + found_xyz.y + dimensions_plate.y/2, + found_xyz.z + dimensions_plate.z) + await hw_api.move_to(mount, Point( + edge_location.x, + edge_location.y, + current_position[OT3Axis.by_mount(mount)] + ), + critical_point = cp) + + await hw_api.move_to(mount, Point(edge_location.x, + edge_location.y, + home_with_tip_position - edge_location.z), + critical_point = cp) + while True: + hole_val = input("Which hole: ") + hole_location = plate_definition(hole_val) + hole = Point(hole_location.x, + hole_location.y, + home_with_tip_position - edge_location.z) + await hw_api.move_to(mount, Point( + edge_location.x + hole.x, + edge_location.y - hole.y, + hole.z)) + current_position = await hw_api.current_position_ot3(mount, cp) + await jog(hw_api, current_position, cp) await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) except KeyboardInterrupt: @@ -580,11 +704,12 @@ async def _main() -> None: parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") parser.add_argument('-o', '--slot', type=int, required=False, help='Deck slot number', default=2) - parser.add_argument("--tip_size", type=str, default="T50", help="Tip Size") + parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") parser.add_argument("--prep_distance_mm", type = float, default = 5, help="Prep Probe Distance") parser.add_argument("--max_overrun_distance_mm", type = float, default = 6, help = "Max Probe Distance") parser.add_argument("--probe_speed", type = int, default = 0.3, help = "Probe Speed") - parser.add_argument("--sensor_threshold_pf", type = float, default = 0.9, help = "capacitive sensor threshold") + parser.add_argument("--probe_threshold_pf", type = float, default = 3.0, help = "capacitive probe threshold") + parser.add_argument("--nozzle_threshold_pf", type = float, default = 0.6, help = "capacitive Nozzle threshold") args = parser.parse_args() if args.mount == "left": From f5dff07be8656cea19eade2ec4a310c7a167e300 Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 10 May 2023 12:40:43 -0400 Subject: [PATCH 21/53] tip length test added --- api/src/opentrons/hardware_control/ot3api.py | 2 + .../hardware_testing/scripts/jog.py | 2 +- .../scripts/pipette_tip_length_test.py | 507 ++++++++++++++++++ 3 files changed, 510 insertions(+), 1 deletion(-) create mode 100644 hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py diff --git a/api/src/opentrons/hardware_control/ot3api.py b/api/src/opentrons/hardware_control/ot3api.py index 607b5b091a1..d3a87f583d0 100644 --- a/api/src/opentrons/hardware_control/ot3api.py +++ b/api/src/opentrons/hardware_control/ot3api.py @@ -1545,6 +1545,7 @@ async def _force_pick_up_tip( ) await asyncio.sleep(1) enc_pos = await self.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + print(enc_pos) await self._move(target_up) return enc_pos @@ -1593,6 +1594,7 @@ async def pick_up_tip( if spec.pick_up_motor_actions: await self._motor_pick_up_tip(realmount, spec.pick_up_motor_actions) + enc_pos = None else: enc_pos = await self._force_pick_up_tip(realmount, spec) diff --git a/hardware-testing/hardware_testing/scripts/jog.py b/hardware-testing/hardware_testing/scripts/jog.py index 8901ea865c2..ceed34afcda 100644 --- a/hardware-testing/hardware_testing/scripts/jog.py +++ b/hardware-testing/hardware_testing/scripts/jog.py @@ -207,7 +207,7 @@ async def _main() -> None: is_simulating=args.simulate, use_defaults=True ) tip_length = {"T1K": 85.7, "T50": 57.9} - pipette_model = hw_api.get_all_attached_instr()[OT3Mount.RIGHT]["pipette_id"] + pipette_model = hw_api.get_all_attached_instr()[OT3Mount.LEFT]["pipette_id"] pipette = _get_pipette_from_mount(hw_api, mount) dial_data = {"Tip": None, "Tip Height": None, "Motor Current": None} m_current = 0.2 diff --git a/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py b/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py new file mode 100644 index 00000000000..4f6ba2f3ae3 --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py @@ -0,0 +1,507 @@ +"""Demo OT3 Gantry Functionality.""" +import argparse +import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os +import sys +import termios +import tty +import json + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + OT3Mount, + OT3Axis, + Point, + CriticalPoint, +) +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + move_plunger_absolute_ot3, + move_plunger_relative_ot3, + get_plunger_positions_ot3, + update_pick_up_current, + update_pick_up_distance, + _get_pipette_from_mount, +) + +from opentrons.config.types import LiquidProbeSettings + +from hardware_testing import data +from hardware_testing.drivers.mark10 import Mark10 +from hardware_testing.drivers import mitutoyo_digimatic_indicator + +aspirate_depth = 7 +dispense_depth = 3 +liquid_retract_dist = 12 +liquid_retract_speed = 5 +retract_dist = 100 +retract_speed = 60 + +leak_test_time = 30 + + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + + +def file_setup(test_data, details): + today = datetime.date.today() + test_name = "{}-pick_up-up-test-{}Amps".format( + details[0], # Pipette model + details[1], # Motor Current + ) + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + + +def dial_indicator_setup(): + gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator( + port="/dev/ttyUSB0" + ) + gauge.connect() + return gauge + + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[OT3Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") + return position + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + +async def move_to_point(api, mount, point, cp): + home_pos = api.get_instrument_max_height(mount, cp) + pos = await api.current_position_ot3(mount, refresh=True, critical_point = cp) + await api.move_to(mount, + Point(pos[OT3Axis.X], + pos[OT3Axis.Y], + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + point.z)) + + +async def countdown(count_time: float): + """ + This function loops through a countdown before checking the leak visually + """ + time_suspend = 0 + while time_suspend < count_time: + await asyncio.sleep(1) + time_suspend += 1 + print(f"Remaining: {count_time-time_suspend} (s)", end="") + print("\r", end="") + print("") + + +async def update_pickup_tip_speed(api, mount, speed) -> None: + """Update drop-tip current.""" + pipette = _get_pipette_from_mount(api, mount) + config_model = pipette.pick_up_configurations + config_model.speed = speed + pipette.pick_up_configurations = config_model + print(pipette.pick_up_configurations) + + +async def _main() -> None: + today = datetime.date.today() + tips_to_use = 8 + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.92, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + tip_length = {"T1K": 95.7, "T200": 58.35, "T50": 57.9} + pipette_model = hw_api._pipette_handler.hardware_instruments[mount].name + dial_data = {"Tip": None, "Tip Height": None, "tip_length": None, "Nozzle Height": None,} + m_current = float(input("motor_current in amps: ")) + pick_up_speed = float(input("pick up tip speed in mm/s: ")) + details = [pipette_model, m_current] + test_n, test_f = file_setup(dial_data, details) + file_name = "/home/root/.opentrons/testing_data/data/tip_length_test_%s-%s.csv" % ( + m_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + ) + print(file_name) + print(test_n) + print(test_f) + await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + home_position = await hw_api.current_position_ot3(mount) + start_time = time.time() + nozzle_home_pos = hw_api.get_instrument_max_height(mount, CriticalPoint.NOZZLE) + tip_home_pos = hw_api.get_instrument_max_height(mount, CriticalPoint.TIP) + + if args.dial_indicator: + cp = CriticalPoint.NOZZLE + dial_point = Point(slot_loc["D2"][0], + slot_loc["D2"][1], + nozzle_home_pos - 100) + await move_to_point(hw_api, mount, dial_point, cp) + print("Moved to Dial Indicator") + current_position = await hw_api.current_position_ot3(mount) + nozzle_dial_loc = await jog(hw_api, current_position, cp) + await asyncio.sleep(1) + nozzle_measurement = gauge.read() + z_enc = await hw_api.encoder_current_position_ot3(mount = mount, critical_point = cp) + await asyncio.sleep(2) + d_str = f"{m_current}, {pipette_model}, {nozzle_measurement}, {z_enc}, Nozzle \n" + data.append_data_to_file(test_n, test_f, d_str) + nozzle_dial_point = Point(nozzle_dial_loc[OT3Axis.X], + nozzle_dial_loc[OT3Axis.Y], + nozzle_dial_loc[OT3Axis.by_mount(mount)]) + + if args.tiprack: + cp = CriticalPoint.NOZZLE + tiprack_point = Point(slot_loc["C2"][0], + slot_loc["C2"][1], + nozzle_home_pos-100) + await move_to_point(hw_api, mount, tiprack_point, cp) + print("Move to Tiprack") + current_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await jog(hw_api, current_position, cp) + # Start recording the encoder + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + await update_pick_up_current(hw_api, mount, m_current) + await update_pickup_tip_speed(hw_api, mount, pick_up_speed) + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + print(f"Nozzle Pos: {init_tip_loc}") + print(f"Press Pos: {final_tip_loc}") + location = "Tiprack" + tip_count = 1 + test_details = [ + start_time, + m_current, + location, + init_tip_loc, + final_tip_loc, + tip_count, + ] + enc_record(file_name, test_details) + tiprack_loc = Point( + tiprack_loc[OT3Axis.X], + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)]) + + if args.dial_indicator: + cp = CriticalPoint.TIP + dial_point = Point(slot_loc["D2"][0], + slot_loc["D2"][1], + tip_home_pos - tip_length[args.tip_size]) + await move_to_point(hw_api, mount, dial_point, cp) + print("Moved to Dial Indicator") + current_position = await hw_api.current_position_ot3(mount) + dial_loc = await jog(hw_api, current_position, cp) + await asyncio.sleep(1) + tip_measurement = gauge.read() + await asyncio.sleep(2) + d_str = f"{m_current}, {pipette_model}, {tip_measurement}, Tip \n" + data.append_data_to_file(test_n, test_f, d_str) + dial_point = Point(dial_loc[OT3Axis.X], + dial_loc[OT3Axis.Y], + dial_loc[OT3Axis.by_mount(mount)]) + + # Move to trash slot + cp = CriticalPoint.TIP + trash = Point(slot_loc["A3"][0]+50, slot_loc["A3"][1]-20, tip_home_pos - 150) + await move_to_point(hw_api, mount, trash, cp) + await hw_api.drop_tip(mount) + + tip_count = 0 + x_offset = 0 + y_offset = 0 + try: + + for tip in range(2, tips_to_use + 1): + tip_count += 1 + y_offset -= 9 + if tip_count % 8 == 0: + y_offset = 0 + if tip_count % 8 == 0: + x_offset += 9 + # -----------------------Tiprack------------------------------------ + # # Move over to the TipRack location and + tiprack_loc = Point(tiprack_loc.x + x_offset, tiprack_loc.y + y_offset, tiprack_loc.z) + await move_to_point(hw_api, mount, tiprack_loc, cp) + location = "Tiprack" + + # Start recording the encoder + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc + # Press Pipette into the tip + await update_pick_up_current(hw_api, mount, m_current) + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + print(f"Start encoder: {init_tip_loc}") + print(f"End Encoder: {final_tip_loc}") + elasped_time = (time.time() - start_time) / 60 + test_details = [ + elasped_time, + m_current, + location, + init_tip_loc, + final_tip_loc, + tip_count, + ] + enc_record(file_name, test_details) + await move_to_point(hw_api, mount, dial_point, cp) + await asyncio.sleep(3) + tip_measurement = gauge.read() + d_str = f"{m_current}, {pipette_model}, {tip_measurement}, Tip \n" + print(f"{d_str}") + data.append_data_to_file(test_n, test_f, d_str) + await asyncio.sleep(1) + # --------------------Drop Tip-------------------------------------- + current_position = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + await move_to_point(hw_api, mount, trash, cp) + await hw_api.drop_tip(mount) + + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + +def enc_record(f_name, t_data): + # test_details = [start_time, m_current, location, init_tip_loc, final_tip_loc, tip_count] + with open(f_name, "a", newline="") as f: + test_data = { + "time(s)": None, + "motor_current": None, + "location": None, + "start_enc_pos(mm)": None, + "end_enc_pos(mm)": None, + } + log_file = csv.DictWriter(f, test_data) + if t_data[5] < 1: + log_file.writeheader() + try: + test_data["time(s)"] = time.perf_counter() - t_data[0] + test_data["motor_current"] = t_data[1] + test_data["location"] = t_data[2] + test_data["start_enc_pos(mm)"] = t_data[3] + test_data["end_enc_pos(mm)"] = t_data[4] + log_file.writerow(test_data) + print(test_data) + f.flush() + except KeyboardInterrupt: + print("Test Cancelled") + test_data["Errors"] = "Test Cancelled" + f.flush() + except Exception as e: + print("ERROR OCCURED") + test_data["Errors"] = e + f.flush() + raise e + print("Test done") + f.flush() + f.close() + + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--fg_jog", action="store_true") + parser.add_argument("--trough", action="store_true") + parser.add_argument("--tiprack", action="store_true") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C1") + parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") + parser.add_argument("--dial_indicator", action="store_true") + parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") + parser.add_argument( + "--port", type=str, default="/dev/ttyUSB0", help="Force Gauge Port" + ) + args = parser.parse_args() + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + + if args.dial_indicator: + gauge = dial_indicator_setup() + asyncio.run(_main()) From 9f5ed7508d41498559b6f06713464a01a193d990 Mon Sep 17 00:00:00 2001 From: Carlos Date: Fri, 12 May 2023 09:19:40 -0400 Subject: [PATCH 22/53] working test script for tip length --- .../scripts/pipette_tip_length_test.py | 104 +++++++++++------- 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py b/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py index 4f6ba2f3ae3..21215a1d34d 100644 --- a/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py +++ b/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py @@ -53,7 +53,7 @@ def dict_keys_to_line(dict): def file_setup(test_data, details): today = datetime.date.today() - test_name = "{}-pick_up-up-test-{}Amps".format( + test_name = "{}-tip_length-test-{}Amps".format( details[0], # Pipette model details[1], # Motor Current ) @@ -240,7 +240,7 @@ async def update_pickup_tip_speed(api, mount, speed) -> None: async def _main() -> None: today = datetime.date.today() - tips_to_use = 8 + tips_to_use = 96 slot_loc = { "A1": (13.42, 394.92, 110), "A2": (177.32, 394.92, 110), @@ -260,7 +260,12 @@ async def _main() -> None: ) tip_length = {"T1K": 95.7, "T200": 58.35, "T50": 57.9} pipette_model = hw_api._pipette_handler.hardware_instruments[mount].name - dial_data = {"Tip": None, "Tip Height": None, "tip_length": None, "Nozzle Height": None,} + dial_data = {"M_current(Amps)": None, + "Pipette Model": None, + "Tip_Height(mm)": None, + "Nozzle_Height": None, + "Tip_Overlap(mm)": None} + # d_str = f"{m_current}, {pipette_model}, {tip_measurement}, {tip_loc}, {noz_loc}, {tip_overlap}, Tip \n" m_current = float(input("motor_current in amps: ")) pick_up_speed = float(input("pick up tip speed in mm/s: ")) details = [pipette_model, m_current] @@ -273,6 +278,7 @@ async def _main() -> None: print(test_n) print(test_f) await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) + await hw_api.home_plunger(mount) plunger_pos = get_plunger_positions_ot3(hw_api, mount) home_position = await hw_api.current_position_ot3(mount) start_time = time.time() @@ -290,9 +296,10 @@ async def _main() -> None: nozzle_dial_loc = await jog(hw_api, current_position, cp) await asyncio.sleep(1) nozzle_measurement = gauge.read() - z_enc = await hw_api.encoder_current_position_ot3(mount = mount, critical_point = cp) + noz_loc = await hw_api.encoder_current_position_ot3(mount = mount, critical_point = cp) + noz_loc = noz_loc[OT3Axis.by_mount(mount)] await asyncio.sleep(2) - d_str = f"{m_current}, {pipette_model}, {nozzle_measurement}, {z_enc}, Nozzle \n" + d_str = f"{m_current}, {pipette_model}, {nozzle_measurement}, {noz_loc}, Nozzle \n" data.append_data_to_file(test_n, test_f, d_str) nozzle_dial_point = Point(nozzle_dial_loc[OT3Axis.X], nozzle_dial_loc[OT3Axis.Y], @@ -307,33 +314,29 @@ async def _main() -> None: print("Move to Tiprack") current_position = await hw_api.current_position_ot3(mount) tiprack_loc = await jog(hw_api, current_position, cp) - # Start recording the encoder - init_tip_loc = await hw_api.encoder_current_position_ot3( - mount, CriticalPoint.NOZZLE - ) - init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] - encoder_position = init_tip_loc - init_tip_loc = await hw_api.encoder_current_position_ot3( + init_noz_loc = await hw_api.encoder_current_position_ot3( mount, CriticalPoint.NOZZLE ) + init_noz_loc = init_noz_loc[OT3Axis.by_mount(mount)] await update_pick_up_current(hw_api, mount, m_current) await update_pickup_tip_speed(hw_api, mount, pick_up_speed) # Move pipette to Force Gauge press location - final_tip_loc = await hw_api.pick_up_tip( + final_noz_loc = await hw_api.pick_up_tip( mount, tip_length=tip_length[args.tip_size] ) - init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] - final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] - print(f"Nozzle Pos: {init_tip_loc}") - print(f"Press Pos: {final_tip_loc}") + final_noz_loc = final_noz_loc[OT3Axis.by_mount(mount)] + print(f"Nozzle Pos: {init_noz_loc}") + print(f"Press Pos: {final_noz_loc}") + tip_overlap = init_noz_loc - final_noz_loc + print(f"tip_overlap: {tip_overlap}") location = "Tiprack" tip_count = 1 test_details = [ start_time, m_current, location, - init_tip_loc, - final_tip_loc, + init_noz_loc, + final_noz_loc, tip_count, ] enc_record(file_name, test_details) @@ -344,85 +347,108 @@ async def _main() -> None: if args.dial_indicator: cp = CriticalPoint.TIP + tip_count = 1 dial_point = Point(slot_loc["D2"][0], slot_loc["D2"][1], tip_home_pos - tip_length[args.tip_size]) await move_to_point(hw_api, mount, dial_point, cp) print("Moved to Dial Indicator") - current_position = await hw_api.current_position_ot3(mount) + current_position = await hw_api.current_position_ot3(mount, cp) dial_loc = await jog(hw_api, current_position, cp) await asyncio.sleep(1) tip_measurement = gauge.read() await asyncio.sleep(2) - d_str = f"{m_current}, {pipette_model}, {tip_measurement}, Tip \n" + tip_loc = await hw_api.encoder_current_position_ot3(mount = mount, critical_point = CriticalPoint.NOZZLE) + tip_loc = tip_loc[OT3Axis.by_mount(mount)] + measured_tip_length = (tip_loc - noz_loc) + d_str = f"{m_current}, {pipette_model}, {tip_measurement}, {tip_loc}, {tip_overlap}, {tip_count}, Tip \n" data.append_data_to_file(test_n, test_f, d_str) dial_point = Point(dial_loc[OT3Axis.X], dial_loc[OT3Axis.Y], dial_loc[OT3Axis.by_mount(mount)]) + print(f"Tip Length: {measured_tip_length}") # Move to trash slot cp = CriticalPoint.TIP trash = Point(slot_loc["A3"][0]+50, slot_loc["A3"][1]-20, tip_home_pos - 150) await move_to_point(hw_api, mount, trash, cp) await hw_api.drop_tip(mount) - tip_count = 0 + tip_count = 1 x_offset = 0 y_offset = 0 try: for tip in range(2, tips_to_use + 1): tip_count += 1 - y_offset -= 9 - if tip_count % 8 == 0: - y_offset = 0 - if tip_count % 8 == 0: - x_offset += 9 + # y_offset -= 9 + # if tip_count % 8 == 0: + # y_offset = 0 + # if tip_count % 8 == 0: + # x_offset += 9 # -----------------------Tiprack------------------------------------ + # Move the Nozzle to the Dial Indicator + cp = CriticalPoint.NOZZLE + await move_to_point(hw_api, mount, nozzle_dial_point, cp) + noz_loc = await hw_api.encoder_current_position_ot3(mount, cp) + noz_loc = noz_loc[OT3Axis.by_mount(mount)] + await asyncio.sleep(3) + nozzle_measurement = gauge.read() + await asyncio.sleep(1) + d_str = f"{m_current}, {pipette_model}, {nozzle_measurement}, {noz_loc}, Nozzle \n" + data.append_data_to_file(test_n, test_f, d_str) # # Move over to the TipRack location and - tiprack_loc = Point(tiprack_loc.x + x_offset, tiprack_loc.y + y_offset, tiprack_loc.z) + tiprack_loc = Point(tiprack_loc.x, tiprack_loc.y, tiprack_loc.z) await move_to_point(hw_api, mount, tiprack_loc, cp) location = "Tiprack" # Start recording the encoder - init_tip_loc = await hw_api.encoder_current_position_ot3( + init_noz_loc = await hw_api.encoder_current_position_ot3( mount, CriticalPoint.NOZZLE ) - init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] - encoder_position = init_tip_loc + init_noz_loc = init_noz_loc[OT3Axis.by_mount(mount)] # Press Pipette into the tip await update_pick_up_current(hw_api, mount, m_current) # Move pipette to Force Gauge press location - final_tip_loc = await hw_api.pick_up_tip( + final_noz_loc = await hw_api.pick_up_tip( mount, tip_length=tip_length[args.tip_size] ) - final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] - print(f"Start encoder: {init_tip_loc}") - print(f"End Encoder: {final_tip_loc}") + final_noz_loc = final_noz_loc[OT3Axis.by_mount(mount)] + print(f"Start encoder: {init_noz_loc}") + print(f"End Encoder: {final_noz_loc}") + tip_overlap = init_noz_loc - final_noz_loc + print(f"tip_overlap: {tip_overlap}") elasped_time = (time.time() - start_time) / 60 test_details = [ elasped_time, m_current, location, - init_tip_loc, - final_tip_loc, + init_noz_loc, + final_noz_loc, tip_count, ] enc_record(file_name, test_details) + cp = CriticalPoint.TIP await move_to_point(hw_api, mount, dial_point, cp) await asyncio.sleep(3) tip_measurement = gauge.read() - d_str = f"{m_current}, {pipette_model}, {tip_measurement}, Tip \n" + await asyncio.sleep(1) + tip_loc = await hw_api.encoder_current_position_ot3(mount = mount, critical_point = CriticalPoint.NOZZLE) + tip_loc = tip_loc[OT3Axis.by_mount(mount)] + measured_tip_length = (tip_loc - noz_loc) + d_str = f"{m_current}, {pipette_model}, {tip_measurement}, {tip_loc}, {tip_overlap}, {tip_count}, Tip \n" print(f"{d_str}") data.append_data_to_file(test_n, test_f, d_str) await asyncio.sleep(1) + print(f"Tip Length: {measured_tip_length}") # --------------------Drop Tip-------------------------------------- current_position = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) await move_to_point(hw_api, mount, trash, cp) await hw_api.drop_tip(mount) + input("Press Enter to Continue") await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) except KeyboardInterrupt: @@ -492,7 +518,7 @@ def enc_record(f_name, t_data): parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C1") parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") parser.add_argument("--dial_indicator", action="store_true") - parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") + parser.add_argument("--tip_size", type=str, default="T200", help="Tip Size") parser.add_argument( "--port", type=str, default="/dev/ttyUSB0", help="Force Gauge Port" ) From 7720df49169679a105212ae941ea31b8cd242a32 Mon Sep 17 00:00:00 2001 From: Carlos Date: Fri, 12 May 2023 13:55:44 -0400 Subject: [PATCH 23/53] small change --- .../hardware_testing/scripts/pipette_tip_length_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py b/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py index 21215a1d34d..ca892571f22 100644 --- a/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py +++ b/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py @@ -395,7 +395,7 @@ async def _main() -> None: await asyncio.sleep(3) nozzle_measurement = gauge.read() await asyncio.sleep(1) - d_str = f"{m_current}, {pipette_model}, {nozzle_measurement}, {noz_loc}, Nozzle \n" + d_str = f"{m_current}, {pipette_model}, {nozzle_measurement}, {noz_loc},{None},{tip_count}, Nozzle \n" data.append_data_to_file(test_n, test_f, d_str) # # Move over to the TipRack location and tiprack_loc = Point(tiprack_loc.x, tiprack_loc.y, tiprack_loc.z) @@ -518,7 +518,7 @@ def enc_record(f_name, t_data): parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C1") parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") parser.add_argument("--dial_indicator", action="store_true") - parser.add_argument("--tip_size", type=str, default="T200", help="Tip Size") + parser.add_argument("--tip_size", type=str, default="T50", help="Tip Size") parser.add_argument( "--port", type=str, default="/dev/ttyUSB0", help="Force Gauge Port" ) From f5877a4fd4172b0f8b613c6a81eeb92f00f87637 Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Fri, 26 May 2023 10:40:14 -0400 Subject: [PATCH 24/53] new changes --- .../opentrons_api/helpers_ot3.py | 8 + .../scripts/96_channel_pick_up_tip.py | 763 ++++++++++++++++++ 2 files changed, 771 insertions(+) create mode 100644 hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py diff --git a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py index ef89807a421..68210366b4d 100644 --- a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py +++ b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py @@ -381,6 +381,14 @@ async def update_pick_up_current( config_model.current = current pipette.pick_up_configurations = config_model +async def update_96_pick_up_current( + api: OT3API, mount: OT3Mount, current: float = 0.125 +) -> None: + """Update pick-up-tip current.""" + pipette = _get_pipette_from_mount(api, mount) + config_model = pipette.pick_up_configurations + config_model.pick_up_motor_actions = current + pipette.pick_up_configurations = config_model async def update_drop_tip_current( api: OT3API, mount: OT3Mount, current: float = 1.0 diff --git a/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py new file mode 100644 index 00000000000..1502b72b76e --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py @@ -0,0 +1,763 @@ +"""Demo OT3 Gantry Functionality.""" +import argparse +import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os +import sys +import termios +import tty +import json + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + OT3Mount, + OT3Axis, + Point, + CriticalPoint, +) +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + move_plunger_absolute_ot3, + move_plunger_relative_ot3, + get_plunger_positions_ot3, + update_pick_up_current, + update_pick_up_distance, + _get_pipette_from_mount, +) + +from opentrons.config.types import LiquidProbeSettings + +from hardware_testing import data +from hardware_testing.drivers.mark10 import Mark10 +from hardware_testing.drivers import mitutoyo_digimatic_indicator + +aspirate_depth = 7 +dispense_depth = 3 +liquid_retract_dist = 12 +liquid_retract_speed = 5 +retract_dist = 100 +retract_speed = 60 + +leak_test_time = 30 + + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + + +def file_setup(test_data, details): + today = datetime.date.today() + test_name = "{}-pick_up-up-test-{}Amps".format( + details[0], # Pipette model + details[1], # Motor Current + ) + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + + +def dial_indicator_setup(port): + gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator(port=port) + gauge.connect() + return gauge + + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[OT3Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") + return position + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + + +async def countdown(count_time: float): + """ + This function loops through a countdown before checking the leak visually + """ + time_suspend = 0 + while time_suspend < count_time: + await asyncio.sleep(1) + time_suspend += 1 + print(f"Remaining: {count_time-time_suspend} (s)", end="") + print("\r", end="") + print("") + + +async def update_pickup_tip_speed(api, mount, speed) -> None: + """Update drop-tip current.""" + pipette = _get_pipette_from_mount(api, mount) + config_model = pipette.pick_up_configurations + config_model.speed = speed + pipette.pick_up_configurations = config_model + print(pipette.pick_up_configurations) + + +async def _main() -> None: + today = datetime.date.today() + tips_to_use = 12 + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.0, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + tip_length = {"T1K": 85.7, "T200": 48.35, "T50": 47.9} + pipette_model = hw_api.get_all_attached_instr()[OT3Mount.RIGHT]["pipette_id"] + dial_data = { + "Ch1": None, + "Ch2": None, + "Ch3": None, + "Ch4": None, + "Ch5": None, + "Ch6": None, + "Ch7": None, + "Ch8": None, + "Motor Current": None, + "Trial": None, + "pipette_model": None, + } + m_current = float(input("motor_current in amps: ")) + pick_up_speed = float(input("pick up tip speed in mm/s: ")) + details = [pipette_model, m_current] + test_n, test_f = file_setup(dial_data, details) + file_name = "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s.csv" % ( + m_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + ) + print(file_name) + print(test_n) + print(test_f) + await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) + await hw_api.home_plunger(mount) + await hw_api.set_lights(rails=True) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + home_position = await hw_api.current_position_ot3(mount) + start_time = time.perf_counter() + if args.tip_size == "T1K": + home_with_tip_position = 164.3 # T1K + if args.tip_size == "T200": + home_with_tip_position = 201.64999999999998 # T1K + elif args.tip_size == "T50": + home_with_tip_position = 192.1 # T50 + + # if args.fg_jog: + # cp = CriticalPoint.NOZZLE + # await hw_api.move_to( + # mount, + # Point( + # slot_loc["D2"][0], + # slot_loc["D2"][1], + # home_position[OT3Axis.by_mount(mount)], + # ), + # ) + # current_position = await hw_api.current_position_ot3(mount) + # print("Move to Force Gauge") + # fg_loc = await jog(hw_api, current_position, cp) + # fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(mount)]] + # await hw_api.move_to( + # mount, + # Point(fg_loc[0], fg_loc[1], home_position[OT3Axis.by_mount(mount)]), + # critical_point=CriticalPoint.TIP, + # ) + # await hw_api.home_z(mount, allow_home_other=False) + + if args.tiprack: + await hw_api.move_to( + mount, + Point( + slot_loc["B2"][0], + slot_loc["B2"][1], + home_position[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.NOZZLE + print("Move to Tiprack") + current_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await jog(hw_api, current_position, cp) + # Start recording the encoder + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + print(f"Start encoder: {init_tip_loc}") + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + await update_pick_up_current(hw_api, mount, m_current) + await update_pickup_tip_speed(hw_api, mount, pick_up_speed) + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + await home_ot3(hw_api, [OT3Axis.by_mount(mount)]) + home_with_tip_position = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + await hw_api.move_to( + mount, + Point( + current_position[OT3Axis.X], + current_position[OT3Axis.Y], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + + encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] + # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + print(f"End Encoder: {final_tip_loc}") + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + location = "Tiprack" + tip_count = 1 + test_details = [ + start_time, + m_current, + location, + init_tip_loc, + final_tip_loc, + tip_count, + ] + enc_record(file_name, test_details) + tiprack_loc = [ + tiprack_loc[OT3Axis.X], + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)], + ] + + if args.dial_indicator: + await hw_api.move_to( + mount, + Point( + slot_loc["C2"][0], + slot_loc["C2"][1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.TIP + print("Move to Tiprack") + current_position = await hw_api.current_position_ot3(mount) + dial_loc = await jog(hw_api, current_position, cp) + dial_loc = [ + dial_loc[OT3Axis.X], + dial_loc[OT3Axis.Y], + dial_loc[OT3Axis.by_mount(mount)], + ] + tip_offset = 0 + measurements = "" + tips = 8 + trial = 1 + for tip in range(1, tips + 1): + + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1] + tip_offset, + dial_loc[2], + ), + ) + await asyncio.sleep(1) + tip_measurement = gauge.read() + await asyncio.sleep(2) + measurements += str(tip_measurement) + "," + + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1] + tip_offset, + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + tip_offset += 9 + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1] + tip_offset, + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" + data.append_data_to_file(test_n, test_f, d_str) + + if args.trough: + await hw_api.move_to( + mount, + Point( + slot_loc["B3"][0], + slot_loc["B3"][1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + cp = CriticalPoint.TIP + print("Move to Trough") + current_position = await hw_api.current_position_ot3(mount) + trough_loc = await jog(hw_api, current_position, cp) + trough_loc = [ + trough_loc[OT3Axis.X], + trough_loc[OT3Axis.Y], + trough_loc[OT3Axis.by_mount(mount)], + ] + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + input("Feel the Tip!") + await hw_api.drop_tip(mount) + + lp_file_name = "/var/pressure_sensor_data_P-{}_Z-{}-{}.csv".format( + args.plunger_speed, args.mount_speed, today.strftime("%b-%d-%Y") + ) + liquid_probe_settings = LiquidProbeSettings( + max_z_distance=args.max_z_distance, + min_z_distance=args.min_z_distance, + mount_speed=args.mount_speed, + plunger_speed=args.plunger_speed, + sensor_threshold_pascals=args.sensor_threshold, + expected_liquid_height=args.expected_liquid_height, + log_pressure=args.log_pressure, + aspirate_while_sensing=False, + auto_zero_sensor=False, + num_baseline_reads=10, + data_file=lp_file_name, + ) + tip_count = 0 + x_offset = 0 + y_offset = 0 + try: + + while True: + # -----------------------Tiprack------------------------------------ + m_current = float(input("motor_current in amps: ")) + pick_up_speed = float(input("pick up tip speed in mm/s: ")) + # Move over to the TipRack location and + await hw_api.move_to( + mount, + Point( + tiprack_loc[0] + x_offset, + tiprack_loc[1] + y_offset, + home_position[OT3Axis.by_mount(mount)], + ), + ) + + # Move Pipette to top of Tip Rack Location + await hw_api.move_to( + mount, + Point( + tiprack_loc[0] + x_offset, + tiprack_loc[1] + y_offset, + tiprack_loc[2], + ), + speed=65, + ) + location = "Tiprack" + + # Start recording the encoder + init_tip_loc = await hw_api.encoder_current_position_ot3( + mount, CriticalPoint.NOZZLE + ) + print(f"Start encoder: {init_tip_loc}") + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + encoder_position = init_tip_loc + # Press Pipette into the tip + await update_pick_up_current(hw_api, mount, m_current) + # Move pipette to Force Gauge press location + final_tip_loc = await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + await hw_api.move_to( + mount, + Point( + tiprack_loc[0] + x_offset, + tiprack_loc[1] + y_offset, + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + # home_with_tip_position = await hw_api.current_position_ot3( + # mount, critical_point=CriticalPoint.TIP + # ) + encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] + # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + print(f"End Encoder: {final_tip_loc}") + final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + test_details = [ + start_time, + m_current, + location, + init_tip_loc, + final_tip_loc, + tip_count, + ] + enc_record(file_name, test_details) + # Home Z + await hw_api.home([OT3Axis.by_mount(mount)]) + # --------------------------Dial Indicator----------------------- + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + tip_offset = 0 + measurements = "" + tips = 8 + for t in range(1, tips + 1): + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point(dial_loc[0], dial_loc[1] + tip_offset, dial_loc[2]), + ) + await asyncio.sleep(3) + tip_measurement = gauge.read() + measurements += str(tip_measurement) + "," + await asyncio.sleep(1) + # Move over to the dial indicator + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1] + tip_offset, + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + tip_offset += 9 + await hw_api.move_to( + mount, + Point( + dial_loc[0], + dial_loc[1] + tip_offset, + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" + data.append_data_to_file(test_n, test_f, d_str) + # -----------------------Aspirate----------------------------------- + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + ) + # Move to offset from trough + await hw_api.move_to( + mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) + ) + # Move the plunger to the top position + await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[0]) + # Liquid Probe + liquid_height, enc_liquid_height = await hw_api.liquid_probe( + mount, probe_settings=liquid_probe_settings + ) + + liquid_height = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + # await move_plunger_relative_ot3(hw_api, mount, 1.5, None, speed = 2) # P50S + await move_plunger_relative_ot3( + hw_api, mount, 0.25, None, speed=2 + ) # P1KS + await hw_api.move_to( + mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) + ) + # Prepare to aspirate before descending to trough well + await hw_api.prepare_for_aspirate(mount) + # Descend to aspirate depth + await hw_api.move_to( + mount, + Point( + liquid_height[OT3Axis.X], + liquid_height[OT3Axis.Y], + liquid_height[OT3Axis.by_mount(mount)] - aspirate_depth, + ), + speed=5, + critical_point=CriticalPoint.TIP, + ) + # Aspirate + await hw_api.aspirate(mount, volume=50) + cur_pos = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + z_pos = cur_pos[OT3Axis.by_mount(mount)] + # Retract from liquid with retract speed + await hw_api.move_to( + mount, + Point(trough_loc[0], trough_loc[1], z_pos + liquid_retract_dist), + speed=liquid_retract_speed, + critical_point=CriticalPoint.TIP, + ) + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + await countdown(count_time=leak_test_time) + # input("Check to see if the pipette is leaking") + await hw_api.move_to( + mount, + Point(trough_loc[0], trough_loc[1], trough_loc[2]), + critical_point=CriticalPoint.TIP, + ) + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + liquid_height[OT3Axis.by_mount(mount)] - dispense_depth, + ), + speed=5, + critical_point=CriticalPoint.TIP, + ) + await hw_api.dispense(mount) + await hw_api.blow_out(mount) + # --------------------Drop Tip-------------------------------------- + current_position = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + await hw_api.move_to( + mount, + Point( + trough_loc[0], + trough_loc[1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1], + home_with_tip_position[OT3Axis.by_mount(mount)], + ), + critical_point=CriticalPoint.TIP, + ) + await hw_api.drop_tip(mount) + + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--fg_jog", action="store_true") + parser.add_argument("--trough", action="store_true") + parser.add_argument("--tiprack", action="store_true") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="right") + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C1") + parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") + parser.add_argument("--fg", action="store_true") + parser.add_argument("--dial_indicator", action="store_true") + parser.add_argument("--tip_size", type=str, default="T50", help="Tip Size") + parser.add_argument("--max_z_distance", type=float, default=40) + parser.add_argument("--min_z_distance", type=float, default=5) + parser.add_argument("--mount_speed", type=float, default=5) + parser.add_argument("--plunger_speed", type=float, default=11) + parser.add_argument( + "--sensor_threshold", type=float, default=200, help="Threshold in Pascals" + ) + parser.add_argument("--expected_liquid_height", type=int, default=0) + parser.add_argument("--log_pressure", action="store_true") + # parser.add_argument( + # "--fg_port", type=str, default="/dev/ttyUSB0", help="Force Gauge Port" + # ) + parser.add_argument( + "--dial_port", type=str, default="/dev/ttyUSB0", help="Dial indicator Port" + ) + args = parser.parse_args() + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + # if args.fg: + # fg = Mark10.create(port=args.fg_port) + # fg.connect() + + if args.dial_indicator: + gauge = dial_indicator_setup(port=args.dial_port) + asyncio.run(_main()) From e6618b00db3be03b71fa271b57d4845f6ed3d4e5 Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Fri, 26 May 2023 12:45:07 -0400 Subject: [PATCH 25/53] commit --- .../scripts/96_channel_pick_up_tip.py | 572 ++++++++---------- 1 file changed, 264 insertions(+), 308 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py index 1502b72b76e..8493dcd3485 100644 --- a/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py @@ -219,6 +219,21 @@ async def update_pickup_tip_speed(api, mount, speed) -> None: pipette.pick_up_configurations = config_model print(pipette.pick_up_configurations) +async def move_to_point(api, mount, point, cp): + home_pos = api.get_instrument_max_height(mount, cp) + pos = await api.current_position_ot3(mount, refresh=True, critical_point = cp) + await api.move_to(mount, + Point(pos[OT3Axis.X], + pos[OT3Axis.Y], + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + point.z)) async def _main() -> None: today = datetime.date.today() @@ -241,16 +256,24 @@ async def _main() -> None: is_simulating=args.simulate, use_defaults=True ) tip_length = {"T1K": 85.7, "T200": 48.35, "T50": 47.9} - pipette_model = hw_api.get_all_attached_instr()[OT3Mount.RIGHT]["pipette_id"] + pipette_model = hw_api.get_all_attached_instr()[OT3Mount.LEFT]["pipette_id"] dial_data = { - "Ch1": None, - "Ch2": None, - "Ch3": None, - "Ch4": None, - "Ch5": None, - "Ch6": None, - "Ch7": None, - "Ch8": None, + "Ch1": None, "Ch2": None, "Ch3": None, "Ch4": None, "Ch5": None, "Ch6": None, + "Ch7": None, "Ch8": None, "Ch9": None, "Ch10": None, "Ch11": None, "Ch12": None, + "Ch13": None, "Ch14": None, "Ch15": None, "Ch16": None, "Ch17": None, "Ch18": None, + "Ch19": None, "Ch20": None, "Ch21": None, "Ch22": None, "Ch23": None, "Ch24": None, + "Ch25": None, "Ch26": None, "Ch27": None, "Ch28": None, "Ch29": None, "Ch30": None, + "Ch31": None, "Ch32": None, "Ch33": None, "Ch34": None, "Ch35": None, "Ch36": None, + "Ch37": None, "Ch38": None, "Ch39": None, "Ch40": None, "Ch41": None, "Ch42": None, + "Ch43": None, "Ch44": None, "Ch45": None, "Ch46": None, "Ch47": None, "Ch48": None, + "Ch49": None, "Ch50": None, "Ch51": None, "Ch52": None, "Ch53": None, "Ch54": None, + "Ch55": None, "Ch56": None, "Ch57": None, "Ch58": None, "Ch59": None, "Ch60": None, + "Ch61": None, "Ch62": None, "Ch63": None, "Ch64": None, "Ch65": None, "Ch66": None, + "Ch67": None, "Ch68": None, "Ch69": None, "Ch70": None, "Ch71": None, "Ch72": None, + "Ch73": None, "Ch74": None, "Ch75": None, "Ch76": None, "Ch77": None, "Ch78": None, + "Ch79": None, "Ch80": None, "Ch81": None, "Ch82": None, "Ch83": None, "Ch84": None, + "Ch85": None, "Ch86": None, "Ch87": None, "Ch88": None, "Ch89": None, "Ch90": None, + "Ch91": None, "Ch92": None, "Ch93": None, "Ch94": None, "Ch95": None, "Ch96": None, "Motor Current": None, "Trial": None, "pipette_model": None, @@ -272,47 +295,26 @@ async def _main() -> None: plunger_pos = get_plunger_positions_ot3(hw_api, mount) home_position = await hw_api.current_position_ot3(mount) start_time = time.perf_counter() - if args.tip_size == "T1K": - home_with_tip_position = 164.3 # T1K - if args.tip_size == "T200": - home_with_tip_position = 201.64999999999998 # T1K - elif args.tip_size == "T50": - home_with_tip_position = 192.1 # T50 - - # if args.fg_jog: - # cp = CriticalPoint.NOZZLE - # await hw_api.move_to( - # mount, - # Point( - # slot_loc["D2"][0], - # slot_loc["D2"][1], - # home_position[OT3Axis.by_mount(mount)], - # ), - # ) - # current_position = await hw_api.current_position_ot3(mount) - # print("Move to Force Gauge") - # fg_loc = await jog(hw_api, current_position, cp) - # fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(mount)]] - # await hw_api.move_to( - # mount, - # Point(fg_loc[0], fg_loc[1], home_position[OT3Axis.by_mount(mount)]), - # critical_point=CriticalPoint.TIP, - # ) - # await hw_api.home_z(mount, allow_home_other=False) + # if args.tip_size == "T1K": + # home_with_tip_position = 164.3 # T1K + # if args.tip_size == "T200": + # home_with_tip_position = 201.64999999999998 # T1K + # elif args.tip_size == "T50": + # home_with_tip_position = 192.1 # T50 if args.tiprack: - await hw_api.move_to( - mount, - Point( - slot_loc["B2"][0], - slot_loc["B2"][1], - home_position[OT3Axis.by_mount(mount)], - ), - ) cp = CriticalPoint.NOZZLE + tiprack_loc = Point( + slot_loc["B2"][0], + slot_loc["B2"][1], + home_position[OT3Axis.by_mount(mount)]) print("Move to Tiprack") + await move_to_point(hw_api, mount, tiprack_loc, cp) current_position = await hw_api.current_position_ot3(mount) tiprack_loc = await jog(hw_api, current_position, cp) + tiprack_loc = Point(tiprack_loc[OT3Axis.X], + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)]) # Start recording the encoder init_tip_loc = await hw_api.encoder_current_position_ot3( mount, CriticalPoint.NOZZLE @@ -329,97 +331,78 @@ async def _main() -> None: final_tip_loc = await hw_api.pick_up_tip( mount, tip_length=tip_length[args.tip_size] ) + # move_to_point(hw_api, mount, tiprack_loc, cp) await home_ot3(hw_api, [OT3Axis.by_mount(mount)]) home_with_tip_position = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) - await hw_api.move_to( - mount, - Point( - current_position[OT3Axis.X], - current_position[OT3Axis.Y], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - critical_point=CriticalPoint.TIP, - ) - + # await hw_api.move_to( + # mount, + # Point( + # current_position[OT3Axis.X], + # current_position[OT3Axis.Y], + # home_with_tip_position[OT3Axis.by_mount(mount)], + # ), + # critical_point=CriticalPoint.TIP, + # ) encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) print(f"End Encoder: {final_tip_loc}") final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] - location = "Tiprack" - tip_count = 1 - test_details = [ - start_time, - m_current, - location, - init_tip_loc, - final_tip_loc, - tip_count, - ] - enc_record(file_name, test_details) - tiprack_loc = [ - tiprack_loc[OT3Axis.X], - tiprack_loc[OT3Axis.Y], - tiprack_loc[OT3Axis.by_mount(mount)], - ] - if args.dial_indicator: - await hw_api.move_to( - mount, - Point( - slot_loc["C2"][0], - slot_loc["C2"][1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - ) - cp = CriticalPoint.TIP - print("Move to Tiprack") - current_position = await hw_api.current_position_ot3(mount) - dial_loc = await jog(hw_api, current_position, cp) - dial_loc = [ - dial_loc[OT3Axis.X], - dial_loc[OT3Axis.Y], - dial_loc[OT3Axis.by_mount(mount)], - ] - tip_offset = 0 - measurements = "" - tips = 8 - trial = 1 - for tip in range(1, tips + 1): - - await hw_api.move_to( - mount, - Point( - dial_loc[0], - dial_loc[1] + tip_offset, - dial_loc[2], - ), - ) - await asyncio.sleep(1) - tip_measurement = gauge.read() - await asyncio.sleep(2) - measurements += str(tip_measurement) + "," - - await hw_api.move_to( - mount, - Point( - dial_loc[0], - dial_loc[1] + tip_offset, - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - ) - tip_offset += 9 - await hw_api.move_to( - mount, - Point( - dial_loc[0], - dial_loc[1] + tip_offset, - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - ) - d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" - data.append_data_to_file(test_n, test_f, d_str) + # if args.dial_indicator: + # cp = CriticalPoint.TIP + # dial_loc = Point( + # slot_loc["C2"][0], + # slot_loc["C2"][1], + # home_with_tip_position[OT3Axis.by_mount(mount)]) + # print("Move to Tiprack") + # await move_to_point(hw_api, mount, dial_loc, cp) + # print("Move to Tiprack") + # current_position = await hw_api.current_position_ot3(mount) + # dial_loc = await jog(hw_api, current_position, cp) + # dial_loc = [ + # dial_loc[OT3Axis.X], + # dial_loc[OT3Axis.Y], + # dial_loc[OT3Axis.by_mount(mount)], + # ] + # tip_offset = 0 + # measurements = "" + # tips = 96 + # trial = 1 + # for tip in range(1, tips + 1): + # await hw_api.move_to( + # mount, + # Point( + # dial_loc[0], + # dial_loc[1] + tip_offset, + # dial_loc[2], + # ), + # ) + # await asyncio.sleep(1) + # tip_measurement = gauge.read() + # await asyncio.sleep(2) + # measurements += str(tip_measurement) + "," + # + # await hw_api.move_to( + # mount, + # Point( + # dial_loc[0], + # dial_loc[1] + tip_offset, + # home_with_tip_position[OT3Axis.by_mount(mount)], + # ), + # ) + # tip_offset += 9 + # await hw_api.move_to( + # mount, + # Point( + # dial_loc[0], + # dial_loc[1] + tip_offset, + # home_with_tip_position[OT3Axis.by_mount(mount)], + # ), + # ) + # d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" + # data.append_data_to_file(test_n, test_f, d_str) if args.trough: await hw_api.move_to( @@ -486,27 +469,10 @@ async def _main() -> None: # -----------------------Tiprack------------------------------------ m_current = float(input("motor_current in amps: ")) pick_up_speed = float(input("pick up tip speed in mm/s: ")) + cp = CriticalPoint.NOZZLE # Move over to the TipRack location and - await hw_api.move_to( - mount, - Point( - tiprack_loc[0] + x_offset, - tiprack_loc[1] + y_offset, - home_position[OT3Axis.by_mount(mount)], - ), - ) - - # Move Pipette to top of Tip Rack Location - await hw_api.move_to( - mount, - Point( - tiprack_loc[0] + x_offset, - tiprack_loc[1] + y_offset, - tiprack_loc[2], - ), - speed=65, - ) - location = "Tiprack" + print("Move to Tiprack") + await move_to_point(hw_api, mount, tiprack_loc, cp) # Start recording the encoder init_tip_loc = await hw_api.encoder_current_position_ot3( @@ -521,18 +487,6 @@ async def _main() -> None: final_tip_loc = await hw_api.pick_up_tip( mount, tip_length=tip_length[args.tip_size] ) - await hw_api.move_to( - mount, - Point( - tiprack_loc[0] + x_offset, - tiprack_loc[1] + y_offset, - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - critical_point=CriticalPoint.TIP, - ) - # home_with_tip_position = await hw_api.current_position_ot3( - # mount, critical_point=CriticalPoint.TIP - # ) encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) print(f"End Encoder: {final_tip_loc}") @@ -548,157 +502,159 @@ async def _main() -> None: enc_record(file_name, test_details) # Home Z await hw_api.home([OT3Axis.by_mount(mount)]) + cp = CriticalPoint.TIP + current_position = await hw_api.current_position_ot3(mount, cp) # --------------------------Dial Indicator----------------------- # Move over to the dial indicator - await hw_api.move_to( - mount, - Point( - dial_loc[0], - dial_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - ) - tip_offset = 0 - measurements = "" - tips = 8 - for t in range(1, tips + 1): - # Move over to the dial indicator - await hw_api.move_to( - mount, - Point(dial_loc[0], dial_loc[1] + tip_offset, dial_loc[2]), - ) - await asyncio.sleep(3) - tip_measurement = gauge.read() - measurements += str(tip_measurement) + "," - await asyncio.sleep(1) - # Move over to the dial indicator - await hw_api.move_to( - mount, - Point( - dial_loc[0], - dial_loc[1] + tip_offset, - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - ) - tip_offset += 9 - await hw_api.move_to( - mount, - Point( - dial_loc[0], - dial_loc[1] + tip_offset, - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - ) - d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" - data.append_data_to_file(test_n, test_f, d_str) - # -----------------------Aspirate----------------------------------- - await hw_api.move_to( - mount, - Point( - trough_loc[0], - trough_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - ) - # Move to offset from trough - await hw_api.move_to( - mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) - ) - # Move the plunger to the top position - await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[0]) - # Liquid Probe - liquid_height, enc_liquid_height = await hw_api.liquid_probe( - mount, probe_settings=liquid_probe_settings - ) - - liquid_height = await hw_api.current_position_ot3( - mount, critical_point=CriticalPoint.TIP - ) - # await move_plunger_relative_ot3(hw_api, mount, 1.5, None, speed = 2) # P50S - await move_plunger_relative_ot3( - hw_api, mount, 0.25, None, speed=2 - ) # P1KS - await hw_api.move_to( - mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) - ) - # Prepare to aspirate before descending to trough well - await hw_api.prepare_for_aspirate(mount) - # Descend to aspirate depth - await hw_api.move_to( - mount, - Point( - liquid_height[OT3Axis.X], - liquid_height[OT3Axis.Y], - liquid_height[OT3Axis.by_mount(mount)] - aspirate_depth, - ), - speed=5, - critical_point=CriticalPoint.TIP, - ) - # Aspirate - await hw_api.aspirate(mount, volume=50) - cur_pos = await hw_api.current_position_ot3( - mount, critical_point=CriticalPoint.TIP - ) - z_pos = cur_pos[OT3Axis.by_mount(mount)] - # Retract from liquid with retract speed - await hw_api.move_to( - mount, - Point(trough_loc[0], trough_loc[1], z_pos + liquid_retract_dist), - speed=liquid_retract_speed, - critical_point=CriticalPoint.TIP, - ) - await hw_api.move_to( - mount, - Point( - trough_loc[0], - trough_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - critical_point=CriticalPoint.TIP, - ) - await countdown(count_time=leak_test_time) - # input("Check to see if the pipette is leaking") - await hw_api.move_to( - mount, - Point(trough_loc[0], trough_loc[1], trough_loc[2]), - critical_point=CriticalPoint.TIP, - ) - await hw_api.move_to( - mount, - Point( - trough_loc[0], - trough_loc[1], - liquid_height[OT3Axis.by_mount(mount)] - dispense_depth, - ), - speed=5, - critical_point=CriticalPoint.TIP, - ) - await hw_api.dispense(mount) - await hw_api.blow_out(mount) + # await hw_api.move_to( + # mount, + # Point( + # dial_loc[0], + # dial_loc[1], + # home_with_tip_position[OT3Axis.by_mount(mount)], + # ), + # ) + # tip_offset = 0 + # measurements = "" + # tips = 8 + # for t in range(1, tips + 1): + # # Move over to the dial indicator + # await hw_api.move_to( + # mount, + # Point(dial_loc[0], dial_loc[1] + tip_offset, dial_loc[2]), + # ) + # await asyncio.sleep(3) + # tip_measurement = gauge.read() + # measurements += str(tip_measurement) + "," + # await asyncio.sleep(1) + # # Move over to the dial indicator + # await hw_api.move_to( + # mount, + # Point( + # dial_loc[0], + # dial_loc[1] + tip_offset, + # home_with_tip_position[OT3Axis.by_mount(mount)], + # ), + # ) + # tip_offset += 9 + # await hw_api.move_to( + # mount, + # Point( + # dial_loc[0], + # dial_loc[1] + tip_offset, + # home_with_tip_position[OT3Axis.by_mount(mount)], + # ), + # ) + # d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" + # data.append_data_to_file(test_n, test_f, d_str) + # # -----------------------Aspirate----------------------------------- + # await hw_api.move_to( + # mount, + # Point( + # trough_loc[0], + # trough_loc[1], + # home_with_tip_position[OT3Axis.by_mount(mount)], + # ), + # ) + # # Move to offset from trough + # await hw_api.move_to( + # mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) + # ) + # # Move the plunger to the top position + # await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[0]) + # # Liquid Probe + # liquid_height, enc_liquid_height = await hw_api.liquid_probe( + # mount, probe_settings=liquid_probe_settings + # ) + # + # liquid_height = await hw_api.current_position_ot3( + # mount, critical_point=CriticalPoint.TIP + # ) + # # await move_plunger_relative_ot3(hw_api, mount, 1.5, None, speed = 2) # P50S + # await move_plunger_relative_ot3( + # hw_api, mount, 0.25, None, speed=2 + # ) # P1KS + # await hw_api.move_to( + # mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) + # ) + # # Prepare to aspirate before descending to trough well + # await hw_api.prepare_for_aspirate(mount) + # # Descend to aspirate depth + # await hw_api.move_to( + # mount, + # Point( + # liquid_height[OT3Axis.X], + # liquid_height[OT3Axis.Y], + # liquid_height[OT3Axis.by_mount(mount)] - aspirate_depth, + # ), + # speed=5, + # critical_point=CriticalPoint.TIP, + # ) + # # Aspirate + # await hw_api.aspirate(mount, volume=50) + # cur_pos = await hw_api.current_position_ot3( + # mount, critical_point=CriticalPoint.TIP + # ) + # z_pos = cur_pos[OT3Axis.by_mount(mount)] + # # Retract from liquid with retract speed + # await hw_api.move_to( + # mount, + # Point(trough_loc[0], trough_loc[1], z_pos + liquid_retract_dist), + # speed=liquid_retract_speed, + # critical_point=CriticalPoint.TIP, + # ) + # await hw_api.move_to( + # mount, + # Point( + # trough_loc[0], + # trough_loc[1], + # home_with_tip_position[OT3Axis.by_mount(mount)], + # ), + # critical_point=CriticalPoint.TIP, + # ) + # await countdown(count_time=leak_test_time) + # # input("Check to see if the pipette is leaking") + # await hw_api.move_to( + # mount, + # Point(trough_loc[0], trough_loc[1], trough_loc[2]), + # critical_point=CriticalPoint.TIP, + # ) + # await hw_api.move_to( + # mount, + # Point( + # trough_loc[0], + # trough_loc[1], + # liquid_height[OT3Axis.by_mount(mount)] - dispense_depth, + # ), + # speed=5, + # critical_point=CriticalPoint.TIP, + # ) + # await hw_api.dispense(mount) + # await hw_api.blow_out(mount) # --------------------Drop Tip-------------------------------------- - current_position = await hw_api.current_position_ot3( - mount, critical_point=CriticalPoint.TIP - ) - await hw_api.move_to( - mount, - Point( - trough_loc[0], - trough_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - critical_point=CriticalPoint.TIP, - ) - # Move to trash slot - await hw_api.move_to( - mount, - Point( - slot_loc["A3"][0] + 50, - slot_loc["A3"][1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - critical_point=CriticalPoint.TIP, - ) - await hw_api.drop_tip(mount) + # current_position = await hw_api.current_position_ot3( + # mount, critical_point=CriticalPoint.TIP + # ) + # await hw_api.move_to( + # mount, + # Point( + # trough_loc[0], + # trough_loc[1], + # home_with_tip_position[OT3Axis.by_mount(mount)], + # ), + # critical_point=CriticalPoint.TIP, + # ) + # # Move to trash slot + # await hw_api.move_to( + # mount, + # Point( + # slot_loc["A3"][0] + 50, + # slot_loc["A3"][1], + # home_with_tip_position[OT3Axis.by_mount(mount)], + # ), + # critical_point=CriticalPoint.TIP, + # ) + # await hw_api.drop_tip(mount) await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) except KeyboardInterrupt: @@ -727,13 +683,13 @@ async def _main() -> None: parser.add_argument("--fg_jog", action="store_true") parser.add_argument("--trough", action="store_true") parser.add_argument("--tiprack", action="store_true") - parser.add_argument("--mount", type=str, choices=["left", "right"], default="right") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C1") parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") parser.add_argument("--fg", action="store_true") parser.add_argument("--dial_indicator", action="store_true") - parser.add_argument("--tip_size", type=str, default="T50", help="Tip Size") + parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") parser.add_argument("--max_z_distance", type=float, default=40) parser.add_argument("--min_z_distance", type=float, default=5) parser.add_argument("--mount_speed", type=float, default=5) From 393d9893206a4bf7dcc5da92414c77cb757c242e Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Wed, 31 May 2023 08:56:47 -0400 Subject: [PATCH 26/53] new changes --- .../scripts/96_channel_pick_up_tip.py | 502 ++++-------------- 1 file changed, 111 insertions(+), 391 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py index 8493dcd3485..bc8a1315c13 100644 --- a/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py @@ -235,27 +235,38 @@ async def move_to_point(api, mount, point, cp): point.y, point.z)) +async def calibrate_tiprack(api, home_position, mount): + cp = CriticalPoint.NOZZLE + tiprack_loc = Point( + slot_loc["B2"][0], + slot_loc["B2"][1], + home_position[OT3Axis.by_mount(mount)]) + print("Move to Tiprack") + await move_to_point(api, mount, tiprack_loc, cp) + current_position = await api.current_position_ot3(mount, cp) + tiprack_loc = await jog(api, current_position, cp) + tiprack_loc = Point(tiprack_loc[OT3Axis.X], + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)]) + await api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + await api.home_z(mount) + cp = CriticalPoint.TIP + home_with_tip = await api.current_position(mount, cp) + drop_tip_loc = await jog(api, home_with_tip, cp) + drop_tip_loc = Point(drop_tip_loc[OT3Axis.X], + drop_tip_loc[OT3Axis.Y], + drop_tip_loc[OT3Axis.by_mount(mount)]) + await api.drop_tip(mount) + return tiprack_loc, drop_tip_loc + async def _main() -> None: today = datetime.date.today() - tips_to_use = 12 - slot_loc = { - "A1": (13.42, 394.92, 110), - "A2": (177.32, 394.92, 110), - "A3": (341.03, 394.0, 110), - "B1": (13.42, 288.42, 110), - "B2": (177.32, 288.92, 110), - "B3": (341.03, 288.92, 110), - "C1": (13.42, 181.92, 110), - "C2": (177.32, 181.92, 110), - "C3": (341.03, 181.92, 110), - "D1": (13.42, 75.5, 110), - "D2": (177.32, 75.5, 110), - "D3": (341.03, 75.5, 110), - } + tips_to_use = 96 hw_api = await build_async_ot3_hardware_api( is_simulating=args.simulate, use_defaults=True ) - tip_length = {"T1K": 85.7, "T200": 48.35, "T50": 47.9} pipette_model = hw_api.get_all_attached_instr()[OT3Mount.LEFT]["pipette_id"] dial_data = { "Ch1": None, "Ch2": None, "Ch3": None, "Ch4": None, "Ch5": None, "Ch6": None, @@ -278,391 +289,91 @@ async def _main() -> None: "Trial": None, "pipette_model": None, } - m_current = float(input("motor_current in amps: ")) - pick_up_speed = float(input("pick up tip speed in mm/s: ")) - details = [pipette_model, m_current] - test_n, test_f = file_setup(dial_data, details) - file_name = "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s.csv" % ( - m_current, - datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), - ) - print(file_name) - print(test_n) - print(test_f) - await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) + # m_current = float(input("motor_current in amps: ")) + # pick_up_speed = float(input("pick up tip speed in mm/s: ")) + # details = [pipette_model, m_current] + # test_n, test_f = file_setup(dial_data, details) + # file_name = "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s.csv" % ( + # m_current, + # datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + # ) + # print(file_name) + # print(test_n) + # print(test_f) + await hw_api.home() + await asyncio.sleep(1) await hw_api.home_plunger(mount) await hw_api.set_lights(rails=True) plunger_pos = get_plunger_positions_ot3(hw_api, mount) + print(plunger_pos) home_position = await hw_api.current_position_ot3(mount) start_time = time.perf_counter() - # if args.tip_size == "T1K": - # home_with_tip_position = 164.3 # T1K - # if args.tip_size == "T200": - # home_with_tip_position = 201.64999999999998 # T1K - # elif args.tip_size == "T50": - # home_with_tip_position = 192.1 # T50 - + m_current = float(input("motor_current in amps: ")) + pick_up_speed = float(input("pick up tip speed in mm/s: ")) + # Calibrate to tiprack if args.tiprack: - cp = CriticalPoint.NOZZLE - tiprack_loc = Point( - slot_loc["B2"][0], - slot_loc["B2"][1], - home_position[OT3Axis.by_mount(mount)]) - print("Move to Tiprack") - await move_to_point(hw_api, mount, tiprack_loc, cp) - current_position = await hw_api.current_position_ot3(mount) - tiprack_loc = await jog(hw_api, current_position, cp) - tiprack_loc = Point(tiprack_loc[OT3Axis.X], - tiprack_loc[OT3Axis.Y], - tiprack_loc[OT3Axis.by_mount(mount)]) - # Start recording the encoder - init_tip_loc = await hw_api.encoder_current_position_ot3( - mount, CriticalPoint.NOZZLE - ) - print(f"Start encoder: {init_tip_loc}") - init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] - encoder_position = init_tip_loc - init_tip_loc = await hw_api.encoder_current_position_ot3( - mount, CriticalPoint.NOZZLE - ) - await update_pick_up_current(hw_api, mount, m_current) - await update_pickup_tip_speed(hw_api, mount, pick_up_speed) - # Move pipette to Force Gauge press location - final_tip_loc = await hw_api.pick_up_tip( - mount, tip_length=tip_length[args.tip_size] - ) - # move_to_point(hw_api, mount, tiprack_loc, cp) - await home_ot3(hw_api, [OT3Axis.by_mount(mount)]) - home_with_tip_position = await hw_api.current_position_ot3( - mount, critical_point=CriticalPoint.TIP - ) - # await hw_api.move_to( - # mount, - # Point( - # current_position[OT3Axis.X], - # current_position[OT3Axis.Y], - # home_with_tip_position[OT3Axis.by_mount(mount)], - # ), - # critical_point=CriticalPoint.TIP, - # ) - encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] - # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) - print(f"End Encoder: {final_tip_loc}") - final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] - - # if args.dial_indicator: - # cp = CriticalPoint.TIP - # dial_loc = Point( - # slot_loc["C2"][0], - # slot_loc["C2"][1], - # home_with_tip_position[OT3Axis.by_mount(mount)]) - # print("Move to Tiprack") - # await move_to_point(hw_api, mount, dial_loc, cp) - # print("Move to Tiprack") - # current_position = await hw_api.current_position_ot3(mount) - # dial_loc = await jog(hw_api, current_position, cp) - # dial_loc = [ - # dial_loc[OT3Axis.X], - # dial_loc[OT3Axis.Y], - # dial_loc[OT3Axis.by_mount(mount)], - # ] - # tip_offset = 0 - # measurements = "" - # tips = 96 - # trial = 1 - # for tip in range(1, tips + 1): - # await hw_api.move_to( - # mount, - # Point( - # dial_loc[0], - # dial_loc[1] + tip_offset, - # dial_loc[2], - # ), - # ) - # await asyncio.sleep(1) - # tip_measurement = gauge.read() - # await asyncio.sleep(2) - # measurements += str(tip_measurement) + "," - # - # await hw_api.move_to( - # mount, - # Point( - # dial_loc[0], - # dial_loc[1] + tip_offset, - # home_with_tip_position[OT3Axis.by_mount(mount)], - # ), - # ) - # tip_offset += 9 - # await hw_api.move_to( - # mount, - # Point( - # dial_loc[0], - # dial_loc[1] + tip_offset, - # home_with_tip_position[OT3Axis.by_mount(mount)], - # ), - # ) - # d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" - # data.append_data_to_file(test_n, test_f, d_str) - - if args.trough: - await hw_api.move_to( - mount, - Point( - slot_loc["B3"][0], - slot_loc["B3"][1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - ) + pickup_loc, droptip_loc = await calibrate_tiprack(hw_api, home_position, mount) + # Let's pick up the same tips and move to up. + cp = CriticalPoint.NOZZLE + await move_to_point(hw_api, mount, pickup_loc, cp) + await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size]) + await hw_api.home_z(mount) + cp = CriticalPoint.TIP + home_w_tip = await hw_api.current_position_ot3(mount, cp) + # Calibrate Dial Indicator with single tip + if args.dial_indicator: + tip_count = 0 + x_offset = 0 + y_offset = 0 cp = CriticalPoint.TIP - print("Move to Trough") - current_position = await hw_api.current_position_ot3(mount) - trough_loc = await jog(hw_api, current_position, cp) - trough_loc = [ - trough_loc[OT3Axis.X], - trough_loc[OT3Axis.Y], - trough_loc[OT3Axis.by_mount(mount)], - ] - await hw_api.move_to( - mount, - Point( - trough_loc[0], - trough_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - critical_point=CriticalPoint.TIP, - ) - # Move to trash slot - await hw_api.move_to( - mount, - Point( - slot_loc["A3"][0] + 50, - slot_loc["A3"][1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - critical_point=CriticalPoint.TIP, - ) - input("Feel the Tip!") - await hw_api.drop_tip(mount) + dial_loc = Point( + slot_loc["C2"][0], + slot_loc["C2"][1], + home_w_tip[OT3Axis.by_mount(mount)]) + print("Move to Dial Indicator") + await move_to_point(hw_api, mount, dial_loc, cp) + current_position = await hw_api.current_position_ot3(mount, cp) + dial_loc = await jog(hw_api, current_position, cp) + dial_loc = Point(dial_loc[OT3Axis.X], + dial_loc[OT3Axis.Y], + dial_loc[OT3Axis.by_mount(mount)]) - lp_file_name = "/var/pressure_sensor_data_P-{}_Z-{}-{}.csv".format( - args.plunger_speed, args.mount_speed, today.strftime("%b-%d-%Y") - ) - liquid_probe_settings = LiquidProbeSettings( - max_z_distance=args.max_z_distance, - min_z_distance=args.min_z_distance, - mount_speed=args.mount_speed, - plunger_speed=args.plunger_speed, - sensor_threshold_pascals=args.sensor_threshold, - expected_liquid_height=args.expected_liquid_height, - log_pressure=args.log_pressure, - aspirate_while_sensing=False, - auto_zero_sensor=False, - num_baseline_reads=10, - data_file=lp_file_name, - ) - tip_count = 0 - x_offset = 0 - y_offset = 0 try: - while True: - # -----------------------Tiprack------------------------------------ - m_current = float(input("motor_current in amps: ")) - pick_up_speed = float(input("pick up tip speed in mm/s: ")) + for tip in range(1, tips_to_use + 1): + tip_count += 1 + y_offset += 9 + if tip_count % 8 == 0: + y_offset = 0 + if tip_count % 8 == 0: + x_offset -= 9 + await asyncio.sleep(1) + tip_measurement = gauge.read() + print(f"Tip_attachment(mm): {tip_measurement}") + tip_position = Point(dial_loc[0] + x_offset, + dial_loc[1] + y_offset, + dial_loc[2]) + await move_to_point(hw_api, mount, tip_position, cp) + cp = CriticalPoint.TIP + await move_to_point(hw_api, mount, droptip_loc, cp) + await hw_api.drop_tip(mount) + await hw_api.home_z(mount) + # m_current = float(input("motor_current in amps: ")) + # pick_up_speed = float(input("pick up tip speed in mm/s: ")) cp = CriticalPoint.NOZZLE - # Move over to the TipRack location and - print("Move to Tiprack") - await move_to_point(hw_api, mount, tiprack_loc, cp) + await move_to_point(hw_api, mount, pickup_loc, cp) + await hw_api.pick_up_tip(mount, tip_length=tip_length[args.tip_size]) + # await hw_api.home_z(mount) + - # Start recording the encoder - init_tip_loc = await hw_api.encoder_current_position_ot3( - mount, CriticalPoint.NOZZLE - ) - print(f"Start encoder: {init_tip_loc}") - init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] - encoder_position = init_tip_loc - # Press Pipette into the tip - await update_pick_up_current(hw_api, mount, m_current) - # Move pipette to Force Gauge press location - final_tip_loc = await hw_api.pick_up_tip( - mount, tip_length=tip_length[args.tip_size] - ) - encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] - # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) - print(f"End Encoder: {final_tip_loc}") - final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] - test_details = [ - start_time, - m_current, - location, - init_tip_loc, - final_tip_loc, - tip_count, - ] - enc_record(file_name, test_details) - # Home Z - await hw_api.home([OT3Axis.by_mount(mount)]) - cp = CriticalPoint.TIP - current_position = await hw_api.current_position_ot3(mount, cp) - # --------------------------Dial Indicator----------------------- - # Move over to the dial indicator - # await hw_api.move_to( - # mount, - # Point( - # dial_loc[0], - # dial_loc[1], - # home_with_tip_position[OT3Axis.by_mount(mount)], - # ), - # ) - # tip_offset = 0 - # measurements = "" - # tips = 8 - # for t in range(1, tips + 1): - # # Move over to the dial indicator - # await hw_api.move_to( - # mount, - # Point(dial_loc[0], dial_loc[1] + tip_offset, dial_loc[2]), - # ) - # await asyncio.sleep(3) - # tip_measurement = gauge.read() - # measurements += str(tip_measurement) + "," - # await asyncio.sleep(1) - # # Move over to the dial indicator - # await hw_api.move_to( - # mount, - # Point( - # dial_loc[0], - # dial_loc[1] + tip_offset, - # home_with_tip_position[OT3Axis.by_mount(mount)], - # ), - # ) - # tip_offset += 9 - # await hw_api.move_to( - # mount, - # Point( - # dial_loc[0], - # dial_loc[1] + tip_offset, - # home_with_tip_position[OT3Axis.by_mount(mount)], - # ), - # ) - # d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" - # data.append_data_to_file(test_n, test_f, d_str) - # # -----------------------Aspirate----------------------------------- - # await hw_api.move_to( - # mount, - # Point( - # trough_loc[0], - # trough_loc[1], - # home_with_tip_position[OT3Axis.by_mount(mount)], - # ), - # ) - # # Move to offset from trough - # await hw_api.move_to( - # mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) - # ) - # # Move the plunger to the top position - # await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[0]) - # # Liquid Probe - # liquid_height, enc_liquid_height = await hw_api.liquid_probe( - # mount, probe_settings=liquid_probe_settings - # ) - # - # liquid_height = await hw_api.current_position_ot3( - # mount, critical_point=CriticalPoint.TIP - # ) - # # await move_plunger_relative_ot3(hw_api, mount, 1.5, None, speed = 2) # P50S - # await move_plunger_relative_ot3( - # hw_api, mount, 0.25, None, speed=2 - # ) # P1KS - # await hw_api.move_to( - # mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) - # ) - # # Prepare to aspirate before descending to trough well - # await hw_api.prepare_for_aspirate(mount) - # # Descend to aspirate depth - # await hw_api.move_to( - # mount, - # Point( - # liquid_height[OT3Axis.X], - # liquid_height[OT3Axis.Y], - # liquid_height[OT3Axis.by_mount(mount)] - aspirate_depth, - # ), - # speed=5, - # critical_point=CriticalPoint.TIP, - # ) - # # Aspirate - # await hw_api.aspirate(mount, volume=50) - # cur_pos = await hw_api.current_position_ot3( - # mount, critical_point=CriticalPoint.TIP - # ) - # z_pos = cur_pos[OT3Axis.by_mount(mount)] - # # Retract from liquid with retract speed - # await hw_api.move_to( - # mount, - # Point(trough_loc[0], trough_loc[1], z_pos + liquid_retract_dist), - # speed=liquid_retract_speed, - # critical_point=CriticalPoint.TIP, - # ) - # await hw_api.move_to( - # mount, - # Point( - # trough_loc[0], - # trough_loc[1], - # home_with_tip_position[OT3Axis.by_mount(mount)], - # ), - # critical_point=CriticalPoint.TIP, - # ) - # await countdown(count_time=leak_test_time) - # # input("Check to see if the pipette is leaking") - # await hw_api.move_to( - # mount, - # Point(trough_loc[0], trough_loc[1], trough_loc[2]), - # critical_point=CriticalPoint.TIP, - # ) - # await hw_api.move_to( - # mount, - # Point( - # trough_loc[0], - # trough_loc[1], - # liquid_height[OT3Axis.by_mount(mount)] - dispense_depth, - # ), - # speed=5, - # critical_point=CriticalPoint.TIP, - # ) - # await hw_api.dispense(mount) - # await hw_api.blow_out(mount) - # --------------------Drop Tip-------------------------------------- - # current_position = await hw_api.current_position_ot3( - # mount, critical_point=CriticalPoint.TIP - # ) - # await hw_api.move_to( - # mount, - # Point( - # trough_loc[0], - # trough_loc[1], - # home_with_tip_position[OT3Axis.by_mount(mount)], - # ), - # critical_point=CriticalPoint.TIP, - # ) - # # Move to trash slot - # await hw_api.move_to( - # mount, - # Point( - # slot_loc["A3"][0] + 50, - # slot_loc["A3"][1], - # home_with_tip_position[OT3Axis.by_mount(mount)], - # ), - # critical_point=CriticalPoint.TIP, - # ) - # await hw_api.drop_tip(mount) - - await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) except KeyboardInterrupt: - await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y]) finally: - await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y]) await hw_api.clean_up() + if __name__ == "__main__": slot_locs = [ "A1", @@ -699,20 +410,29 @@ async def _main() -> None: ) parser.add_argument("--expected_liquid_height", type=int, default=0) parser.add_argument("--log_pressure", action="store_true") - # parser.add_argument( - # "--fg_port", type=str, default="/dev/ttyUSB0", help="Force Gauge Port" - # ) parser.add_argument( "--dial_port", type=str, default="/dev/ttyUSB0", help="Dial indicator Port" ) args = parser.parse_args() + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.0, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + tip_length = {"T1K": 95.7, "T200": 58.35, "T50": 57.5} if args.mount == "left": mount = OT3Mount.LEFT else: mount = OT3Mount.RIGHT - # if args.fg: - # fg = Mark10.create(port=args.fg_port) - # fg.connect() if args.dial_indicator: gauge = dial_indicator_setup(port=args.dial_port) From f1edd0a7a52efb6d39537bc96fb3ee59551db92f Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Wed, 31 May 2023 09:00:50 -0400 Subject: [PATCH 27/53] git attributes should match the default file --- .gitattributes | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 0662e2ecd1f..3ba1a647169 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,5 +5,3 @@ *.bat binary *.gltf binary api/pypi-readme.rst text eol=lf -git ls-files --eol -* -text From 609e401921befe163314410fa274a9bfa318b2b4 Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Wed, 31 May 2023 12:26:21 -0400 Subject: [PATCH 28/53] More changes --- .../scripts/96_channel_pick_up_tip.py | 71 +++++++++++-------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py index bc8a1315c13..c4cde6b0b54 100644 --- a/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py @@ -268,35 +268,38 @@ async def _main() -> None: is_simulating=args.simulate, use_defaults=True ) pipette_model = hw_api.get_all_attached_instr()[OT3Mount.LEFT]["pipette_id"] - dial_data = { - "Ch1": None, "Ch2": None, "Ch3": None, "Ch4": None, "Ch5": None, "Ch6": None, - "Ch7": None, "Ch8": None, "Ch9": None, "Ch10": None, "Ch11": None, "Ch12": None, - "Ch13": None, "Ch14": None, "Ch15": None, "Ch16": None, "Ch17": None, "Ch18": None, - "Ch19": None, "Ch20": None, "Ch21": None, "Ch22": None, "Ch23": None, "Ch24": None, - "Ch25": None, "Ch26": None, "Ch27": None, "Ch28": None, "Ch29": None, "Ch30": None, - "Ch31": None, "Ch32": None, "Ch33": None, "Ch34": None, "Ch35": None, "Ch36": None, - "Ch37": None, "Ch38": None, "Ch39": None, "Ch40": None, "Ch41": None, "Ch42": None, - "Ch43": None, "Ch44": None, "Ch45": None, "Ch46": None, "Ch47": None, "Ch48": None, - "Ch49": None, "Ch50": None, "Ch51": None, "Ch52": None, "Ch53": None, "Ch54": None, - "Ch55": None, "Ch56": None, "Ch57": None, "Ch58": None, "Ch59": None, "Ch60": None, - "Ch61": None, "Ch62": None, "Ch63": None, "Ch64": None, "Ch65": None, "Ch66": None, - "Ch67": None, "Ch68": None, "Ch69": None, "Ch70": None, "Ch71": None, "Ch72": None, - "Ch73": None, "Ch74": None, "Ch75": None, "Ch76": None, "Ch77": None, "Ch78": None, - "Ch79": None, "Ch80": None, "Ch81": None, "Ch82": None, "Ch83": None, "Ch84": None, - "Ch85": None, "Ch86": None, "Ch87": None, "Ch88": None, "Ch89": None, "Ch90": None, - "Ch91": None, "Ch92": None, "Ch93": None, "Ch94": None, "Ch95": None, "Ch96": None, - "Motor Current": None, - "Trial": None, - "pipette_model": None, - } + # dial_data = { + # "Ch1": None, "Ch2": None, "Ch3": None, "Ch4": None, "Ch5": None, "Ch6": None, + # "Ch7": None, "Ch8": None, "Ch9": None, "Ch10": None, "Ch11": None, "Ch12": None, + # "Ch13": None, "Ch14": None, "Ch15": None, "Ch16": None, "Ch17": None, "Ch18": None, + # "Ch19": None, "Ch20": None, "Ch21": None, "Ch22": None, "Ch23": None, "Ch24": None, + # "Ch25": None, "Ch26": None, "Ch27": None, "Ch28": None, "Ch29": None, "Ch30": None, + # "Ch31": None, "Ch32": None, "Ch33": None, "Ch34": None, "Ch35": None, "Ch36": None, + # "Ch37": None, "Ch38": None, "Ch39": None, "Ch40": None, "Ch41": None, "Ch42": None, + # "Ch43": None, "Ch44": None, "Ch45": None, "Ch46": None, "Ch47": None, "Ch48": None, + # "Ch49": None, "Ch50": None, "Ch51": None, "Ch52": None, "Ch53": None, "Ch54": None, + # "Ch55": None, "Ch56": None, "Ch57": None, "Ch58": None, "Ch59": None, "Ch60": None, + # "Ch61": None, "Ch62": None, "Ch63": None, "Ch64": None, "Ch65": None, "Ch66": None, + # "Ch67": None, "Ch68": None, "Ch69": None, "Ch70": None, "Ch71": None, "Ch72": None, + # "Ch73": None, "Ch74": None, "Ch75": None, "Ch76": None, "Ch77": None, "Ch78": None, + # "Ch79": None, "Ch80": None, "Ch81": None, "Ch82": None, "Ch83": None, "Ch84": None, + # "Ch85": None, "Ch86": None, "Ch87": None, "Ch88": None, "Ch89": None, "Ch90": None, + # "Ch91": None, "Ch92": None, "Ch93": None, "Ch94": None, "Ch95": None, "Ch96": None, + # "Motor Current": None, + # "Trial": None, + # "pipette_model": None, + # } + + dial_data = {"Column_1": None, "Column_2": None, "Column_3": None, "Column_4": None, "Column_5": None, "Column_6": None, + "Column_7": None, "Column_8": None, "Column_9": None, "Column_10": None, "Column_11": None, "Column_12": None} # m_current = float(input("motor_current in amps: ")) # pick_up_speed = float(input("pick up tip speed in mm/s: ")) - # details = [pipette_model, m_current] - # test_n, test_f = file_setup(dial_data, details) - # file_name = "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s.csv" % ( - # m_current, - # datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), - # ) + details = [pipette_model, m_current] + test_n, test_f = file_setup(dial_data, details) + file_name = "/home/root/.opentrons/testing_data/pickup_tip_test/pu_96_pipette_%s-%s.csv" % ( + m_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + ) # print(file_name) # print(test_n) # print(test_f) @@ -341,9 +344,10 @@ async def _main() -> None: try: while True: + measurements = [] for tip in range(1, tips_to_use + 1): tip_count += 1 - y_offset += 9 + x_offset += 9 if tip_count % 8 == 0: y_offset = 0 if tip_count % 8 == 0: @@ -354,7 +358,17 @@ async def _main() -> None: tip_position = Point(dial_loc[0] + x_offset, dial_loc[1] + y_offset, dial_loc[2]) + measurements.append(tip_measurement) + if tip_count % 12 == 0: + d_str = '' + for m in measurements: + d_str += str(m) + ',' + d_str = d_str[:-1] + data.append_data_to_file(test_n, test_f, d_str) + # Reset Measurements list + measurements = [] await move_to_point(hw_api, mount, tip_position, cp) + cp = CriticalPoint.TIP await move_to_point(hw_api, mount, droptip_loc, cp) await hw_api.drop_tip(mount) @@ -366,7 +380,6 @@ async def _main() -> None: await hw_api.pick_up_tip(mount, tip_length=tip_length[args.tip_size]) # await hw_api.home_z(mount) - except KeyboardInterrupt: await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y]) finally: From 31035f70cd134ab2ab59a75e99c3dffda408e00c Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Wed, 7 Jun 2023 12:13:43 -0400 Subject: [PATCH 29/53] delete this test script --- .../scripts/pick_up_tip_testing.py | 568 ------------------ 1 file changed, 568 deletions(-) delete mode 100644 hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py diff --git a/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py b/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py deleted file mode 100644 index f8d846de7f7..00000000000 --- a/hardware-testing/hardware_testing/scripts/pick_up_tip_testing.py +++ /dev/null @@ -1,568 +0,0 @@ -import logging -import asyncio -import argparse -from numpy import float64 -import termios -import sys, tty, os, time - -from opentrons.hardware_control.motion_utilities import target_position_from_plunger -from hardware_testing.opentrons_api.types import ( - GantryLoad, - OT3Mount, - OT3Axis, - Point, - Axis, -) -from hardware_testing.opentrons_api.helpers_ot3 import ( - OT3API, - build_async_ot3_hardware_api, - GantryLoadSettings, - set_gantry_load_per_axis_settings_ot3, - home_ot3, - get_endstop_position_ot3, - move_plunger_absolute_ot3, - update_pick_up_current, - update_pick_up_distance, -) - -from hardware_testing import data -from hardware_testing.drivers import mitutoyo_digimatic_indicator - - -def dict_values_to_line(dict): - return str.join(",", list(dict.values())) + "\n" - - -def dict_keys_to_line(dict): - return str.join(",", list(dict.keys())) + "\n" - - -def file_setup(test_data): - current_val = float(input("Enter Motor Current to be Tested:")) - test_name = "Tip_Attachment_Test" - test_header = dict_keys_to_line(test_data) - test_tag = "{}Amps-start-time-{}".format(current_val, int(time.time())) - test_id = data.create_run_id() - test_path = data.create_folder_for_test_data(test_name) - test_file = data.create_file_name(test_name, test_id, test_tag) - data.append_data_to_file(test_name, test_file, test_header) - print("FILE PATH = ", test_path) - print("FILE NAME = ", test_file) - return test_name, test_file - - -def dial_indicator_setup(): - gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator( - port="/dev/ttyUSB0" - ) - gauge.connect() - return gauge - - -def getch(): - """ - fd: file descriptor stdout, stdin, stderr - This functions gets a single input keyboard character from the user - """ - - def _getch(): - fd = sys.stdin.fileno() - old_settings = termios.tcgetattr(fd) - try: - tty.setraw(fd) - ch = sys.stdin.read(1) - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - return ch - - return _getch() - - -async def _jog_axis(api, position) -> None: - step_size = [0.05, 0.1, 0.5, 1, 10, 20, 50] - step_length_index = 3 - step = step_size[step_length_index] - xy_speed = 150 - za_speed = 65 - information_str = """ - Click >> i << to move up - Click >> k << to move down - Click >> a << to move left - Click >> d << to move right - Click >> w << to move forward - Click >> s << to move back - Click >> + << to Increase the length of each step - Click >> - << to decrease the length of each step - Click >> Enter << to save position - Click >> q << to quit the test script - """ - print(information_str) - while True: - input = getch() - if input == "a": - # minus x direction - sys.stdout.flush() - await api.move_rel( - mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed - ) - - elif input == "d": - # plus x direction - sys.stdout.flush() - await api.move_rel( - mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed - ) - - elif input == "w": - # minus y direction - sys.stdout.flush() - await api.move_rel( - mount, Point(0, step_size[step_length_index], 0), speed=xy_speed - ) - - elif input == "s": - # plus y direction - sys.stdout.flush() - await api.move_rel( - mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed - ) - - elif input == "i": - sys.stdout.flush() - await api.move_rel( - mount, Point(0, 0, step_size[step_length_index]), speed=za_speed - ) - - elif input == "k": - sys.stdout.flush() - await api.move_rel( - mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed - ) - - elif input == "r": - sys.stdout.flush() - position = await api.current_position_ot3(mount) - gauge_reading = gauge.read_stable(timeout=20) - test_data["X-Coordinate"] = round(position[OT3Axis.X], 2) - test_data["Y-Coordinate"] = round(position[OT3Axis.Y], 2) - test_data["Z-Coordinate"] = round(position[OT3Axis.by_mount(mount)], 2) - test_data["Deck Height(mm)"] = gauge_reading - print(test_data) - d_str = f"{round(position[OT3Axis.X], 2)}, \ - {round(position[OT3Axis.Y], 2)}, \ - {round(position[OT3Axis.by_mount(mount)], 2)}, \ - {gauge.read_stable(timeout=20)}, {gauge_reading}\n" - data.append_data_to_file(test_n, test_f, d_str) - - elif input == "q": - sys.stdout.flush() - print("TEST CANCELLED") - quit() - - elif input == "+": - sys.stdout.flush() - step_length_index = step_length_index + 1 - if step_length_index >= 6: - step_length_index = 6 - step = step_size[step_length_index] - - elif input == "-": - sys.stdout.flush() - step_length_index = step_length_index - 1 - if step_length_index <= 0: - step_length_index = 0 - step = step_size[step_length_index] - - elif input == "\r": - sys.stdout.flush() - return position - position = await api.current_position_ot3(mount) - - print( - "Coordinates: ", - round(position[OT3Axis.X], 2), - ",", - round(position[OT3Axis.Y], 2), - ",", - round(position[OT3Axis.by_mount(mount)], 2), - " Motor Step: ", - step_size[step_length_index], - end="", - ) - print("\r", end="") - - -async def _main() -> None: - hw_api = await build_async_ot3_hardware_api( - is_simulating=args.simulate, use_defaults=True - ) - # await set_default_current_settings(hw_api, load=None) - await home_ot3(hw_api, [OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) - home_pos = await hw_api.current_position_ot3(mount) - start_loc = { - OT3Axis.X: 150, - OT3Axis.Y: 200, - OT3Axis.by_mount(mount): home_pos[OT3Axis.by_mount(mount)], - } - # await hw_api.current_position_ot3(mount - await hw_api.cache_instruments() - trash_loc = () - await hw_api.home_plunger(mount) - tip_column = 0 - columns_to_use = 12 - try: - if args.test == "pick_up_tip": - start_time = time.time() - await hw_api.move_to( - mount, - Point( - start_loc[OT3Axis.X], - start_loc[OT3Axis.Y], - home_pos[OT3Axis.by_mount(mount)], - ), - ) - # await hw_api.move_to(mount, Point(home_pos[OT3Axis.X], - # start_loc[OT3Axis.Y], - # home_pos[OT3Axis.by_mount(mount)])) - # await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], - # start_loc[OT3Axis.Y], - # home_pos[OT3Axis.by_mount(mount)])) - for cycle in range(1, columns_to_use + 1): - if cycle <= 1: - print("Move to Tiprack") - home_position = await hw_api.current_position_ot3(mount) - tiprack_loc = await _jog_axis(hw_api, home_position) - # Coordinates: 177.0 , 182.1 , 97.5 - # tiprack_loc = {OT3Axis.X: 177.0, OT3Axis.Y: 182.1, OT3Axis.by_mount(mount): 97.5} - # await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X], - # tiprack_loc[OT3Axis.Y], - # home_pos[OT3Axis.by_mount(mount)])) - await hw_api.move_to( - mount, - Point( - tiprack_loc[OT3Axis.X] + tip_column, - tiprack_loc[OT3Axis.Y], - tiprack_loc[OT3Axis.by_mount(mount)], - ), - ) - # await hw_api.pick_up_tip(mount, tip_length = 58.5, presses = 2, increment = 1) - current_val = float(input("Enter Current Val: ")) - print(f"Current Val: {current_val}") - await update_pick_up_current(hw_api, mount, current_val) - await hw_api.pick_up_tip(mount, tip_length=58.5) - current_pos = await hw_api.current_position_ot3(mount) - # Move to Block - await hw_api.move_to( - mount, Point(207.2, 72.7, current_pos[OT3Axis.by_mount(mount)]) - ) - current_pos = await hw_api.current_position_ot3(mount) - block = await _jog_axis(hw_api, current_pos) - await hw_api.home_z(mount, allow_home_other=False) - - trash_loc = [432.7, 393.3, 100.0] - await hw_api.home_z(mount, allow_home_other=False) - current_pos = await hw_api.current_position_ot3(mount) - await hw_api.move_to( - mount, - Point( - trash_loc[0], trash_loc[1], current_pos[OT3Axis.by_mount(mount)] - ), - ) - await hw_api.move_to( - mount, Point(trash_loc[0], trash_loc[1], trash_loc[2]) - ) - await hw_api.drop_tip(mount) - await hw_api.home_z(mount, allow_home_other=False) - current_pos = await hw_api.current_position_ot3(mount) - # tip_column += 9 - await hw_api.move_to( - mount, - Point( - tiprack_loc[OT3Axis.X] + tip_column, - tiprack_loc[OT3Axis.Y], - current_pos[OT3Axis.by_mount(mount)], - ), - ) - elif args.test == "tip_height_test": - start_time = time.time() - await hw_api.move_to( - mount, - Point( - start_loc[OT3Axis.X], - start_loc[OT3Axis.Y], - home_pos[OT3Axis.by_mount(mount)], - ), - ) - # await hw_api.move_to(mount, Point(home_pos[OT3Axis.X], - # start_loc[OT3Axis.Y], - # home_pos[OT3Axis.by_mount(mount)])) - # await hw_api.move_to(mount, Point(start_loc[OT3Axis.X], - # start_loc[OT3Axis.Y], - # home_pos[OT3Axis.by_mount(mount)])) - for cycle in range(1, columns_to_use + 1): - if cycle <= 1: - print("Move to Tiprack") - home_position = await hw_api.current_position_ot3(mount) - tiprack_loc = await _jog_axis(hw_api, home_position) - # Coordinates: 177.0 , 182.1 , 97.5 - # tiprack_loc = {OT3Axis.X: 177.0, OT3Axis.Y: 182.1, OT3Axis.by_mount(mount): 97.5} - # await hw_api.move_to(mount, Point(tiprack_loc[OT3Axis.X], - # tiprack_loc[OT3Axis.Y], - # home_pos[OT3Axis.by_mount(mount)])) - await hw_api.move_to( - mount, - Point( - tiprack_loc[OT3Axis.X] + tip_column, - tiprack_loc[OT3Axis.Y], - tiprack_loc[OT3Axis.by_mount(mount)], - ), - ) - # await hw_api.pick_up_tip(mount, tip_length = 58.5, presses = 2, increment = 1) - current_val = float(input("Enter Current Val: ")) - print(f"Current Val: {current_val}") - await update_pick_up_current(hw_api, mount, current_val) - await hw_api.pick_up_tip(mount, tip_length=58.5) - current_pos = await hw_api.current_position_ot3(mount) - # Move to Block - await hw_api.move_to( - mount, Point(207.2, 72.7, current_pos[OT3Axis.by_mount(mount)]) - ) - current_pos = await hw_api.current_position_ot3(mount) - block = await _jog_axis(hw_api, current_pos) - await hw_api.home_z(mount, allow_home_other=False) - - if cycle <= 1: - print("Move to Tiprack") - home_position = await hw_api.current_position_ot3(mount) - dial_pos = await _jog_axis(hw_api, home_position) - await hw_api.home_z(mount) - current_pos = await hw_api.current_position_ot3(mount) - await hw_api.move_to( - mount, - Point( - dial_pos[OT3Axis.X], - dial_pos[OT3Axis.Y], - current_pos[OT3Axis.by_mount(mount)] - 3, - ), - ) - current_pos = await hw_api.current_position_ot3(mount) - await hw_api.move_to( - mount, - Point( - dial_pos[OT3Axis.X], - dial_pos[OT3Axis.Y], - current_pos[OT3Axis.by_mount(mount)], - ), - ) - tip_increment = 0 - tips = 8 - z_distance_press = 6 - for tip in range(1, tips + 1): - # Press onto the dial indicator - await hw_api.move_to( - mount, - Point( - dial_pos[OT3Axis.X], - dial_pos[OT3Axis.Y] - tip_increment, - dial_pos[OT3Axis.by_mount(mount)], - ), - ) - await asyncio.sleep(2) - elasped_time = (time.time() - start_time) / 60 - test_data["Time"] = round(elasped_time, 3) - test_data["Tip Height(mm)"] = gauge.read_stable(timeout=20) - test_data["Tip"] = tip - print(test_data) - d_str = f"{elasped_time}, {gauge.read_stable(timeout=20)}, {tip}\n" - data.append_data_to_file(test_n, test_f, d_str) - await asyncio.sleep(1) - # Retract from the dial indicator by 6mm - await hw_api.move_to( - mount, - Point( - dial_pos[OT3Axis.X], - dial_pos[OT3Axis.Y] - tip_increment, - dial_pos[OT3Axis.by_mount(mount)] + z_distance_press, - ), - ) - # backlash compensation - await hw_api.move_to( - mount, - Point( - dial_pos[OT3Axis.X], - dial_pos[OT3Axis.Y] - tip_increment, - dial_pos[OT3Axis.by_mount(mount)] + z_distance_press - 3, - ), - ) - # move to the next nozzle - tip_increment += 9 - current_pos = await hw_api.current_position_ot3(mount) - await hw_api.move_to( - mount, - Point( - dial_pos[OT3Axis.X], - dial_pos[OT3Axis.Y] - tip_increment, - current_pos[OT3Axis.by_mount(mount)], - ), - ) - - trash_loc = [432.7, 393.3, 100.0] - await hw_api.home_z(mount, allow_home_other=False) - current_pos = await hw_api.current_position_ot3(mount) - await hw_api.move_to( - mount, - Point( - trash_loc[0], trash_loc[1], current_pos[OT3Axis.by_mount(mount)] - ), - ) - await hw_api.move_to( - mount, Point(trash_loc[0], trash_loc[1], trash_loc[2]) - ) - await hw_api.drop_tip(mount) - await hw_api.home_z(mount, allow_home_other=False) - current_pos = await hw_api.current_position_ot3(mount) - # tip_column += 9 - await hw_api.move_to( - mount, - Point( - tiprack_loc[OT3Axis.X] + tip_column, - tiprack_loc[OT3Axis.Y], - current_pos[OT3Axis.by_mount(mount)], - ), - ) - elif args.test == "flatness": - home_position = await hw_api.current_position_ot3(mount) - await hw_api.move_to( - mount, - Point( - start_loc[OT3Axis.X], - start_loc[OT3Axis.Y], - home_pos[OT3Axis.by_mount(mount)], - ), - ) - # we don't need this, this is just for a placeholder - flatness = await _jog_axis(hw_api, home_position) - elif args.test == "flatness_with_move": - coordinates = [ - (515, 29, 294.16), - (465, 29, 294.16), - (415, 29, 294.16), - (317.23, 29, 294.16), - (317.23, 96.72, 294.16), - (326.73, 96.72, 294.16), - (326.73, 143.32, 294.16), - (415, 143.32, 294.16), - (465, 143.32, 294.16), - (515, 143.32, 294.16), - (317.24, 203.12, 294.16), - (326.24, 250.11, 294.16), - (415, 250.11, 294.16), - (465, 250.11, 294.16), - (515, 250.11, 294.16), - (317.22, 303.62, 294.16), - (326.25, 357.62, 294.16), - (415, 357.62, 294.16), - (465, 357.62, 294.16), - (515, 357.62, 294.16), - (317.22, 411.11, 294.16), - (256.22, 29, 294.16), - (268.28, 29, 294.16), - (218.28, 29, 294.16), - (154.29, 29, 294.16), - (154.29, 96.72, 294.16), - (162.29, 143.32, 294.16), - (218.29, 143.32, 294.16), - (268.29, 143.32, 294.16), - (154.8, 203.12, 294.16), - (162.3, 250.11, 294.16), - (218.3, 250.11, 294.16), - (268.3, 250.11, 294.16), - (154.81, 303.62, 294.16), - (162.31, 357.62, 294.16), - (218.3, 357.62, 294.16), - (268.31, 357.62, 294.16), - (154.86, 411.11, 294.16), - (-21.85, 29, 294.16), - (62.85, 29, 294.16), - (-21.85, 143.32, 294.16), - (62.85, 143.32, 294.16), - (-21.85, 250.11, 294.16), - (62.85, 250.11, 294.16), - ] - home_position = await hw_api.current_position_ot3(mount) - - array_num = 0 - for coord in coordinates: - await hw_api.move_to( - mount, - Point(coord[0], coord[1], home_pos[OT3Axis.by_mount(mount)]), - speed=60, - ) - await asyncio.sleep(1) - await hw_api.move_to( - mount, Point(coord[0], coord[1], coord[2]), speed=65 - ) - await asyncio.sleep(3) - gauge_reading = gauge.read_stable(timeout=20) - position = await hw_api.current_position_ot3(mount) - test_data["X-Coordinate"] = round(position[OT3Axis.X], 2) - test_data["Y-Coordinate"] = round(position[OT3Axis.Y], 2) - test_data["Z-Coordinate"] = round(position[OT3Axis.by_mount(mount)], 2) - test_data["Deck Height(mm)"] = gauge_reading - print(test_data) - d_str = f"{round(position[OT3Axis.X], 2)}, \ - {round(position[OT3Axis.Y], 2)}, \ - {round(position[OT3Axis.by_mount(mount)], 2)}, \ - {gauge.read_stable(timeout=20)}, {gauge_reading}\n" - data.append_data_to_file(test_n, test_f, d_str) - await hw_api.move_to( - mount, - Point(coord[0], coord[1], home_pos[OT3Axis.by_mount(mount)]), - speed=65, - ) - array_num += 1 - # we don't need this, this is just for a placeholder - flatness = await _jog_axis(hw_api, home_position) - await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) - except KeyboardInterrupt: - await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) - finally: - await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) - await hw_api.clean_up() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--simulate", action="store_true") - parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") - parser.add_argument( - "--test", - type=str, - help="pick_up_tip, tip_height_test, flatness, flatness_with_move", - default="pick_up_tip", - ) - parser.add_argument("--dial_indicator", action="store_true") - parser.add_argument("--flatness", action="store_true") - args = parser.parse_args() - if args.mount == "left": - mount = OT3Mount.LEFT - else: - mount = OT3Mount.RIGHT - xy_speed = 250 - speed_z = 60 - if args.dial_indicator: - if args.flatness: - test_data = { - "X-Coordinate": None, - "Y-Coordinate": None, - "Z-Coordinate": None, - "Deck Height(mm)": None, - } - else: - test_data = {"Time": None, "Tip Height(mm)": None, "Tip": None} - gauge = dial_indicator_setup() - test_n, test_f = file_setup(test_data) - pick_up_speed = 5 - press_distance = 15 - PIPETTE_SPEED = 10 - asyncio.run(_main()) From 65d498cec1d4b1a344b7060ce69612aa65560a5b Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Wed, 7 Jun 2023 12:15:26 -0400 Subject: [PATCH 30/53] add backlash test --- .../scripts/pipette_backlash_test.py | 329 ++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 hardware-testing/hardware_testing/scripts/pipette_backlash_test.py diff --git a/hardware-testing/hardware_testing/scripts/pipette_backlash_test.py b/hardware-testing/hardware_testing/scripts/pipette_backlash_test.py new file mode 100644 index 00000000000..c968f5c1f1d --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/pipette_backlash_test.py @@ -0,0 +1,329 @@ +"""Demo OT3 Gantry Functionality.""" +import argparse +import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os +import sys +import termios +import tty +import json + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + OT3Mount, + OT3Axis, + Point, + CriticalPoint, +) +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + move_plunger_absolute_ot3, + move_plunger_relative_ot3, + get_plunger_positions_ot3, + update_pick_up_current, + update_pick_up_distance, + update_drop_tip_current, + _get_pipette_from_mount, +) + +from hardware_testing import data +from hardware_testing.drivers.mark10 import Mark10 + + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + + +def file_setup(test_data, details): + today = datetime.date.today() + test_name = "{}-drop_tip-force-test-{}Amps".format( + details[0], # Pipette model + details[1], # Motor Current + ) + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[OT3Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") + return position + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + + + +async def pipette_jog(api, position, cp) -> Dict[OT3Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 5, 20] + step_length_index = 0 + step = step_size[step_length_index] + pipette_speed = 10 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "k": + sys.stdout.flush() + await move_plunger_relative_ot3(api, + mount, + step_size[step_length_index], + motor_current = 1.0, + speed=pipette_speed + ) + + elif input == "i": + sys.stdout.flush() + await move_plunger_relative_ot3(api, + mount, -step_size[step_length_index], + motor_current = 1.0, + speed=pipette_speed + ) + + elif input == "h": + sys.stdout.flush() + await api.home_plunger(mount) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= (len(step_size) -1): + step_length_index = len(step_size)-1 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.encoder_current_position_ot3( + mount = mount, critical_point=cp + ) + print("\r\n") + return position[OT3Axis.of_main_tool_actuator(mount)] + position = await api.encoder_current_position_ot3( + mount = mount, critical_point=cp + ) + switch_status = await api.get_limit_switches() + print( + "Coordinates: ", + round(position[OT3Axis.of_main_tool_actuator(mount)], 2), + ",", + "Switch Stats: ", + switch_status[OT3Axis.of_main_tool_actuator(mount)], + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + + +async def _main() -> None: + today = datetime.date.today() + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + pipette_model = hw_api.get_all_attached_instr()[mount]["pipette_id"] + pipette = _get_pipette_from_mount(hw_api, mount) + dial_data = {"Tip": None, "Tip Height": None, "Motor Current": None} + m_current = 0.2 + await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) + await hw_api.home_plunger(mount) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + home_position = await hw_api.current_position_ot3(mount) + encoder_positon = await hw_api.encoder_current_position_ot3(mount) + start_time = time.perf_counter() + + try: + + cp = CriticalPoint.NOZZLE + current_position = await hw_api.encoder_current_position_ot3(mount) + jog_loc = await pipette_jog(hw_api, current_position, cp) + await hw_api.move_to(mount, Point(220, + 150, + current_position[OT3Axis.by_mount(mount)])) + current_position = await hw_api.current_position_ot3(mount) + dial_loc = await jog(hw_api, current_position, cp) + current_position = await hw_api.encoder_current_position_ot3(mount) + jog_loc = await pipette_jog(hw_api, current_position, cp) + + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + args = parser.parse_args() + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + asyncio.run(_main()) From 8b71abc0f49601caee79d8dc8e4ba53d40d398ca Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Thu, 8 Jun 2023 12:14:45 -0400 Subject: [PATCH 31/53] update pick up speed --- .../opentrons_api/helpers_ot3.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py index 05d58a54a10..1f170394f89 100644 --- a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py +++ b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py @@ -394,15 +394,6 @@ async def update_pick_up_current( config_model.current = current pipette.pick_up_configurations = config_model -async def update_96_pick_up_current( - api: OT3API, mount: OT3Mount, current: float = 0.125 -) -> None: - """Update pick-up-tip current.""" - pipette = _get_pipette_from_mount(api, mount) - config_model = pipette.pick_up_configurations - config_model.pick_up_motor_actions = current - pipette.pick_up_configurations = config_model - async def update_drop_tip_current( api: OT3API, mount: OT3Mount, current: float = 1.0 ) -> None: @@ -422,6 +413,14 @@ async def update_pick_up_distance( config_model.distance = distance pipette.pick_up_configurations = config_model +async def update_pick_up_speed( + api: OT3API, mount: OT3Mount, speed: float = 5.0 +) -> None: + """Update pick-up-tip current.""" + pipette = _get_pipette_from_mount(api, mount) + config_model = pipette.pick_up_configurations + config_model.speed = speed + pipette.pick_up_configurations = config_model async def move_plunger_absolute_ot3( api: OT3API, From 7e475254c7f48a1898265394fb4f5e2e1dc3fb59 Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Thu, 8 Jun 2023 12:16:11 -0400 Subject: [PATCH 32/53] a few changes that fixes recent edge changes --- .../hardware_testing/scripts/single_channel_pick_up_tip.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/single_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/single_channel_pick_up_tip.py index c8d288ab205..f5999f8f8e8 100644 --- a/hardware-testing/hardware_testing/scripts/single_channel_pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/single_channel_pick_up_tip.py @@ -439,6 +439,8 @@ async def _main() -> None: expected_liquid_height=args.expected_liquid_height, log_pressure=args.log_pressure, aspirate_while_sensing=False, + auto_zero_sensor = False, + num_baseline_reads = 10, data_file=lp_file_name, ) tip_count = 0 @@ -839,9 +841,9 @@ def enc_record(f_name, t_data): parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C1") parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") - parser.add_argument("--fg", action="store_true", default=True) + parser.add_argument("--fg", action="store_true", default=False) parser.add_argument("--dial_indicator", action="store_true") - parser.add_argument("--tip_size", type=str, default="T50", help="Tip Size") + parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") parser.add_argument("--max_z_distance", type=float, default=40) parser.add_argument("--min_z_distance", type=float, default=5) parser.add_argument("--mount_speed", type=float, default=11) From 7935276da52c844f96746d822dcd7417f68df4bb Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Thu, 15 Jun 2023 10:44:46 -0400 Subject: [PATCH 33/53] 96 channel test script --- .../scripts/96_channel_pick_up_tip.py | 102 ++++++++---------- 1 file changed, 43 insertions(+), 59 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py index c4cde6b0b54..fb30c04b91f 100644 --- a/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py @@ -24,10 +24,11 @@ build_async_ot3_hardware_api, home_ot3, move_plunger_absolute_ot3, - move_plunger_relative_ot3, get_plunger_positions_ot3, update_pick_up_current, + update_pick_up_speed, update_pick_up_distance, + update_drop_tip_current, _get_pipette_from_mount, ) @@ -258,7 +259,7 @@ async def calibrate_tiprack(api, home_position, mount): drop_tip_loc = Point(drop_tip_loc[OT3Axis.X], drop_tip_loc[OT3Axis.Y], drop_tip_loc[OT3Axis.by_mount(mount)]) - await api.drop_tip(mount) + #await api.drop_tip(mount) return tiprack_loc, drop_tip_loc async def _main() -> None: @@ -268,31 +269,10 @@ async def _main() -> None: is_simulating=args.simulate, use_defaults=True ) pipette_model = hw_api.get_all_attached_instr()[OT3Mount.LEFT]["pipette_id"] - # dial_data = { - # "Ch1": None, "Ch2": None, "Ch3": None, "Ch4": None, "Ch5": None, "Ch6": None, - # "Ch7": None, "Ch8": None, "Ch9": None, "Ch10": None, "Ch11": None, "Ch12": None, - # "Ch13": None, "Ch14": None, "Ch15": None, "Ch16": None, "Ch17": None, "Ch18": None, - # "Ch19": None, "Ch20": None, "Ch21": None, "Ch22": None, "Ch23": None, "Ch24": None, - # "Ch25": None, "Ch26": None, "Ch27": None, "Ch28": None, "Ch29": None, "Ch30": None, - # "Ch31": None, "Ch32": None, "Ch33": None, "Ch34": None, "Ch35": None, "Ch36": None, - # "Ch37": None, "Ch38": None, "Ch39": None, "Ch40": None, "Ch41": None, "Ch42": None, - # "Ch43": None, "Ch44": None, "Ch45": None, "Ch46": None, "Ch47": None, "Ch48": None, - # "Ch49": None, "Ch50": None, "Ch51": None, "Ch52": None, "Ch53": None, "Ch54": None, - # "Ch55": None, "Ch56": None, "Ch57": None, "Ch58": None, "Ch59": None, "Ch60": None, - # "Ch61": None, "Ch62": None, "Ch63": None, "Ch64": None, "Ch65": None, "Ch66": None, - # "Ch67": None, "Ch68": None, "Ch69": None, "Ch70": None, "Ch71": None, "Ch72": None, - # "Ch73": None, "Ch74": None, "Ch75": None, "Ch76": None, "Ch77": None, "Ch78": None, - # "Ch79": None, "Ch80": None, "Ch81": None, "Ch82": None, "Ch83": None, "Ch84": None, - # "Ch85": None, "Ch86": None, "Ch87": None, "Ch88": None, "Ch89": None, "Ch90": None, - # "Ch91": None, "Ch92": None, "Ch93": None, "Ch94": None, "Ch95": None, "Ch96": None, - # "Motor Current": None, - # "Trial": None, - # "pipette_model": None, - # } dial_data = {"Column_1": None, "Column_2": None, "Column_3": None, "Column_4": None, "Column_5": None, "Column_6": None, "Column_7": None, "Column_8": None, "Column_9": None, "Column_10": None, "Column_11": None, "Column_12": None} - # m_current = float(input("motor_current in amps: ")) + m_current = float(input("motor_current in amps: ")) # pick_up_speed = float(input("pick up tip speed in mm/s: ")) details = [pipette_model, m_current] test_n, test_f = file_setup(dial_data, details) @@ -313,22 +293,17 @@ async def _main() -> None: start_time = time.perf_counter() m_current = float(input("motor_current in amps: ")) pick_up_speed = float(input("pick up tip speed in mm/s: ")) + await update_pick_up_current(hw_api, mount, m_current) + await update_pick_up_speed(hw_api, mount, pick_up_speed) + await update_pick_up_distance(hw_api, mount, 16.5) # Calibrate to tiprack if args.tiprack: pickup_loc, droptip_loc = await calibrate_tiprack(hw_api, home_position, mount) - # Let's pick up the same tips and move to up. - cp = CriticalPoint.NOZZLE - await move_to_point(hw_api, mount, pickup_loc, cp) - await hw_api.pick_up_tip( - mount, tip_length=tip_length[args.tip_size]) await hw_api.home_z(mount) cp = CriticalPoint.TIP home_w_tip = await hw_api.current_position_ot3(mount, cp) # Calibrate Dial Indicator with single tip if args.dial_indicator: - tip_count = 0 - x_offset = 0 - y_offset = 0 cp = CriticalPoint.TIP dial_loc = Point( slot_loc["C2"][0], @@ -343,42 +318,51 @@ async def _main() -> None: dial_loc[OT3Axis.by_mount(mount)]) try: + tip_count = 0 + x_offset = 0 + y_offset = 0 while True: measurements = [] - for tip in range(1, tips_to_use + 1): - tip_count += 1 - x_offset += 9 - if tip_count % 8 == 0: - y_offset = 0 - if tip_count % 8 == 0: - x_offset -= 9 - await asyncio.sleep(1) - tip_measurement = gauge.read() - print(f"Tip_attachment(mm): {tip_measurement}") - tip_position = Point(dial_loc[0] + x_offset, - dial_loc[1] + y_offset, - dial_loc[2]) - measurements.append(tip_measurement) - if tip_count % 12 == 0: - d_str = '' - for m in measurements: - d_str += str(m) + ',' - d_str = d_str[:-1] - data.append_data_to_file(test_n, test_f, d_str) - # Reset Measurements list - measurements = [] - await move_to_point(hw_api, mount, tip_position, cp) - + tip_count = 0 cp = CriticalPoint.TIP + # for tip in range(1, tips_to_use + 1): + # cp = CriticalPoint.TIP + # tip_count += 1 + # x_offset -= 9 + # if tip_count % 12 == 0: + # y_offset += 9 + # if tip_count % 12 == 0: + # x_offset = 0 + # await asyncio.sleep(1) + # tip_measurement = gauge.read() + # print("tip-",tip_count, "(mm): " ,tip_measurement, end="") + # print("\r", end="") + # tip_position = Point(dial_loc[0] + x_offset, + # dial_loc[1] + y_offset, + # dial_loc[2]) + # measurements.append(tip_measurement) + # if tip_count % 12 == 0: + # d_str = '' + # for m in measurements: + # d_str += str(m) + ',' + # d_str = d_str[:-1] + '\n' + # print(f"{d_str}") + # data.append_data_to_file(test_n, test_f, d_str) + # # Reset Measurements list + # measurements = [] + # print("\r\n") + # await move_to_point(hw_api, mount, tip_position, cp) await move_to_point(hw_api, mount, droptip_loc, cp) await hw_api.drop_tip(mount) await hw_api.home_z(mount) - # m_current = float(input("motor_current in amps: ")) - # pick_up_speed = float(input("pick up tip speed in mm/s: ")) + m_current = float(input("motor_current in amps: ")) + pick_up_speed = float(input("pick up tip speed in mm/s: ")) + await update_pick_up_current(hw_api, mount, m_current) + await update_pick_up_speed(hw_api, mount, pick_up_speed) + await update_pick_up_distance(hw_api, mount, 16.5) cp = CriticalPoint.NOZZLE await move_to_point(hw_api, mount, pickup_loc, cp) await hw_api.pick_up_tip(mount, tip_length=tip_length[args.tip_size]) - # await hw_api.home_z(mount) except KeyboardInterrupt: await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y]) From 99a905df93db9a269becae220388d5a56cd187ae Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Thu, 15 Jun 2023 10:45:47 -0400 Subject: [PATCH 34/53] production and tip length testing --- .../scripts/pipette_tip_length_test.py | 12 - .../pipette_tip_length_test_production.py | 390 ++++++++++++++++++ 2 files changed, 390 insertions(+), 12 deletions(-) create mode 100644 hardware-testing/hardware_testing/scripts/pipette_tip_length_test_production.py diff --git a/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py b/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py index ca892571f22..674c286c923 100644 --- a/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py +++ b/hardware-testing/hardware_testing/scripts/pipette_tip_length_test.py @@ -31,21 +31,9 @@ _get_pipette_from_mount, ) -from opentrons.config.types import LiquidProbeSettings - from hardware_testing import data -from hardware_testing.drivers.mark10 import Mark10 from hardware_testing.drivers import mitutoyo_digimatic_indicator -aspirate_depth = 7 -dispense_depth = 3 -liquid_retract_dist = 12 -liquid_retract_speed = 5 -retract_dist = 100 -retract_speed = 60 - -leak_test_time = 30 - def dict_keys_to_line(dict): return str.join(",", list(dict.keys())) + "\n" diff --git a/hardware-testing/hardware_testing/scripts/pipette_tip_length_test_production.py b/hardware-testing/hardware_testing/scripts/pipette_tip_length_test_production.py new file mode 100644 index 00000000000..3319fca1191 --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/pipette_tip_length_test_production.py @@ -0,0 +1,390 @@ +"""Demo OT3 Gantry Functionality.""" +import argparse +import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os +import sys +import termios +import tty +import json + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + OT3Mount, + OT3Axis, + Point, + CriticalPoint, +) +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + get_plunger_positions_ot3, +) + +from hardware_testing import data +from hardware_testing.drivers import mitutoyo_digimatic_indicator + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + + +def file_setup(test_data, details): + today = datetime.date.today() + test_name = "{}-tip_length-test".format( + details[0], # Pipette id + ) + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + + +def dial_indicator_setup(): + gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator( + port="/dev/ttyUSB0" + ) + gauge.connect() + return gauge + + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[OT3Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") + return position + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + +async def move_to_point(api, mount, point, cp): + home_pos = api.get_instrument_max_height(mount, cp) + pos = await api.current_position_ot3(mount, refresh=True, critical_point = cp) + await api.move_to(mount, + Point(pos[OT3Axis.X], + pos[OT3Axis.Y], + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + point.z)) + +async def _main() -> None: + today = datetime.date.today() + # tips_to_use = 96 + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.92, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + tip_overlap = 10.5 + tip_length = {"T1K": 95.6-tip_overlap, "T200": 58.35-tip_overlap, "T50": 57.9-tip_overlap} + dial_data = { + "Pipette id": None, + "Noz_Height(mm)": None, + "Tip_Height(mm)": None, + "Tip_Overlap(mm)": None, + "Noz_dial(mm)": None, + "tip_dial(mm)": None, + "tip_attached": None, + "Tip_Count": None, + "Tip_Length(mm)": None} + await hw_api.home() + await hw_api.cache_instruments() + await hw_api.home_plunger(mount) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + home_position = await hw_api.current_position_ot3(mount) + start_time = time.time() + nozzle_home_pos = hw_api.get_instrument_max_height(mount, CriticalPoint.NOZZLE) + tip_home_pos = hw_api.get_instrument_max_height(mount, CriticalPoint.TIP) + pipette_id = hw_api._pipette_handler.hardware_instruments[OT3Mount.LEFT]._pipette_id + details = [pipette_id] + test_n, test_f = file_setup(dial_data, details) + + if args.dial_indicator: + cp = CriticalPoint.NOZZLE + dial_point = Point(slot_loc[args.dial_slot][0], + slot_loc[args.dial_slot][1], + nozzle_home_pos - 100) + await move_to_point(hw_api, mount, dial_point, cp) + print("Moved to Dial Indicator") + current_position = await hw_api.current_position_ot3(mount) + nozzle_dial_loc = await jog(hw_api, current_position, cp) + await asyncio.sleep(1) + nozzle_measurement = gauge.read() + noz_loc = await hw_api.encoder_current_position_ot3(mount = mount, critical_point = cp) + noz_loc = noz_loc[OT3Axis.by_mount(mount)] + await asyncio.sleep(2) + nozzle_dial_point = Point(nozzle_dial_loc[OT3Axis.X], + nozzle_dial_loc[OT3Axis.Y], + nozzle_dial_loc[OT3Axis.by_mount(mount)]) + + if args.tiprack: + cp = CriticalPoint.NOZZLE + tiprack_point = Point(slot_loc[args.tiprack_slot][0], + slot_loc[args.tiprack_slot][1], + nozzle_home_pos-100) + await move_to_point(hw_api, mount, tiprack_point, cp) + print("Move to Tiprack") + current_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await jog(hw_api, current_position, cp) + # Move pipette to Force Gauge press location + await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + location = "Tiprack" + tip_count = 1 + cp = CriticalPoint.TIP + tiprack_loc = Point( + tiprack_loc[OT3Axis.X], + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)]) + await move_to_point(hw_api, mount, nozzle_dial_point, cp) + tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + tip_loc = tip_loc[OT3Axis.by_mount(mount)] + await asyncio.sleep(3) + tip_measurement = gauge.read() + await asyncio.sleep(1) + measured_tip_overlap = (tip_length[args.tip_size]+tip_overlap) - (tip_loc - noz_loc) # tiplength - tip overlap. + tip_attached = nozzle_measurement - tip_measurement + measured_tip_overlap = tip_attached + measured_tip_overlap + print(f"Tip_Overlap: {measured_tip_overlap}") + d_str = f"{pipette_id}, {noz_loc},{tip_loc}, {measured_tip_overlap}, {nozzle_measurement}, {tip_measurement}, {tip_attached}, {tip_count}, {tip_length[args.tip_size] + tip_overlap}, Nozzle \n" + data.append_data_to_file(test_n, test_f, d_str) + + # Move to trash slot + cp = CriticalPoint.TIP + trash = Point(slot_loc["A3"][0]+50, slot_loc["A3"][1]-20, tip_home_pos - 150) + await move_to_point(hw_api, mount, trash, cp) + await hw_api.drop_tip(mount) + tip_count = 1 + x_offset = 0 + y_offset = 0 + try: + for tip in range(2, args.tips_to_use + 1): + y_offset -= 9 + print("tip_count: ", tip_count) + print("y_offset: ",y_offset) + if tip_count % 8 == 0: + y_offset = 0 + if tip_count % 8 == 0: + x_offset += 9 + cp = CriticalPoint.NOZZLE + await move_to_point(hw_api, mount, nozzle_dial_point, cp) + noz_loc = await hw_api.encoder_current_position_ot3(mount, cp) + noz_loc = noz_loc[OT3Axis.by_mount(mount)] + await asyncio.sleep(3) + nozzle_measurement = gauge.read() + await asyncio.sleep(1) + tip_location = Point(tiprack_loc.x + x_offset, + tiprack_loc.y + y_offset, + tiprack_loc.z) + await move_to_point(hw_api, mount, tip_location, cp) + location = "Tiprack" + await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + cp = CriticalPoint.TIP + await move_to_point(hw_api, + mount, + nozzle_dial_point, + cp) + await asyncio.sleep(3) + tip_measurement = gauge.read() + await asyncio.sleep(1) + tip_loc = await hw_api.encoder_current_position_ot3(mount = mount, + critical_point = CriticalPoint.NOZZLE) + tip_loc = tip_loc[OT3Axis.by_mount(mount)] + measured_tip_overlap = (tip_length[args.tip_size]+tip_overlap) - (tip_loc - noz_loc) # tiplength - tip overlap. + tip_attached = (nozzle_measurement - tip_measurement) + measured_tip_overlap = tip_attached + measured_tip_overlap + print(f"Tip_Overlap: {measured_tip_overlap}") + d_str = f"{pipette_id}, {noz_loc},{tip_loc}, {measured_tip_overlap}, {nozzle_measurement}, {tip_measurement}, {tip_attached}, {tip_count}, {tip_length[args.tip_size] + tip_overlap}, Nozzle \n" + data.append_data_to_file(test_n, test_f, d_str) + # --------------------Drop Tip-------------------------------------- + current_position = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + await move_to_point(hw_api, mount, trash, cp) + await hw_api.drop_tip(mount) + tip_count += 1 + + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--tiprack", action="store_true") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C1") + parser.add_argument("--dial_indicator", action="store_true") + parser.add_argument("--tip_size", type=str, default="T200", help="Tip Size") + parser.add_argument("--tips_to_use", type=int, default=40) + args = parser.parse_args() + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + + if args.dial_indicator: + gauge = dial_indicator_setup() + asyncio.run(_main()) From 95fa2dd64f98dc3d387682dce99a08442b3633b4 Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Thu, 15 Jun 2023 10:46:18 -0400 Subject: [PATCH 35/53] work in progress multi tip length test --- .../scripts/multi_channel_tip_length_test.py | 434 ++++++++++++++++++ 1 file changed, 434 insertions(+) create mode 100644 hardware-testing/hardware_testing/scripts/multi_channel_tip_length_test.py diff --git a/hardware-testing/hardware_testing/scripts/multi_channel_tip_length_test.py b/hardware-testing/hardware_testing/scripts/multi_channel_tip_length_test.py new file mode 100644 index 00000000000..bcdd7fc9597 --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/multi_channel_tip_length_test.py @@ -0,0 +1,434 @@ +"""Demo OT3 Gantry Functionality.""" +import argparse +import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os +import sys +import termios +import tty +import json +import itertools + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + OT3Mount, + OT3Axis, + Point, + CriticalPoint, +) +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + get_plunger_positions_ot3, +) + +from hardware_testing import data +from hardware_testing.drivers import mitutoyo_digimatic_indicator + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + + +def file_setup(test_data, details): + today = datetime.date.today() + test_name = "{}-tip_length-test".format( + details[0], # Pipette id + ) + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + + +def dial_indicator_setup(): + gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator( + port="/dev/ttyUSB0" + ) + gauge.connect() + return gauge + + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[OT3Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") + return position + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + + print( + "Coordinates: ", + round(position[OT3Axis.X], 2), + ",", + round(position[OT3Axis.Y], 2), + ",", + round(position[OT3Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + +async def move_to_point(api, mount, point, cp): + home_pos = api.get_instrument_max_height(mount, cp) + pos = await api.current_position_ot3(mount, refresh=True, critical_point = cp) + await api.move_to(mount, + Point(pos[OT3Axis.X], + pos[OT3Axis.Y], + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + point.z)) + +def calculate_data(list1,list2): + # list_1 = (nozzle_measurement, encoder_measurement) + # list_2 = (tip_measurement, encoder_measurement) + tip_overlap = 10.5 + tip_overlaps = [] + for noz, tip in itertools.zip_longest(list1, list2): + tip_attached = noz[0] - tip[0] # tip_attached = nozzle_measurement - tip_measurement + measured_tip_overlap = \ + (tip_length[args.tip_size]+tip_overlap) - (tip_loc[1] - noz_loc[1]) + tip_overlap = measured_tip_overlap + tip_attached + print(f"Tip_Overlap: {tip_overlap}") + tip_overlaps.append(tip_overlap) + data.append_data_to_file(test_n, test_f, d_str) + +async def _main() -> None: + today = datetime.date.today() + # tips_to_use = 96 + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.92, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + tip_overlap = 10.5 + tip_length = {"T1K": 95.6-tip_overlap, "T200": 58.35-tip_overlap, "T50": 57.9-tip_overlap} + dial_data = { + "Pipette id": None, + "Noz_Height(mm)": None, + "Tip_Height(mm)": None, + "Tip_Overlap(mm)": None, + "Noz_dial(mm)": None, + "tip_dial(mm)": None, + "tip_attached": None, + "Tip_Count": None, + "Tip_Length(mm)": None} + await hw_api.home() + await hw_api.cache_instruments() + await hw_api.home_plunger(mount) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + home_position = await hw_api.current_position_ot3(mount) + start_time = time.time() + nozzle_home_pos = hw_api.get_instrument_max_height(mount, CriticalPoint.NOZZLE) + tip_home_pos = hw_api.get_instrument_max_height(mount, CriticalPoint.TIP) + pipette_id = hw_api._pipette_handler.hardware_instruments[OT3Mount.LEFT]._pipette_id + details = [pipette_id] + test_n, test_f = file_setup(dial_data, details) + nozzles = 8 + if args.dial_indicator: + nozzle_measurements = [] + cp = CriticalPoint.NOZZLE + dial_point = Point(slot_loc["D2"][0], + slot_loc["D2"][1], + nozzle_home_pos - 100) + await move_to_point(hw_api, mount, dial_point, cp) + print("Moved to Dial Indicator") + current_position = await hw_api.current_position_ot3(mount) + nozzle_dial_loc = await jog(hw_api, current_position, cp) + nozzle_dial_point = Point(nozzle_dial_loc[OT3Axis.X], + nozzle_dial_loc[OT3Axis.Y], + nozzle_dial_loc[OT3Axis.by_mount(mount)]) + y_offset = 0 + for noz in range(1, nozzles + 1): + y_offset -= 9 + await asyncio.sleep(1) + noz_loc = await hw_api.encoder_current_position_ot3(mount = mount, critical_point = cp) + noz_loc = noz_loc[OT3Axis.by_mount(mount)] + await asyncio.sleep(2) + nozzle_measurement = gauge.read() + await asyncio.sleep(1) + nozzle_measurements.append((nozzle_measurement, noz_loc)) + nozzle_dial_point = Point(nozzle_dial_loc[OT3Axis.X] + x_offset, + nozzle_dial_loc[OT3Axis.Y] + y_offset, + nozzle_dial_loc[OT3Axis.by_mount(mount)]) + await move_to_point(hw_api, mount, nozzle_dial_point, cp) + print(nozzle_measurements) + y_offset = 0 + + if args.tiprack: + cp = CriticalPoint.NOZZLE + tiprack_point = Point(slot_loc["C2"][0], + slot_loc["C2"][1], + nozzle_home_pos-100) + await move_to_point(hw_api, mount, tiprack_point, cp) + print("Move to Tiprack") + current_position = await hw_api.current_position_ot3(mount) + tiprack_loc = await jog(hw_api, current_position, cp) + # Move pipette to Force Gauge press location + await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + location = "Tiprack" + tip_count = 1 + cp = CriticalPoint.TIP + tip_measurements = [] + tiprack_loc = Point( + tiprack_loc[OT3Axis.X], + tiprack_loc[OT3Axis.Y], + tiprack_loc[OT3Axis.by_mount(mount)]) + y_offset = 0 + for noz in range(1, nozzles + 1): + y_offset -= 9 + await asyncio.sleep(2) + tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) + tip_loc = tip_loc[OT3Axis.by_mount(mount)] + await asyncio.sleep(2) + tip_measurement = gauge.read() + await asyncio.sleep(1) + tip_measurements.append((tip_measurement, tip_loc)) + tip_dial_point = Point(nozzle_dial_loc[OT3Axis.X] + x_offset, + nozzle_dial_loc[OT3Axis.Y] + y_offset, + nozzle_dial_loc[OT3Axis.by_mount(mount)]) + await move_to_point(hw_api, mount, tip_dial_point, cp) + print(tip_measurements) + y_offset = 0 + # await move_to_point(hw_api, mount, nozzle_dial_point, cp) + + + # await asyncio.sleep(3) + # tip_measurement = gauge.read() + # await asyncio.sleep(1) + measured_tip_overlap = (tip_length[args.tip_size]+tip_overlap) - (tip_loc - noz_loc) # tiplength - tip overlap. + tip_attached = nozzle_measurement - tip_measurement + measured_tip_overlap = tip_attached + measured_tip_overlap + print(f"Tip_Overlap: {measured_tip_overlap}") + d_str = f"{pipette_id}, {noz_loc},{tip_loc}, {measured_tip_overlap}, {nozzle_measurement}, {tip_measurement}, {tip_attached}, {tip_count}, {tip_length[args.tip_size] + tip_overlap}, Nozzle \n" + data.append_data_to_file(test_n, test_f, d_str) + + # Move to trash slot + cp = CriticalPoint.TIP + trash = Point(slot_loc["A3"][0]+50, slot_loc["A3"][1]-20, tip_home_pos - 150) + await move_to_point(hw_api, mount, trash, cp) + await hw_api.drop_tip(mount) + tip_count = 1 + x_offset = 0 + y_offset = 0 + try: + for tip in range(2, args.tips_to_use + 1): + y_offset -= 9 + if tip_count % 8 == 0: + y_offset = 0 + if tip_count % 8 == 0: + x_offset += 9 + print("tip_count: ", tip_count) + print("x_offset: ",x_offset) + cp = CriticalPoint.NOZZLE + await move_to_point(hw_api, mount, nozzle_dial_point, cp) + noz_loc = await hw_api.encoder_current_position_ot3(mount, cp) + noz_loc = noz_loc[OT3Axis.by_mount(mount)] + await asyncio.sleep(3) + nozzle_measurement = gauge.read() + await asyncio.sleep(1) + tip_location = Point(tiprack_loc.x + x_offset, + tiprack_loc.y + y_offset, + tiprack_loc.z) + await move_to_point(hw_api, mount, tip_location, cp) + location = "Tiprack" + await hw_api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + cp = CriticalPoint.TIP + await move_to_point(hw_api, + mount, + nozzle_dial_point, + cp) + await asyncio.sleep(3) + tip_measurement = gauge.read() + await asyncio.sleep(1) + tip_loc = await hw_api.encoder_current_position_ot3(mount = mount, + critical_point = CriticalPoint.NOZZLE) + tip_loc = tip_loc[OT3Axis.by_mount(mount)] + measured_tip_overlap = (tip_length[args.tip_size]+tip_overlap) - (tip_loc - noz_loc) # tiplength - tip overlap. + tip_attached = (nozzle_measurement - tip_measurement) + measured_tip_overlap = tip_attached + measured_tip_overlap + print(f"Tip_Overlap: {measured_tip_overlap}") + d_str = f"{pipette_id}, {noz_loc},{tip_loc}, {measured_tip_overlap}, {nozzle_measurement}, {tip_measurement}, {tip_attached}, {tip_count}, {tip_length[args.tip_size] + tip_overlap}, Nozzle \n" + data.append_data_to_file(test_n, test_f, d_str) + # --------------------Drop Tip-------------------------------------- + current_position = await hw_api.current_position_ot3( + mount, critical_point=CriticalPoint.TIP + ) + await move_to_point(hw_api, mount, trash, cp) + await hw_api.drop_tip(mount) + tip_count += 8 + + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + except KeyboardInterrupt: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + finally: + await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.clean_up() + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--tiprack", action="store_true") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C1") + parser.add_argument("--dial_indicator", action="store_true") + parser.add_argument("--tip_size", type=str, default="T200", help="Tip Size") + parser.add_argument("--tips_to_use", type=int, default=40) + args = parser.parse_args() + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + + if args.dial_indicator: + gauge = dial_indicator_setup() + asyncio.run(_main()) From f31b575139e1aae7a7566b5b416b72bba7a83e10 Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Tue, 27 Jun 2023 12:35:26 -0400 Subject: [PATCH 36/53] change home command --- .../scripts/taylors_awesome_tip_straightness.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hardware-testing/hardware_testing/scripts/taylors_awesome_tip_straightness.py b/hardware-testing/hardware_testing/scripts/taylors_awesome_tip_straightness.py index b74355bcafe..e03ac908cc6 100644 --- a/hardware-testing/hardware_testing/scripts/taylors_awesome_tip_straightness.py +++ b/hardware-testing/hardware_testing/scripts/taylors_awesome_tip_straightness.py @@ -164,7 +164,8 @@ async def _main() -> None: ) # await set_default_current_settings(hw_api, load=None) await hw_api.cache_instruments() - await home_ot3(hw_api, [OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.home() + # await home_ot3(hw_api, [OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) await hw_api.home_plunger(mount) home_pos = await hw_api.current_position_ot3(mount) tip_length = {"T1K": 85.7, "T200": 48.35, "T50": 47.9} From d0015665d2dfab2c2b74b81f78caf5b33757348f Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Thu, 6 Jul 2023 13:22:09 -0400 Subject: [PATCH 37/53] new changes to test script --- .../scripts/multi_channel_pick_up_tip.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py index 0ae56fe4b4d..79275e6ffe0 100644 --- a/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py @@ -238,10 +238,14 @@ async def _main() -> None: "D3": (341.03, 75.5, 110), } hw_api = await build_async_ot3_hardware_api( - is_simulating=args.simulate, use_defaults=True + is_simulating=args.simulate, use_defaults=True, + stall_detection_enable = False ) + await hw_api.home() + await hw_api.home_plunger(mount) + await hw_api.cache_instruments() tip_length = {"T1K": 85.7, "T200": 48.35, "T50": 47.9} - pipette_model = hw_api.get_all_attached_instr()[OT3Mount.RIGHT]["pipette_id"] + pipette_model = hw_api.get_all_attached_instr()[mount]["pipette_id"] dial_data = { "Ch1": None, "Ch2": None, @@ -259,15 +263,14 @@ async def _main() -> None: pick_up_speed = float(input("pick up tip speed in mm/s: ")) details = [pipette_model, m_current] test_n, test_f = file_setup(dial_data, details) - file_name = "/home/root/.opentrons/testing_data/enc_data/enc_pu_test_%s-%s.csv" % ( + file_name = "/data/testing_data/enc_data/enc_pu_test_%s-%s.csv" % ( m_current, datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), ) print(file_name) print(test_n) print(test_f) - await home_ot3(hw_api, [OT3Axis.Z_L, OT3Axis.Z_R, OT3Axis.X, OT3Axis.Y]) - await hw_api.home_plunger(mount) + await hw_api.set_lights(rails=True) plunger_pos = get_plunger_positions_ot3(hw_api, mount) home_position = await hw_api.current_position_ot3(mount) @@ -475,7 +478,7 @@ async def _main() -> None: expected_liquid_height=args.expected_liquid_height, log_pressure=args.log_pressure, aspirate_while_sensing=False, - auto_zero_sensor=True, + auto_zero_sensor=False, num_baseline_reads=10, data_file=lp_file_name, ) @@ -898,9 +901,9 @@ def enc_record(f_name, t_data): parser.add_argument("--max_z_distance", type=float, default=40) parser.add_argument("--min_z_distance", type=float, default=5) parser.add_argument("--mount_speed", type=float, default=5) - parser.add_argument("--plunger_speed", type=float, default=7) + parser.add_argument("--plunger_speed", type=float, default=11) parser.add_argument( - "--sensor_threshold", type=float, default=100, help="Threshold in Pascals" + "--sensor_threshold", type=float, default=50, help="Threshold in Pascals" ) parser.add_argument("--expected_liquid_height", type=int, default=0) parser.add_argument("--log_pressure", action="store_true") From 59f100bc7289b92b7e15922d10cc69d25d6c21a8 Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Tue, 18 Jul 2023 11:55:24 -0400 Subject: [PATCH 38/53] new changes to these test scripts --- .../scripts/96_channel_pick_up_tip.py | 2 +- .../scripts/multi_channel_pick_up_tip.py | 115 ++++++++++++++---- 2 files changed, 90 insertions(+), 27 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py index fb30c04b91f..5481241d72c 100644 --- a/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/96_channel_pick_up_tip.py @@ -46,7 +46,7 @@ retract_speed = 60 leak_test_time = 30 - +volume_test = 1000 def dict_keys_to_line(dict): return str.join(",", list(dict.keys())) + "\n" diff --git a/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py index 79275e6ffe0..06ff5f33fcf 100644 --- a/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py @@ -43,8 +43,11 @@ liquid_retract_speed = 5 retract_dist = 100 retract_speed = 60 +move_speed = 250 +z_speed = 80 leak_test_time = 30 +test_volume = 50 def dict_keys_to_line(dict): @@ -53,9 +56,10 @@ def dict_keys_to_line(dict): def file_setup(test_data, details): today = datetime.date.today() - test_name = "{}-pick_up-up-test-{}Amps".format( + test_name = "{}-pick_up-up-test-{}Amps_TP{}".format( details[0], # Pipette model details[1], # Motor Current + details[2], #Tip Size ) test_header = dict_keys_to_line(test_data) test_tag = "-{}".format(today.strftime("%b-%d-%Y")) @@ -261,7 +265,7 @@ async def _main() -> None: } m_current = float(input("motor_current in amps: ")) pick_up_speed = float(input("pick up tip speed in mm/s: ")) - details = [pipette_model, m_current] + details = [pipette_model, m_current, args.tip_size] test_n, test_f = file_setup(dial_data, details) file_name = "/data/testing_data/enc_data/enc_pu_test_%s-%s.csv" % ( m_current, @@ -293,6 +297,7 @@ async def _main() -> None: slot_loc["D2"][1], home_position[OT3Axis.by_mount(mount)], ), + speed = move_speed, ) current_position = await hw_api.current_position_ot3(mount) print("Move to Force Gauge") @@ -301,6 +306,7 @@ async def _main() -> None: await hw_api.move_to( mount, Point(fg_loc[0], fg_loc[1], home_position[OT3Axis.by_mount(mount)]), + speed = move_speed, critical_point=CriticalPoint.TIP, ) await hw_api.home_z(mount, allow_home_other=False) @@ -313,6 +319,7 @@ async def _main() -> None: slot_loc["B2"][1], home_position[OT3Axis.by_mount(mount)], ), + speed = move_speed ) cp = CriticalPoint.NOZZLE print("Move to Tiprack") @@ -328,6 +335,7 @@ async def _main() -> None: init_tip_loc = await hw_api.encoder_current_position_ot3( mount, CriticalPoint.NOZZLE ) + init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] await update_pick_up_current(hw_api, mount, m_current) await update_pickup_tip_speed(hw_api, mount, pick_up_speed) # Move pipette to Force Gauge press location @@ -345,6 +353,7 @@ async def _main() -> None: current_position[OT3Axis.Y], home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = move_speed, critical_point=CriticalPoint.TIP, ) @@ -377,6 +386,7 @@ async def _main() -> None: slot_loc["C2"][1], home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = move_speed ) cp = CriticalPoint.TIP print("Move to Tiprack") @@ -400,6 +410,7 @@ async def _main() -> None: dial_loc[1] + tip_offset, dial_loc[2], ), + speed = move_speed ) await asyncio.sleep(1) tip_measurement = gauge.read() @@ -413,6 +424,7 @@ async def _main() -> None: dial_loc[1] + tip_offset, home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = move_speed ) tip_offset += 9 await hw_api.move_to( @@ -422,6 +434,7 @@ async def _main() -> None: dial_loc[1] + tip_offset, home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = move_speed ) d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" data.append_data_to_file(test_n, test_f, d_str) @@ -434,6 +447,7 @@ async def _main() -> None: slot_loc["B3"][1], home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = move_speed ) cp = CriticalPoint.TIP print("Move to Trough") @@ -451,6 +465,7 @@ async def _main() -> None: trough_loc[1], home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = z_speed, critical_point=CriticalPoint.TIP, ) # Move to trash slot @@ -461,10 +476,32 @@ async def _main() -> None: slot_loc["A3"][1], home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = move_speed, critical_point=CriticalPoint.TIP, ) input("Feel the Tip!") + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1], + home_with_tip_position[OT3Axis.by_mount(mount)] - 150, + ), + speed = z_speed, + critical_point=CriticalPoint.TIP, + ) await hw_api.drop_tip(mount) + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1], + home_position[OT3Axis.by_mount(mount)], + ), + speed = z_speed, + critical_point=CriticalPoint.TIP, + ) + lp_file_name = "/var/pressure_sensor_data_P-{}_Z-{}-{}.csv".format( args.plunger_speed, args.mount_speed, today.strftime("%b-%d-%Y") @@ -495,7 +532,7 @@ async def _main() -> None: y_offset = 0 if tip_count % 8 == 0: x_offset += 9 - # #-----------------------Force Gauge----------------------------------- + # #-----------------------Force Gauge------------------------------- if args.fg: # Set Motor current by tester await hw_api.move_to( @@ -505,9 +542,10 @@ async def _main() -> None: fg_loc[1], home_position[OT3Axis.by_mount(mount)], ), + speed = move_speed ) # # Move pipette to Force Gauge calibrated location - await hw_api.move_to(mount, Point(fg_loc[0], fg_loc[1], fg_loc[2])) + await hw_api.move_to(mount, Point(fg_loc[0], fg_loc[1], fg_loc[2]), speed=move_speed) init_fg_loc = await hw_api.encoder_current_position_ot3( mount, CriticalPoint.NOZZLE ) @@ -542,15 +580,6 @@ async def _main() -> None: force_thread.join() # Thread Finished await hw_api.remove_tip(mount) await hw_api.home_z(mount, allow_home_other=False) - # await hw_api.move_to( - # mount, - # Point( - # fg_loc[0], - # fg_loc[1], - # home_with_tip_position[OT3Axis.by_mount(mount)], - # ), - # critical_point=CriticalPoint.TIP, - # ) await asyncio.sleep(2) final_fg_loc = await hw_api.encoder_current_position_ot3( mount, CriticalPoint.NOZZLE @@ -567,6 +596,7 @@ async def _main() -> None: tiprack_loc[1] + y_offset, home_position[OT3Axis.by_mount(mount)], ), + speed = move_speed ) # Move Pipette to top of Tip Rack Location @@ -577,7 +607,7 @@ async def _main() -> None: tiprack_loc[1] + y_offset, tiprack_loc[2], ), - speed=65, + speed=z_speed, ) location = "Tiprack" @@ -601,6 +631,7 @@ async def _main() -> None: tiprack_loc[1] + y_offset, home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = z_speed, critical_point=CriticalPoint.TIP, ) # home_with_tip_position = await hw_api.current_position_ot3( @@ -620,8 +651,8 @@ async def _main() -> None: ] enc_record(file_name, test_details) # Home Z - await hw_api.home([OT3Axis.by_mount(mount)]) - # --------------------------Dial Indicator----------------------- + #await hw_api.home([OT3Axis.by_mount(mount)]) + # --------------------------Dial Indicator---------------------- # Move over to the dial indicator await hw_api.move_to( mount, @@ -630,6 +661,7 @@ async def _main() -> None: dial_loc[1], home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = move_speed ) tip_offset = 0 measurements = "" @@ -639,6 +671,7 @@ async def _main() -> None: await hw_api.move_to( mount, Point(dial_loc[0], dial_loc[1] + tip_offset, dial_loc[2]), + speed = z_speed ) await asyncio.sleep(3) tip_measurement = gauge.read() @@ -652,6 +685,7 @@ async def _main() -> None: dial_loc[1] + tip_offset, home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = z_speed ) tip_offset += 9 await hw_api.move_to( @@ -661,10 +695,11 @@ async def _main() -> None: dial_loc[1] + tip_offset, home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = move_speed ) d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" data.append_data_to_file(test_n, test_f, d_str) - # -----------------------Aspirate----------------------------------- + # -----------------------Aspirate------------------------------- await hw_api.move_to( mount, Point( @@ -672,10 +707,12 @@ async def _main() -> None: trough_loc[1], home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = move_speed ) # Move to offset from trough await hw_api.move_to( - mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) + mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]), + speed = z_speed ) # Move the plunger to the top position await move_plunger_absolute_ot3(hw_api, mount, plunger_pos[0]) @@ -687,12 +724,13 @@ async def _main() -> None: liquid_height = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) - # await move_plunger_relative_ot3(hw_api, mount, 1.5, None, speed = 2) # P50S - await move_plunger_relative_ot3( - hw_api, mount, 0.25, None, speed=2 - ) # P1KS + await move_plunger_relative_ot3(hw_api, mount, 1.5, None, speed = 2) # P50S + # await move_plunger_relative_ot3( + # hw_api, mount, 0.25, None, speed=10 + # ) # P1KS await hw_api.move_to( - mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]) + mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]), + speed = z_speed ) # Prepare to aspirate before descending to trough well await hw_api.prepare_for_aspirate(mount) @@ -708,7 +746,7 @@ async def _main() -> None: critical_point=CriticalPoint.TIP, ) # Aspirate - await hw_api.aspirate(mount, volume=50) + await hw_api.aspirate(mount, volume=test_volume) cur_pos = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) @@ -727,6 +765,7 @@ async def _main() -> None: trough_loc[1], home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = z_speed, critical_point=CriticalPoint.TIP, ) await countdown(count_time=leak_test_time) @@ -734,6 +773,7 @@ async def _main() -> None: await hw_api.move_to( mount, Point(trough_loc[0], trough_loc[1], trough_loc[2]), + speed = z_speed, critical_point=CriticalPoint.TIP, ) await hw_api.move_to( @@ -748,7 +788,7 @@ async def _main() -> None: ) await hw_api.dispense(mount) await hw_api.blow_out(mount) - # --------------------Drop Tip-------------------------------------- + # --------------------Drop Tip---------------------------------- current_position = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) @@ -759,6 +799,7 @@ async def _main() -> None: trough_loc[1], home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = z_speed, critical_point=CriticalPoint.TIP, ) # Move to trash slot @@ -769,10 +810,32 @@ async def _main() -> None: slot_loc["A3"][1], home_with_tip_position[OT3Axis.by_mount(mount)], ), + speed = move_speed, + critical_point=CriticalPoint.TIP, + ) + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1], + home_with_tip_position[OT3Axis.by_mount(mount)] - 150, + ), + speed = z_speed, critical_point=CriticalPoint.TIP, ) await hw_api.drop_tip(mount) - + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1], + home_position[OT3Axis.by_mount(mount)], + ), + speed = z_speed, + critical_point=CriticalPoint.TIP, + ) await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) except KeyboardInterrupt: await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) From d6a90883a79a225006e02909c2c480181387cc50 Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Tue, 18 Jul 2023 12:56:41 -0400 Subject: [PATCH 39/53] revert changes to normal --- api/src/opentrons/hardware_control/ot3api.py | 164 ++++--------------- 1 file changed, 36 insertions(+), 128 deletions(-) diff --git a/api/src/opentrons/hardware_control/ot3api.py b/api/src/opentrons/hardware_control/ot3api.py index 7a93cf75721..d9a43dfa5c4 100644 --- a/api/src/opentrons/hardware_control/ot3api.py +++ b/api/src/opentrons/hardware_control/ot3api.py @@ -1613,7 +1613,7 @@ async def blow_out( async def _force_pick_up_tip( self, mount: OT3Mount, pipette_spec: PickUpTipSpec - ) -> Dict[OT3Axis, float]: + ) -> None: for press in pipette_spec.presses: async with self._backend.restore_current(): await self._backend.set_active_current( @@ -1629,11 +1629,7 @@ async def _force_pick_up_tip( target_up = target_position_from_relative( mount, press.relative_up, self._current_position ) - await asyncio.sleep(1) - enc_pos = await self.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) - print(enc_pos) await self._move(target_up) - return enc_pos async def _motor_pick_up_tip( self, mount: OT3Mount, pipette_spec: TipMotorPickUpTipSpec @@ -1671,7 +1667,7 @@ async def pick_up_tip( presses: Optional[int] = None, increment: Optional[float] = None, prep_after: bool = True, - ) -> Dict[OT3Axis, float]: + ) -> None: """Pick up tip from current location.""" realmount = OT3Mount.from_mount(mount) spec, _add_tip_to_instrs = self._pipette_handler.plan_check_pick_up_tip( @@ -1681,15 +1677,14 @@ async def pick_up_tip( await self._move_to_plunger_bottom(realmount, rate=1.0) if spec.pick_up_motor_actions: await self._motor_pick_up_tip(realmount, spec.pick_up_motor_actions) - enc_pos = None else: - enc_pos = await self._force_pick_up_tip(realmount, spec) + await self._force_pick_up_tip(realmount, spec) # neighboring tips tend to get stuck in the space between # the volume chamber and the drop-tip sleeve on p1000. # This extra shake ensures those tips are removed - # for rel_point, speed in spec.shake_off_list: - # await self.move_rel(realmount, rel_point, speed=speed) + for rel_point, speed in spec.shake_off_list: + await self.move_rel(realmount, rel_point, speed=speed) # TODO: implement tip-detection sequence during pick-up-tip for 96ch, # but not with DVT pipettes because those can only detect drops @@ -1701,7 +1696,6 @@ async def pick_up_tip( if prep_after: await self.prepare_for_aspirate(realmount) - return enc_pos def set_current_tiprack_diameter( self, mount: Union[top_types.Mount, OT3Mount], tiprack_diameter: float @@ -2007,13 +2001,16 @@ async def liquid_probe( probe_settings: Optional[LiquidProbeSettings] = None, ) -> float: """Search for and return liquid level height. + This function begins by moving the mount the distance specified by starting_mount_height in the LiquidProbeSettings. After this, the mount and plunger motors will move simultaneously while reading from the pressure sensor. + If the move is completed without the specified threshold being triggered, a LiquidNotFound error will be thrown. If the threshold is triggered before the minimum z distance has been traveled, a EarlyLiquidSenseTrigger error will be thrown. + Otherwise, the function will stop moving once the threshold is triggered, and return the position of the z axis in deck coordinates, as well as the encoder position, where @@ -2030,56 +2027,53 @@ async def liquid_probe( probe_settings = self.config.liquid_sense mount_axis = Axis.by_mount(mount) - # homes plunger then moves it to bottom of axis + gantry_position = await self.gantry_position(mount, refresh=True) + + await self.move_to( + mount, + top_types.Point( + x=gantry_position.x, + y=gantry_position.y, + z=probe_settings.starting_mount_height, + ), + ) + if probe_settings.aspirate_while_sensing: await self.home_plunger(mount) - # if not aspirate_while_sensing, plunger should be at bottom, since - # this expects to be called after pick_up_tip - plunger_direction = -1 if probe_settings.aspirate_while_sensing else 1 - # get position before moving in deck coordinates - starting_position = await self.current_position_ot3( - mount=mount, critical_point=CriticalPoint.TIP, refresh=True - ) - await self._backend.liquid_probe( + machine_pos_node_id = await self._backend.liquid_probe( mount, probe_settings.max_z_distance, probe_settings.mount_speed, (probe_settings.plunger_speed * plunger_direction), probe_settings.sensor_threshold_pascals, probe_settings.log_pressure, + probe_settings.auto_zero_sensor, + probe_settings.num_baseline_reads, ) - - # get final position in deck coordinates - final_position = await self.current_position_ot3( - mount=mount, critical_point=CriticalPoint.TIP, refresh=True - ) - + machine_pos = axis_convert(machine_pos_node_id, 0.0) + position = self._deck_from_machine(machine_pos) z_distance_traveled = ( - starting_position[mount_axis] - final_position[mount_axis] + position[mount_axis] - probe_settings.starting_mount_height ) if z_distance_traveled < probe_settings.min_z_distance: - min_z_travel_pos = final_position + min_z_travel_pos = position min_z_travel_pos[mount_axis] = probe_settings.min_z_distance raise EarlyLiquidSenseTrigger( - triggered_at=final_position, + triggered_at=position, min_z_pos=min_z_travel_pos, ) - elif z_distance_traveled >= probe_settings.max_z_distance: - max_z_travel_pos = final_position + elif z_distance_traveled > probe_settings.max_z_distance: + max_z_travel_pos = position max_z_travel_pos[mount_axis] = probe_settings.max_z_distance raise LiquidNotFound( - position=final_position, + position=position, max_z_pos=max_z_travel_pos, ) - encoder_pos = await self.encoder_current_position_ot3( - mount=mount, critical_point=CriticalPoint.TIP - ) - - return final_position, encoder_pos + return position[mount_axis] async def capacitive_probe( self, @@ -2090,16 +2084,21 @@ async def capacitive_probe( retract_after: bool = True, ) -> float: """Determine the position of something using the capacitive sensor. + This function orchestrates detecting the position of a collision between the capacitive probe on the tool on the specified mount, and some fixed element of the robot. + When calling this function, the mount's probe critical point should already be aligned in the probe axis with the item to be probed. + It will move the mount's probe critical point to a small distance behind the expected position of the element (which is target_pos, in deck coordinates, in the axis to be probed) while running the tool's capacitive sensor. When the sensor senses contact, the mount stops. + This function moves away and returns the sensed position. + This sensed position can be used in several ways, including - To get an absolute position in deck coordinates of whatever was targeted, if something was guaranteed to be physically present. @@ -2161,102 +2160,11 @@ async def capacitive_probe( await self.move_to(mount, pass_start_pos) return moving_axis.of_point(end_pos) - async def capacitive_nozzle( - self, - mount: OT3Mount, - moving_axis: OT3Axis, - target_pos: float, - direction: float, - pass_settings: CapacitivePassSettings, - probe_type: Optional[InstrumentProbeType] = InstrumentProbeType.PRIMARY, - ) -> float: - """Determine the position of something using the capacitive sensor. - - This function orchestrates detecting the position of a collision between the - capacitive probe on the tool on the specified mount, and some fixed element - of the robot. - - When calling this function, the mount's probe critical point should already - be aligned in the probe axis with the item to be probed. - - It will move the mount's probe critical point to a small distance behind - the expected position of the element (which is target_pos, in deck coordinates, - in the axis to be probed) while running the tool's capacitive sensor. When the - sensor senses contact, the mount stops. - - This function moves away and returns the sensed position. - - This sensed position can be used in several ways, including - - To get an absolute position in deck coordinates of whatever was - targeted, if something was guaranteed to be physically present. - - To detect whether a collision occured at all. If this function - returns a value far enough past the anticipated position, then it indicates - there was no material there. - """ - if moving_axis not in [ - OT3Axis.X, - OT3Axis.Y, - ] and moving_axis != OT3Axis.by_mount(mount): - raise RuntimeError( - "Probing must be done with a gantry axis or the mount of the sensing" - " tool" - ) - - here = await self.gantry_position(mount, refresh=True) - origin_pos = moving_axis.of_point(here) - # print(f"origin_pos: {origin_pos}") - # if origin_pos < target_pos: - # pass_start = target_pos - pass_settings.prep_distance_mm - # # print(f"pass_start: {pass_start}") - pass_distance = direction * ( - pass_settings.prep_distance_mm + pass_settings.max_overrun_distance_mm - ) - # else: - # pass_start = target_pos + pass_settings.prep_distance_mm - # # print(f"pass_start: {pass_start}") - # pass_distance = 1.0 * ( - # pass_settings.prep_distance_mm + pass_settings.max_overrun_distance_mm - # ) - machine_pass_distance = moving_axis.of_point( - machine_vector_from_deck_vector( - moving_axis.set_in_point(top_types.Point(0, 0, 0), pass_distance), - self._transforms.deck_calibration.attitude, - ) - ) - # print(f"machine_pass_distance: {machine_pass_distance}") - # pass_start_pos = moving_axis.set_in_point(here, pass_start) - # print(f"pass_start_pos: {pass_start_pos}") - # await self.move_to(mount, pass_start_pos) - if mount == OT3Mount.GRIPPER: - probe = self._gripper_handler.get_attached_probe() - assert probe - await self._backend.capacitive_probe( - mount, - moving_axis, - machine_pass_distance, - pass_settings.speed_mm_per_s, - pass_settings.sensor_threshold_pf, - GripperProbe.to_type(probe), - ) - else: - await self._backend.capacitive_probe( - mount, - moving_axis, - machine_pass_distance, - pass_settings.speed_mm_per_s, - pass_settings.sensor_threshold_pf, - probe=probe_type, - ) - end_pos = await self.gantry_position(mount, refresh=True) - # await self.move_to(mount, pass_start_pos) - return moving_axis.of_point(end_pos) - @contextlib.asynccontextmanager async def instrument_cache_lock(self) -> AsyncGenerator[None, None]: async with self._instrument_cache_lock: yield - async def capacitive_sweep( self, mount: OT3Mount, From b20197355519693f89e7b3b42596caa12d406930 Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Wed, 19 Jul 2023 13:57:41 -0400 Subject: [PATCH 40/53] 96 tip present test --- .../scripts/96_tip_present_test.py | 298 ++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 hardware-testing/hardware_testing/scripts/96_tip_present_test.py diff --git a/hardware-testing/hardware_testing/scripts/96_tip_present_test.py b/hardware-testing/hardware_testing/scripts/96_tip_present_test.py new file mode 100644 index 00000000000..0f7d88aff1f --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/96_tip_present_test.py @@ -0,0 +1,298 @@ +"""Demo OT3 Gantry Functionality.""" +import argparse +import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +import datetime +import os +import sys +import termios +import tty +import json + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + OT3Mount, + Axis, + Point, + CriticalPoint, +) + +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + move_plunger_absolute_ot3, + get_plunger_positions_ot3, + update_pick_up_current, + update_pick_up_speed, + update_pick_up_distance, + update_drop_tip_current, + _get_pipette_from_mount, +) + +from hardware_testing import data + +volume_test = 1000 + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + +def file_setup(test_data, details): + today = datetime.date.today() + test_name = "{}-pick_up-up-test-{}Amps".format( + details[0], # Pipette model + details[1], # Motor Current + ) + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, mount): + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + pickup_motor_speed = 1 + position = 0 + tip_state = 0 + motor_current = {Axis.Q: 1.5} + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "i": + sys.stdout.flush() + await api._motor_pickup_move( + mount, motor_current, step_size[step_length_index], speed=-pickup_motor_speed + ) + position -= step_size[step_length_index] + + elif input == "k": + sys.stdout.flush() + await api._motor_pickup_move( + mount, motor_current, step_size[step_length_index], speed=pickup_motor_speed + ) + position += step_size[step_length_index] + + elif input == "t": + sys.stdout.flush() + tip_state = await api.get_tip_presence(mount) + print(tip_state) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + print("\r\n") + return position + print( + "Coordinates: ", + round(position, 2), + ",", + " Motor Step: ", + step_size[step_length_index], + ",", + " Tip State: ", + tip_state, + end="", + ) + print("\r", end="") + +async def update_pickup_tip_speed(api, mount, speed) -> None: + """Update drop-tip current.""" + pipette = _get_pipette_from_mount(api, mount) + config_model = pipette.pick_up_configurations + config_model.speed = speed + pipette.pick_up_configurations = config_model + print(pipette.pick_up_configurations) + +async def move_to_point(api, mount, point, cp): + home_pos = api.get_instrument_max_height(mount, cp) + pos = await api.current_position_ot3(mount, refresh=True, critical_point = cp) + await api.move_to(mount, + Point(pos[Axis.X], + pos[Axis.Y], + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + point.z)) + +async def calibrate_tiprack(api, home_position, mount): + cp = CriticalPoint.NOZZLE + tiprack_loc = Point( + slot_loc["B2"][0], + slot_loc["B2"][1], + home_position[Axis.by_mount(mount)]) + print("Move to Tiprack") + await move_to_point(api, mount, tiprack_loc, cp) + current_position = await api.current_position_ot3(mount, cp) + tiprack_loc = await jog(api, current_position, cp) + tiprack_loc = Point(tiprack_loc[Axis.X], + tiprack_loc[Axis.Y], + tiprack_loc[Axis.by_mount(mount)]) + await api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size] + ) + await api.home_z(mount) + cp = CriticalPoint.TIP + home_with_tip = await api.current_position(mount, cp) + drop_tip_loc = await jog(api, home_with_tip, cp) + drop_tip_loc = Point(drop_tip_loc[Axis.X], + drop_tip_loc[Axis.Y], + drop_tip_loc[Axis.by_mount(mount)]) + #await api.drop_tip(mount) + return tiprack_loc, drop_tip_loc + +async def _main() -> None: + today = datetime.date.today() + tips_to_use = 96 + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True, + stall_detection_enable = False + ) + await hw_api.cache_instruments() + pipette_model = hw_api.get_all_attached_instr()[OT3Mount.LEFT]["pipette_id"] + dial_data = {"Column_1": None, "Column_2": None, "Column_3": None, "Column_4": None, "Column_5": None, "Column_6": None, + "Column_7": None, "Column_8": None, "Column_9": None, "Column_10": None, "Column_11": None, "Column_12": None} + # m_current = float(input("motor_current in amps: ")) + # details = [pipette_model, m_current] + # test_n, test_f = file_setup(dial_data, details) + # file_name = "/home/root/.opentrons/testing_data/pickup_tip_test/pu_96_pipette_%s-%s.csv" % ( + # m_current, + # datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + # ) + # print(file_name) + # print(test_n) + # print(test_f) + await hw_api.home() + await asyncio.sleep(1) + await hw_api.home_plunger(mount) + await hw_api.set_lights(rails=True) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + print(plunger_pos) + home_pos = await hw_api.current_position_ot3(mount) + start_time = time.perf_counter() + # m_current = float(input("motor_current in amps: ")) + # pick_up_speed = float(input("pick up tip speed in mm/s: ")) + # await update_pick_up_current(hw_api, mount, m_current) + # await update_pick_up_speed(hw_api, mount, pick_up_speed) + # await update_pick_up_distance(hw_api, mount, 16.5) + input("Press enter to pick up tips") + await hw_api.pick_up_tip(mount, 85.5) + home_pos = await hw_api.current_position_ot3(mount) + try: + await hw_api.move_to(mount, Point( + 150, + 150, + home_pos[Axis.by_mount(mount)])) + await jog(hw_api, mount) + await hw_api.drop_tip(mount) + + except KeyboardInterrupt: + await hw_api.disengage_axes([Axis.X, Axis.Y]) + finally: + await hw_api.disengage_axes([Axis.X, Axis.Y]) + await hw_api.clean_up() + + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--tiprack", action="store_true") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") + args = parser.parse_args() + slot_loc = { + "A1": (13.42, 394.92, 110), + "A2": (177.32, 394.92, 110), + "A3": (341.03, 394.0, 110), + "B1": (13.42, 288.42, 110), + "B2": (177.32, 288.92, 110), + "B3": (341.03, 288.92, 110), + "C1": (13.42, 181.92, 110), + "C2": (177.32, 181.92, 110), + "C3": (341.03, 181.92, 110), + "D1": (13.42, 75.5, 110), + "D2": (177.32, 75.5, 110), + "D3": (341.03, 75.5, 110), + } + tip_length = {"T1K": 95.7, "T200": 58.35, "T50": 57.5} + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + asyncio.run(_main()) From 3e7a9c30fb985d38667bf0df7ae23c771093a070 Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Wed, 19 Jul 2023 13:58:42 -0400 Subject: [PATCH 41/53] pickup motors move commands --- .../backends/ot3controller.py | 6 ++++++ api/src/opentrons/hardware_control/ot3api.py | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/api/src/opentrons/hardware_control/backends/ot3controller.py b/api/src/opentrons/hardware_control/backends/ot3controller.py index b1278b155e4..adad8a058f7 100644 --- a/api/src/opentrons/hardware_control/backends/ot3controller.py +++ b/api/src/opentrons/hardware_control/backends/ot3controller.py @@ -785,6 +785,12 @@ async def get_tip_present(self, mount: OT3Mount, tip_state: TipStateType) -> Non def _tip_motor_nodes(axis_current_keys: KeysView[Axis]) -> List[NodeId]: return [axis_to_node(Axis.Q)] if Axis.Q in axis_current_keys else [] + async def get_tip_present_state(self, mount: OT3Mount) -> TipStateType: + res = await get_tip_ejector_state( + self._messenger, sensor_node_for_mount(OT3Mount(mount.value)) # type: ignore + ) + return res + async def set_default_currents(self) -> None: """Set both run and hold currents from robot config to each node.""" assert self._current_settings, "Invalid current settings" diff --git a/api/src/opentrons/hardware_control/ot3api.py b/api/src/opentrons/hardware_control/ot3api.py index d9a43dfa5c4..7bf3c73e12d 100644 --- a/api/src/opentrons/hardware_control/ot3api.py +++ b/api/src/opentrons/hardware_control/ot3api.py @@ -945,6 +945,7 @@ async def move_axes( # noqa: C901 await self.refresh_positions() for axis in position.keys(): + print(axis) if not self._backend.axis_is_present(axis): raise AxisNotPresentError(f"{axis} is not present") @@ -1660,6 +1661,25 @@ async def _motor_pick_up_tip( "home", ) + async def _motor_pickup_move( + self, mount: OT3Mount, motor_current: Mapping[Axis, float], + move_distance: float, speed: float) -> None: + async with self._backend.restore_current(): + await self._backend.set_active_current( + {axis: current for axis, current in motor_current.items()} + ) + # perform pick up tip + await self._backend.tip_action( + [Axis.of_main_tool_actuator(mount)], + move_distance, + speed, + "clamp", + ) + + async def get_tip_presence(self, mount: Union[top_types.Mount, OT3Mount]): + state = await self._backend.get_tip_present_state(mount) + return state + async def pick_up_tip( self, mount: Union[top_types.Mount, OT3Mount], From b5bcc33d828f112b6e6125c6034acf1320775858 Mon Sep 17 00:00:00 2001 From: carlos-fernandez Date: Mon, 21 Aug 2023 15:30:00 -0400 Subject: [PATCH 42/53] a few new updates --- api/src/opentrons/hardware_control/ot3api.py | 19 +- .../scripts/multi_channel_pick_up_tip.py | 247 +++++++++--------- 2 files changed, 135 insertions(+), 131 deletions(-) diff --git a/api/src/opentrons/hardware_control/ot3api.py b/api/src/opentrons/hardware_control/ot3api.py index dcdcb275e0f..381104e946f 100644 --- a/api/src/opentrons/hardware_control/ot3api.py +++ b/api/src/opentrons/hardware_control/ot3api.py @@ -1720,7 +1720,7 @@ async def blow_out( async def _force_pick_up_tip( self, mount: OT3Mount, pipette_spec: PickUpTipSpec - ) -> None: + ) -> Dict[Axis, float]: for press in pipette_spec.presses: async with self._backend.restore_current(): await self._backend.set_active_current( @@ -1730,6 +1730,7 @@ async def _force_pick_up_tip( mount, press.relative_down, self._current_position ) await self._move(target_down, speed=press.speed, expect_stalls=True) + press_dist = await self.encoder_current_position_ot3(mount) # we expect a stall has happened during pick up, so we want to # update the motor estimation await self._update_position_estimation([Axis.by_mount(mount)]) @@ -1737,6 +1738,7 @@ async def _force_pick_up_tip( mount, press.relative_up, self._current_position ) await self._move(target_up) + return press_dist async def _motor_pick_up_tip( self, mount: OT3Mount, pipette_spec: TipMotorPickUpTipSpec @@ -1815,7 +1817,7 @@ async def pick_up_tip( presses: Optional[int] = None, increment: Optional[float] = None, prep_after: bool = True, - ) -> None: + ) -> Dict[Axis, float]: """Pick up tip from current location.""" realmount = OT3Mount.from_mount(mount) spec, _add_tip_to_instrs = self._pipette_handler.plan_check_pick_up_tip( @@ -1826,7 +1828,7 @@ async def pick_up_tip( if spec.pick_up_motor_actions: await self._motor_pick_up_tip(realmount, spec.pick_up_motor_actions) else: - await self._force_pick_up_tip(realmount, spec) + press_dist = await self._force_pick_up_tip(realmount, spec) # neighboring tips tend to get stuck in the space between # the volume chamber and the drop-tip sleeve on p1000. @@ -1837,16 +1839,17 @@ async def pick_up_tip( # TODO: implement tip-detection sequence during pick-up-tip for 96ch, # but not with DVT pipettes because those can only detect drops - if ( - self.gantry_load != GantryLoad.HIGH_THROUGHPUT - and ff.tip_presence_detection_enabled() - ): - await self._backend.get_tip_present(realmount, TipStateType.PRESENT) + # if ( + # self.gantry_load != GantryLoad.HIGH_THROUGHPUT + # and ff.tip_presence_detection_enabled() + # ): + # await self._backend.get_tip_present(realmount, TipStateType.PRESENT) _add_tip_to_instrs() if prep_after: await self.prepare_for_aspirate(realmount) + return press_dist def set_current_tiprack_diameter( self, mount: Union[top_types.Mount, OT3Mount], tiprack_diameter: float diff --git a/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py b/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py index 06ff5f33fcf..ab10df8f8a8 100644 --- a/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py +++ b/hardware-testing/hardware_testing/scripts/multi_channel_pick_up_tip.py @@ -16,7 +16,7 @@ from opentrons.hardware_control.motion_utilities import target_position_from_plunger from hardware_testing.opentrons_api.types import ( OT3Mount, - OT3Axis, + Axis, Point, CriticalPoint, ) @@ -97,7 +97,7 @@ def _getch(): return _getch() -async def jog(api, position, cp) -> Dict[OT3Axis, float]: +async def jog(api, position, cp) -> Dict[Axis, float]: step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] step_length_index = 3 step = step_size[step_length_index] @@ -190,11 +190,11 @@ async def jog(api, position, cp) -> Dict[OT3Axis, float]: print( "Coordinates: ", - round(position[OT3Axis.X], 2), + round(position[Axis.X], 2), ",", - round(position[OT3Axis.Y], 2), + round(position[Axis.Y], 2), ",", - round(position[OT3Axis.by_mount(mount)], 2), + round(position[Axis.by_mount(mount)], 2), " Motor Step: ", step_size[step_length_index], end="", @@ -245,9 +245,9 @@ async def _main() -> None: is_simulating=args.simulate, use_defaults=True, stall_detection_enable = False ) + await hw_api.cache_instruments() await hw_api.home() await hw_api.home_plunger(mount) - await hw_api.cache_instruments() tip_length = {"T1K": 85.7, "T200": 48.35, "T50": 47.9} pipette_model = hw_api.get_all_attached_instr()[mount]["pipette_id"] dial_data = { @@ -295,17 +295,17 @@ async def _main() -> None: Point( slot_loc["D2"][0], slot_loc["D2"][1], - home_position[OT3Axis.by_mount(mount)], + home_position[Axis.by_mount(mount)], ), speed = move_speed, ) current_position = await hw_api.current_position_ot3(mount) print("Move to Force Gauge") fg_loc = await jog(hw_api, current_position, cp) - fg_loc = [fg_loc[OT3Axis.X], fg_loc[OT3Axis.Y], fg_loc[OT3Axis.by_mount(mount)]] + fg_loc = [fg_loc[Axis.X], fg_loc[Axis.Y], fg_loc[Axis.by_mount(mount)]] await hw_api.move_to( mount, - Point(fg_loc[0], fg_loc[1], home_position[OT3Axis.by_mount(mount)]), + Point(fg_loc[0], fg_loc[1], home_position[Axis.by_mount(mount)]), speed = move_speed, critical_point=CriticalPoint.TIP, ) @@ -317,7 +317,7 @@ async def _main() -> None: Point( slot_loc["B2"][0], slot_loc["B2"][1], - home_position[OT3Axis.by_mount(mount)], + home_position[Axis.by_mount(mount)], ), speed = move_speed ) @@ -330,37 +330,37 @@ async def _main() -> None: mount, CriticalPoint.NOZZLE ) print(f"Start encoder: {init_tip_loc}") - init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + init_tip_loc = init_tip_loc[Axis.by_mount(mount)] encoder_position = init_tip_loc init_tip_loc = await hw_api.encoder_current_position_ot3( mount, CriticalPoint.NOZZLE ) - init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + init_tip_loc = init_tip_loc[Axis.by_mount(mount)] await update_pick_up_current(hw_api, mount, m_current) await update_pickup_tip_speed(hw_api, mount, pick_up_speed) # Move pipette to Force Gauge press location final_tip_loc = await hw_api.pick_up_tip( mount, tip_length=tip_length[args.tip_size] ) - await home_ot3(hw_api, [OT3Axis.by_mount(mount)]) + await home_ot3(hw_api, [Axis.by_mount(mount)]) home_with_tip_position = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) await hw_api.move_to( mount, Point( - current_position[OT3Axis.X], - current_position[OT3Axis.Y], - home_with_tip_position[OT3Axis.by_mount(mount)], + current_position[Axis.X], + current_position[Axis.Y], + home_with_tip_position[Axis.by_mount(mount)], ), speed = move_speed, critical_point=CriticalPoint.TIP, ) - encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] + encoder_end = final_tip_loc[Axis.by_mount(mount)] # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) print(f"End Encoder: {final_tip_loc}") - final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + final_tip_loc = final_tip_loc[Axis.by_mount(mount)] location = "Tiprack" tip_count = 1 test_details = [ @@ -373,9 +373,9 @@ async def _main() -> None: ] enc_record(file_name, test_details) tiprack_loc = [ - tiprack_loc[OT3Axis.X], - tiprack_loc[OT3Axis.Y], - tiprack_loc[OT3Axis.by_mount(mount)], + tiprack_loc[Axis.X], + tiprack_loc[Axis.Y], + tiprack_loc[Axis.by_mount(mount)], ] if args.dial_indicator: @@ -384,18 +384,18 @@ async def _main() -> None: Point( slot_loc["C2"][0], slot_loc["C2"][1], - home_with_tip_position[OT3Axis.by_mount(mount)], + home_with_tip_position[Axis.by_mount(mount)], ), speed = move_speed ) cp = CriticalPoint.TIP - print("Move to Tiprack") + print("Move to Dial Indicator") current_position = await hw_api.current_position_ot3(mount) dial_loc = await jog(hw_api, current_position, cp) dial_loc = [ - dial_loc[OT3Axis.X], - dial_loc[OT3Axis.Y], - dial_loc[OT3Axis.by_mount(mount)], + dial_loc[Axis.X], + dial_loc[Axis.Y], + dial_loc[Axis.by_mount(mount)], ] tip_offset = 0 measurements = "" @@ -422,7 +422,7 @@ async def _main() -> None: Point( dial_loc[0], dial_loc[1] + tip_offset, - home_with_tip_position[OT3Axis.by_mount(mount)], + home_with_tip_position[Axis.by_mount(mount)], ), speed = move_speed ) @@ -432,7 +432,7 @@ async def _main() -> None: Point( dial_loc[0], dial_loc[1] + tip_offset, - home_with_tip_position[OT3Axis.by_mount(mount)], + home_with_tip_position[Axis.by_mount(mount)], ), speed = move_speed ) @@ -445,7 +445,7 @@ async def _main() -> None: Point( slot_loc["B3"][0], slot_loc["B3"][1], - home_with_tip_position[OT3Axis.by_mount(mount)], + home_with_tip_position[Axis.by_mount(mount)], ), speed = move_speed ) @@ -454,53 +454,53 @@ async def _main() -> None: current_position = await hw_api.current_position_ot3(mount) trough_loc = await jog(hw_api, current_position, cp) trough_loc = [ - trough_loc[OT3Axis.X], - trough_loc[OT3Axis.Y], - trough_loc[OT3Axis.by_mount(mount)], + trough_loc[Axis.X], + trough_loc[Axis.Y], + trough_loc[Axis.by_mount(mount)], ] await hw_api.move_to( mount, Point( trough_loc[0], trough_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - speed = z_speed, - critical_point=CriticalPoint.TIP, - ) - # Move to trash slot - await hw_api.move_to( - mount, - Point( - slot_loc["A3"][0] + 50, - slot_loc["A3"][1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - speed = move_speed, - critical_point=CriticalPoint.TIP, - ) - input("Feel the Tip!") - await hw_api.move_to( - mount, - Point( - slot_loc["A3"][0] + 50, - slot_loc["A3"][1], - home_with_tip_position[OT3Axis.by_mount(mount)] - 150, - ), - speed = z_speed, - critical_point=CriticalPoint.TIP, - ) - await hw_api.drop_tip(mount) - await hw_api.move_to( - mount, - Point( - slot_loc["A3"][0] + 50, - slot_loc["A3"][1], - home_position[OT3Axis.by_mount(mount)], + home_with_tip_position[Axis.by_mount(mount)], ), speed = z_speed, critical_point=CriticalPoint.TIP, ) + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1], + home_with_tip_position[Axis.by_mount(mount)], + ), + speed = move_speed, + critical_point=CriticalPoint.TIP, + ) + input("Feel the Tip!") + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1], + home_with_tip_position[Axis.by_mount(mount)] - 150, + ), + speed = z_speed, + critical_point=CriticalPoint.TIP, + ) + await hw_api.drop_tip(mount) + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1], + home_position[Axis.by_mount(mount)], + ), + speed = z_speed, + critical_point=CriticalPoint.TIP, + ) lp_file_name = "/var/pressure_sensor_data_P-{}_Z-{}-{}.csv".format( @@ -540,7 +540,7 @@ async def _main() -> None: Point( fg_loc[0], fg_loc[1], - home_position[OT3Axis.by_mount(mount)], + home_position[Axis.by_mount(mount)], ), speed = move_speed ) @@ -549,7 +549,7 @@ async def _main() -> None: init_fg_loc = await hw_api.encoder_current_position_ot3( mount, CriticalPoint.NOZZLE ) - init_fg_loc = init_fg_loc[OT3Axis.by_mount(mount)] + init_fg_loc = init_fg_loc[Axis.by_mount(mount)] location = "Force_Gauge" force_thread = Thread( target=force_record, @@ -584,7 +584,7 @@ async def _main() -> None: final_fg_loc = await hw_api.encoder_current_position_ot3( mount, CriticalPoint.NOZZLE ) - final_fg_loc = final_fg_loc[OT3Axis.by_mount(mount)] + final_fg_loc = final_fg_loc[Axis.by_mount(mount)] # -----------------------Tiprack------------------------------------ if args.tiprack: @@ -594,7 +594,7 @@ async def _main() -> None: Point( tiprack_loc[0] + x_offset, tiprack_loc[1] + y_offset, - home_position[OT3Axis.by_mount(mount)], + home_position[Axis.by_mount(mount)], ), speed = move_speed ) @@ -616,7 +616,7 @@ async def _main() -> None: mount, CriticalPoint.NOZZLE ) print(f"Start encoder: {init_tip_loc}") - init_tip_loc = init_tip_loc[OT3Axis.by_mount(mount)] + init_tip_loc = init_tip_loc[Axis.by_mount(mount)] encoder_position = init_tip_loc # Press Pipette into the tip await update_pick_up_current(hw_api, mount, m_current) @@ -629,7 +629,7 @@ async def _main() -> None: Point( tiprack_loc[0] + x_offset, tiprack_loc[1] + y_offset, - home_with_tip_position[OT3Axis.by_mount(mount)], + home_with_tip_position[Axis.by_mount(mount)], ), speed = z_speed, critical_point=CriticalPoint.TIP, @@ -637,10 +637,10 @@ async def _main() -> None: # home_with_tip_position = await hw_api.current_position_ot3( # mount, critical_point=CriticalPoint.TIP # ) - encoder_end = final_tip_loc[OT3Axis.by_mount(mount)] + encoder_end = final_tip_loc[Axis.by_mount(mount)] # final_tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) print(f"End Encoder: {final_tip_loc}") - final_tip_loc = final_tip_loc[OT3Axis.by_mount(mount)] + final_tip_loc = final_tip_loc[Axis.by_mount(mount)] test_details = [ start_time, m_current, @@ -651,7 +651,7 @@ async def _main() -> None: ] enc_record(file_name, test_details) # Home Z - #await hw_api.home([OT3Axis.by_mount(mount)]) + #await hw_api.home([Axis.by_mount(mount)]) # --------------------------Dial Indicator---------------------- # Move over to the dial indicator await hw_api.move_to( @@ -659,7 +659,7 @@ async def _main() -> None: Point( dial_loc[0], dial_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)], + home_with_tip_position[Axis.by_mount(mount)], ), speed = move_speed ) @@ -683,7 +683,7 @@ async def _main() -> None: Point( dial_loc[0], dial_loc[1] + tip_offset, - home_with_tip_position[OT3Axis.by_mount(mount)], + home_with_tip_position[Axis.by_mount(mount)], ), speed = z_speed ) @@ -693,19 +693,20 @@ async def _main() -> None: Point( dial_loc[0], dial_loc[1] + tip_offset, - home_with_tip_position[OT3Axis.by_mount(mount)], + home_with_tip_position[Axis.by_mount(mount)], ), speed = move_speed ) d_str = f"{measurements} {pipette_model} , {trial}, {m_current} \n" data.append_data_to_file(test_n, test_f, d_str) + if args.trough: # -----------------------Aspirate------------------------------- await hw_api.move_to( mount, Point( trough_loc[0], trough_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)], + home_with_tip_position[Axis.by_mount(mount)], ), speed = move_speed ) @@ -738,9 +739,9 @@ async def _main() -> None: await hw_api.move_to( mount, Point( - liquid_height[OT3Axis.X], - liquid_height[OT3Axis.Y], - liquid_height[OT3Axis.by_mount(mount)] - aspirate_depth, + liquid_height[Axis.X], + liquid_height[Axis.Y], + liquid_height[Axis.by_mount(mount)] - aspirate_depth, ), speed=5, critical_point=CriticalPoint.TIP, @@ -750,7 +751,7 @@ async def _main() -> None: cur_pos = await hw_api.current_position_ot3( mount, critical_point=CriticalPoint.TIP ) - z_pos = cur_pos[OT3Axis.by_mount(mount)] + z_pos = cur_pos[Axis.by_mount(mount)] # Retract from liquid with retract speed await hw_api.move_to( mount, @@ -763,7 +764,7 @@ async def _main() -> None: Point( trough_loc[0], trough_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)], + home_with_tip_position[Axis.by_mount(mount)], ), speed = z_speed, critical_point=CriticalPoint.TIP, @@ -781,7 +782,7 @@ async def _main() -> None: Point( trough_loc[0], trough_loc[1], - liquid_height[OT3Axis.by_mount(mount)] - dispense_depth, + liquid_height[Axis.by_mount(mount)] - dispense_depth, ), speed=5, critical_point=CriticalPoint.TIP, @@ -797,50 +798,50 @@ async def _main() -> None: Point( trough_loc[0], trough_loc[1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - speed = z_speed, - critical_point=CriticalPoint.TIP, - ) - # Move to trash slot - await hw_api.move_to( - mount, - Point( - slot_loc["A3"][0] + 50, - slot_loc["A3"][1], - home_with_tip_position[OT3Axis.by_mount(mount)], - ), - speed = move_speed, - critical_point=CriticalPoint.TIP, - ) - # Move to trash slot - await hw_api.move_to( - mount, - Point( - slot_loc["A3"][0] + 50, - slot_loc["A3"][1], - home_with_tip_position[OT3Axis.by_mount(mount)] - 150, - ), - speed = z_speed, - critical_point=CriticalPoint.TIP, - ) - await hw_api.drop_tip(mount) - # Move to trash slot - await hw_api.move_to( - mount, - Point( - slot_loc["A3"][0] + 50, - slot_loc["A3"][1], - home_position[OT3Axis.by_mount(mount)], + home_with_tip_position[Axis.by_mount(mount)], ), speed = z_speed, critical_point=CriticalPoint.TIP, ) - await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1], + home_with_tip_position[Axis.by_mount(mount)], + ), + speed = move_speed, + critical_point=CriticalPoint.TIP, + ) + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1], + home_with_tip_position[Axis.by_mount(mount)] - 150, + ), + speed = z_speed, + critical_point=CriticalPoint.TIP, + ) + await hw_api.drop_tip(mount) + # Move to trash slot + await hw_api.move_to( + mount, + Point( + slot_loc["A3"][0] + 50, + slot_loc["A3"][1], + home_position[Axis.by_mount(mount)], + ), + speed = z_speed, + critical_point=CriticalPoint.TIP, + ) + await hw_api.disengage_axes([Axis.X, Axis.Y, Axis.Z_L, Axis.Z_R]) except KeyboardInterrupt: - await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.disengage_axes([Axis.X, Axis.Y, Axis.Z_L, Axis.Z_R]) finally: - await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.disengage_axes([Axis.X, Axis.Y, Axis.Z_L, Axis.Z_R]) await hw_api.clean_up() From 864e8044d1eb6841265e21d99d1c3bbe50098b17 Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 14 Dec 2023 13:05:33 -0500 Subject: [PATCH 43/53] ot3 axis to axis --- .../scripts/multi_channel_tip_length_test.py | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/multi_channel_tip_length_test.py b/hardware-testing/hardware_testing/scripts/multi_channel_tip_length_test.py index bcdd7fc9597..d9cb6618f1e 100644 --- a/hardware-testing/hardware_testing/scripts/multi_channel_tip_length_test.py +++ b/hardware-testing/hardware_testing/scripts/multi_channel_tip_length_test.py @@ -17,7 +17,7 @@ from opentrons.hardware_control.motion_utilities import target_position_from_plunger from hardware_testing.opentrons_api.types import ( OT3Mount, - OT3Axis, + Axis, Point, CriticalPoint, ) @@ -77,7 +77,7 @@ def _getch(): return _getch() -async def jog(api, position, cp) -> Dict[OT3Axis, float]: +async def jog(api, position, cp) -> Dict[Axis, float]: step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] step_length_index = 3 step = step_size[step_length_index] @@ -170,11 +170,11 @@ async def jog(api, position, cp) -> Dict[OT3Axis, float]: print( "Coordinates: ", - round(position[OT3Axis.X], 2), + round(position[Axis.X], 2), ",", - round(position[OT3Axis.Y], 2), + round(position[Axis.Y], 2), ",", - round(position[OT3Axis.by_mount(mount)], 2), + round(position[Axis.by_mount(mount)], 2), " Motor Step: ", step_size[step_length_index], end="", @@ -185,8 +185,8 @@ async def move_to_point(api, mount, point, cp): home_pos = api.get_instrument_max_height(mount, cp) pos = await api.current_position_ot3(mount, refresh=True, critical_point = cp) await api.move_to(mount, - Point(pos[OT3Axis.X], - pos[OT3Axis.Y], + Point(pos[Axis.X], + pos[Axis.Y], home_pos)) await api.move_to(mount, Point(point.x, @@ -232,7 +232,7 @@ async def _main() -> None: is_simulating=args.simulate, use_defaults=True ) tip_overlap = 10.5 - tip_length = {"T1K": 95.6-tip_overlap, "T200": 58.35-tip_overlap, "T50": 57.9-tip_overlap} + tip_length = {"T1K": 95.6, "T200": 58.35, "T50": 57.9} dial_data = { "Pipette id": None, "Noz_Height(mm)": None, @@ -265,22 +265,22 @@ async def _main() -> None: print("Moved to Dial Indicator") current_position = await hw_api.current_position_ot3(mount) nozzle_dial_loc = await jog(hw_api, current_position, cp) - nozzle_dial_point = Point(nozzle_dial_loc[OT3Axis.X], - nozzle_dial_loc[OT3Axis.Y], - nozzle_dial_loc[OT3Axis.by_mount(mount)]) + nozzle_dial_point = Point(nozzle_dial_loc[Axis.X], + nozzle_dial_loc[Axis.Y], + nozzle_dial_loc[Axis.by_mount(mount)]) y_offset = 0 for noz in range(1, nozzles + 1): y_offset -= 9 await asyncio.sleep(1) noz_loc = await hw_api.encoder_current_position_ot3(mount = mount, critical_point = cp) - noz_loc = noz_loc[OT3Axis.by_mount(mount)] + noz_loc = noz_loc[Axis.by_mount(mount)] await asyncio.sleep(2) nozzle_measurement = gauge.read() await asyncio.sleep(1) nozzle_measurements.append((nozzle_measurement, noz_loc)) - nozzle_dial_point = Point(nozzle_dial_loc[OT3Axis.X] + x_offset, - nozzle_dial_loc[OT3Axis.Y] + y_offset, - nozzle_dial_loc[OT3Axis.by_mount(mount)]) + nozzle_dial_point = Point(nozzle_dial_loc[Axis.X] + x_offset, + nozzle_dial_loc[Axis.Y] + y_offset, + nozzle_dial_loc[Axis.by_mount(mount)]) await move_to_point(hw_api, mount, nozzle_dial_point, cp) print(nozzle_measurements) y_offset = 0 @@ -303,22 +303,22 @@ async def _main() -> None: cp = CriticalPoint.TIP tip_measurements = [] tiprack_loc = Point( - tiprack_loc[OT3Axis.X], - tiprack_loc[OT3Axis.Y], - tiprack_loc[OT3Axis.by_mount(mount)]) + tiprack_loc[Axis.X], + tiprack_loc[Axis.Y], + tiprack_loc[Axis.by_mount(mount)]) y_offset = 0 for noz in range(1, nozzles + 1): y_offset -= 9 await asyncio.sleep(2) tip_loc = await hw_api.encoder_current_position_ot3(mount, CriticalPoint.NOZZLE) - tip_loc = tip_loc[OT3Axis.by_mount(mount)] + tip_loc = tip_loc[Axis.by_mount(mount)] await asyncio.sleep(2) tip_measurement = gauge.read() await asyncio.sleep(1) tip_measurements.append((tip_measurement, tip_loc)) - tip_dial_point = Point(nozzle_dial_loc[OT3Axis.X] + x_offset, - nozzle_dial_loc[OT3Axis.Y] + y_offset, - nozzle_dial_loc[OT3Axis.by_mount(mount)]) + tip_dial_point = Point(nozzle_dial_loc[Axis.X] + x_offset, + nozzle_dial_loc[Axis.Y] + y_offset, + nozzle_dial_loc[Axis.by_mount(mount)]) await move_to_point(hw_api, mount, tip_dial_point, cp) print(tip_measurements) y_offset = 0 @@ -328,7 +328,7 @@ async def _main() -> None: # await asyncio.sleep(3) # tip_measurement = gauge.read() # await asyncio.sleep(1) - measured_tip_overlap = (tip_length[args.tip_size]+tip_overlap) - (tip_loc - noz_loc) # tiplength - tip overlap. + measured_tip_overlap = (tip_length[args.tip_size]) - (tip_loc - noz_loc) # tiplength - tip overlap. tip_attached = nozzle_measurement - tip_measurement measured_tip_overlap = tip_attached + measured_tip_overlap print(f"Tip_Overlap: {measured_tip_overlap}") @@ -355,7 +355,7 @@ async def _main() -> None: cp = CriticalPoint.NOZZLE await move_to_point(hw_api, mount, nozzle_dial_point, cp) noz_loc = await hw_api.encoder_current_position_ot3(mount, cp) - noz_loc = noz_loc[OT3Axis.by_mount(mount)] + noz_loc = noz_loc[Axis.by_mount(mount)] await asyncio.sleep(3) nozzle_measurement = gauge.read() await asyncio.sleep(1) @@ -377,12 +377,14 @@ async def _main() -> None: await asyncio.sleep(1) tip_loc = await hw_api.encoder_current_position_ot3(mount = mount, critical_point = CriticalPoint.NOZZLE) - tip_loc = tip_loc[OT3Axis.by_mount(mount)] + tip_loc = tip_loc[Axis.by_mount(mount)] measured_tip_overlap = (tip_length[args.tip_size]+tip_overlap) - (tip_loc - noz_loc) # tiplength - tip overlap. tip_attached = (nozzle_measurement - tip_measurement) measured_tip_overlap = tip_attached + measured_tip_overlap print(f"Tip_Overlap: {measured_tip_overlap}") - d_str = f"{pipette_id}, {noz_loc},{tip_loc}, {measured_tip_overlap}, {nozzle_measurement}, {tip_measurement}, {tip_attached}, {tip_count}, {tip_length[args.tip_size] + tip_overlap}, Nozzle \n" + d_str = f"{pipette_id}, {noz_loc},{tip_loc}, {measured_tip_overlap}, \ + {nozzle_measurement}, {tip_measurement}, {tip_attached}, {tip_count}, \ + {tip_length[args.tip_size] + tip_overlap}, Nozzle \n" data.append_data_to_file(test_n, test_f, d_str) # --------------------Drop Tip-------------------------------------- current_position = await hw_api.current_position_ot3( @@ -392,11 +394,11 @@ async def _main() -> None: await hw_api.drop_tip(mount) tip_count += 8 - await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.disengage_axes([Axis.X, Axis.Y, Axis.Z_L, Axis.Z_R]) except KeyboardInterrupt: - await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.disengage_axes([Axis.X, Axis.Y, Axis.Z_L, Axis.Z_R]) finally: - await hw_api.disengage_axes([OT3Axis.X, OT3Axis.Y, OT3Axis.Z_L, OT3Axis.Z_R]) + await hw_api.disengage_axes([Axis.X, Axis.Y, Axis.Z_L, Axis.Z_R]) await hw_api.clean_up() if __name__ == "__main__": @@ -421,8 +423,8 @@ async def _main() -> None: parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C1") parser.add_argument("--dial_indicator", action="store_true") - parser.add_argument("--tip_size", type=str, default="T200", help="Tip Size") - parser.add_argument("--tips_to_use", type=int, default=40) + parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") + parser.add_argument("--tips_to_use", type=int, default=96) args = parser.parse_args() if args.mount == "left": mount = OT3Mount.LEFT From 25a57e69c3d7ff9bcf9fe1d82984622598b140dc Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 4 Jan 2024 14:25:57 -0500 Subject: [PATCH 44/53] some changes to these test scripts --- .../flex_iq_p1000_multi_200ul.py | 4 +- ...ex_p1000_multi_200ul_T1K_Utima_protocol.py | 398 ++++++++++++++++++ 2 files changed, 400 insertions(+), 2 deletions(-) create mode 100644 hardware-testing/hardware_testing/protocols/installation_qualification/flex_p1000_multi_200ul_T1K_Utima_protocol.py diff --git a/hardware-testing/hardware_testing/protocols/installation_qualification/flex_iq_p1000_multi_200ul.py b/hardware-testing/hardware_testing/protocols/installation_qualification/flex_iq_p1000_multi_200ul.py index 1adc97a6a28..3206420a29c 100644 --- a/hardware-testing/hardware_testing/protocols/installation_qualification/flex_iq_p1000_multi_200ul.py +++ b/hardware-testing/hardware_testing/protocols/installation_qualification/flex_iq_p1000_multi_200ul.py @@ -12,11 +12,11 @@ requirements = {"robotType": "Flex", "apiLevel": "2.15"} ALLOW_TEST_PIPETTE_TO_TRANSFER_DILUENT = False -RETURN_TIP = False +RETURN_TIP = True DILUENT_WELLS = ["A11", "A12"] TEST_VOLUME = 200 -TEST_PUSH_OUT = 15 +TEST_PUSH_OUT = 20 TEST_PIPETTE = "flex_8channel_1000" TEST_TIPS = "opentrons_flex_96_tiprack_200uL" TEST_SOURCES = [ diff --git a/hardware-testing/hardware_testing/protocols/installation_qualification/flex_p1000_multi_200ul_T1K_Utima_protocol.py b/hardware-testing/hardware_testing/protocols/installation_qualification/flex_p1000_multi_200ul_T1K_Utima_protocol.py new file mode 100644 index 00000000000..a80b11b9617 --- /dev/null +++ b/hardware-testing/hardware_testing/protocols/installation_qualification/flex_p1000_multi_200ul_T1K_Utima_protocol.py @@ -0,0 +1,398 @@ +"""Flex IQ: P1000 Multi 200ul.""" +from math import pi, ceil +from typing import List, Optional, Dict, Tuple + +from opentrons.protocol_api import ProtocolContext, InstrumentContext, Labware + +############################################## +# EDIT - START # +############################################## + +metadata = {"protocolName": "Flex IQ: P1000 Multi 200ul Ultima Gen Test"} +requirements = {"robotType": "Flex", "apiLevel": "2.15"} + +ALLOW_TEST_PIPETTE_TO_TRANSFER_DILUENT = False +RETURN_TIP = True +DILUENT_WELLS = ["A11", "A12"] + +TEST_VOLUME = 200 +TEST_PUSH_OUT = None +TEST_PIPETTE = "flex_8channel_1000" +TEST_TIPS = "opentrons_flex_96_tiprack_1000uL" +TEST_SOURCES = [ + { + "source": "A1", + "destinations": ["A1", "A2", "A3", "A4", "A5", "A6"], + }, + { + "source": "A2", + "destinations": ["A7", "A8", "A9", "A10", "A11", "A12"], + }, +] + +############################################## +# EDIT - END # +############################################## + +SUBMERGE_MM = { + "aspirate": 2.0, + "dispense": 2.0, +} +RETRACT_MM = 5.0 +MIN_MM_FROM_BOTTOM = 1.0 + +DILUENT_PUSH_OUT = 15 + +TOUCH_TIP_SPEED = 30 +TOUCH_TIP_DEPTH = -1 + +DELAY_ASPIRATE = 1.0 +DELAY_DISPENSE = 0.5 + +_DYE_MAP: Dict[str, Dict[str, float]] = { + "Range-HV": {"min": 200.1, "max": 350}, + "Range-A": {"min": 50, "max": 200}, + "Range-B": {"min": 10, "max": 49.99}, + "Range-C": {"min": 2, "max": 9.999}, + "Range-D": {"min": 1, "max": 1.999}, + "Range-E": {"min": 0.1, "max": 0.99}, +} + + +def _get_dye_type(volume: float) -> str: + for dye in _DYE_MAP.keys(): + if _DYE_MAP[dye]["min"] <= volume <= _DYE_MAP[dye]["max"]: + return dye + raise ValueError(f"volume {volume} is outside of the available dye range") + + +def _get_diluent_volume() -> float: + return max(200 - TEST_VOLUME, 0) + + +SRC_LABWARE_BY_CHANNELS = { + 1: "nest_12_reservoir_15ml", + 8: "nest_12_reservoir_15ml", + 96: "nest_1_reservoir_195ml", +} + +MIN_VOL_SRC = { + "nest_96_wellplate_2ml_deep": 500, + "nest_12_reservoir_15ml": 3000, + "nest_1_reservoir_195ml": 30000, +} + + +class _LiquidHeightInFlatBottomWell: + def __init__( + self, + bottom_diameter: float, + top_diameter: float, + height: float, + resolution_mm: float = 0.1, + ) -> None: + self._bottom_radius = bottom_diameter / 2 + self._top_radius = top_diameter / 2 + self._height = height + self._resolution_mm = resolution_mm + + def _volume_of_frustum(self, surface_height: float, surface_radius: float) -> float: + """Calculate the volume of a frustum given its height and radii.""" + a = pi * self._bottom_radius * surface_radius + b = pi * surface_radius**2 + c = pi * self._bottom_radius**2 + return (a + b + c) * (surface_height / 3) + + def height_from_volume(self, volume: float) -> float: + """Given the volume, compute the height of the liquid in the well.""" + _rad_diff = self._top_radius - self._bottom_radius + low, high = 0.0, self._height + while high - low > self._resolution_mm: + mid = (low + high) / 2 + r_mid = self._bottom_radius + (mid / self._height) * _rad_diff + if self._volume_of_frustum(mid, r_mid) < volume: + low = mid + else: + high = mid + return (low + high) / 2 + + def volume_from_height(self, height: float) -> float: + """Given the height, compute the volume of the liquid in the well.""" + _rel_height = height / self._height + _rad_diff = self._top_radius - self._bottom_radius + surface_radius = self._bottom_radius + _rad_diff * _rel_height + return self._volume_of_frustum(height, surface_radius) + + +LIQUID_HEIGHT_LOOKUP: Dict[str, List[Tuple[float, float]]] = { + "nest_1_reservoir_195ml": [(0, 0), (195000, 25)], + "nest_12_reservoir_15ml": [ + (0, 0), + (3000, 6.0), + (3500, 7.0), + (4000, 8.0), + (5500, 10.5), + (8000, 14.7), + (10000, 18.0), + (12600, 22.5), + (15000, 26.85), # full depth of well + ], + "nest_96_wellplate_2ml_deep": [ + (0, 0), + (2000, 38), # FIXME: create real lookup table + ], +} + + +def _convert_ul_in_well_to_height_in_well(load_name: str, ul: float) -> float: + if load_name in LIQUID_HEIGHT_LOOKUP: + lookup = LIQUID_HEIGHT_LOOKUP[load_name] + for i in range(len(lookup) - 1): + low = lookup[i] + high = lookup[i + 1] + if low[0] <= ul <= high[0]: + ul_scale = (ul - low[0]) / (high[0] - low[0]) + return (ul_scale * (high[1] - low[1])) + low[1] + elif load_name == "corning_96_wellplate_360ul_flat": + well = _LiquidHeightInFlatBottomWell( + bottom_diameter=6.35, top_diameter=6.858, height=10.668 + ) + return well.height_from_volume(ul) + raise ValueError(f"unable to find height of {ul} ul in {load_name}") + + +def _build_diluent_info() -> Optional[Tuple[float, Dict[str, List[str]]]]: + diluent_vol = _get_diluent_volume() + if diluent_vol <= 0: + return None + target_cols = set( + [int(dst[1:]) for test in TEST_SOURCES for dst in test["destinations"]] + ) + dest = [f"A{col}" for col in sorted(list(target_cols))] + num_dest_per_src = ceil(len(dest) / len(DILUENT_WELLS)) + src_to_dest = { + src: dest[i * num_dest_per_src : i * num_dest_per_src + num_dest_per_src] + for i, src in enumerate(DILUENT_WELLS) + } + return diluent_vol, src_to_dest + + +def _start_volumes_per_trial( + volume: float, load_name: str, channels: int, trials: int +) -> List[float]: + ul_per_aspirate = volume * channels + ul_per_run = ul_per_aspirate * trials + ul_at_start = ul_per_run + MIN_VOL_SRC[load_name] + return [ul_at_start - (ul_per_aspirate * i) for i in range(trials)] + + +def _end_volumes_per_trial( + volume: float, load_name: str, channels: int, trials: int +) -> List[float]: + return [ + ul - (volume * channels) + for ul in _start_volumes_per_trial(volume, load_name, channels, trials) + ] + + +def _dye_start_volumes_per_trial( + load_name: str, channels: int, trials: int +) -> List[float]: + return _start_volumes_per_trial(TEST_VOLUME, load_name, channels, trials) + + +def _diluent_start_volumes_per_trial(load_name: str, trials: int) -> List[float]: + return _start_volumes_per_trial(_get_diluent_volume(), load_name, 8, trials) + + +def _assign_starting_volumes_dye( + ctx: ProtocolContext, + pipette: InstrumentContext, + reservoir: Labware, +) -> None: + dye = ctx.define_liquid( + name=_get_dye_type(TEST_VOLUME), + description=f"Artel MVS Dye: {_get_dye_type(TEST_VOLUME)}", + display_color="#FF0000", + ) + for test in TEST_SOURCES: + src_ul_per_trial = _dye_start_volumes_per_trial( + reservoir.load_name, pipette.channels, len(test["destinations"]) + ) + first_trial_ul = src_ul_per_trial[0] + reservoir[str(test["source"])].load_liquid(dye, first_trial_ul) + + +def _assign_starting_volumes_diluent( + ctx: ProtocolContext, + pipette: InstrumentContext, + reservoir: Labware, +) -> None: + diluent_info = _build_diluent_info() + if not diluent_info: + return + diluent_vol, src_to_dst = diluent_info + diluent = ctx.define_liquid( + name="Diluent", + description="Diluent", + display_color="#0000FF", + ) + for source, destinations in src_to_dst.items(): + src_ul_per_trial = _diluent_start_volumes_per_trial( + reservoir.load_name, len(destinations) + ) + if not src_ul_per_trial: + continue + first_trial_ul = src_ul_per_trial[0] + reservoir[source].load_liquid(diluent, first_trial_ul) + + +def _transfer( + ctx: ProtocolContext, + volume: float, + pipette: InstrumentContext, + tips: Labware, + reservoir: Labware, + plate: Labware, + source: str, + destinations: List[str], + same_tip: bool = False, + push_out: Optional[float] = None, + touch_tip: bool = False, + volume_already_in_plate: float = 0, +) -> None: + end_volumes = _end_volumes_per_trial( + volume, reservoir.load_name, pipette.channels, len(destinations) + ) + src_heights = [ + _convert_ul_in_well_to_height_in_well(reservoir.load_name, ul) + for ul in end_volumes + ] + volume_in_plate = volume + volume_already_in_plate + dst_height = _convert_ul_in_well_to_height_in_well(plate.load_name, volume_in_plate) + if same_tip and not pipette.has_tip: + pipette.configure_for_volume(volume) + pipette.pick_up_tip() + for dst_name, height_src in zip(destinations, src_heights): + # calculate pipetting positions + aspirate_pos = reservoir[source].bottom( + max(height_src - SUBMERGE_MM["aspirate"], MIN_MM_FROM_BOTTOM) + ) + dispense_pos = plate[dst_name].bottom( + max(dst_height - SUBMERGE_MM["dispense"], MIN_MM_FROM_BOTTOM) + ) + blow_out_pos = plate[dst_name].bottom( + max(dst_height + RETRACT_MM, MIN_MM_FROM_BOTTOM) + ) + # transfer + if not same_tip: + pipette.configure_for_volume(volume) + pipette.pick_up_tip(tips.next_tip(pipette.channels)) + if pipette.current_volume > 0: + pipette.dispense(pipette.current_volume, reservoir[source].top()) + pipette.aspirate(volume, aspirate_pos) + ctx.delay(seconds=DELAY_ASPIRATE) + pipette.dispense(volume, dispense_pos, push_out=push_out) + ctx.delay(seconds=DELAY_DISPENSE) + pipette.blow_out(blow_out_pos) + if touch_tip: + pipette.touch_tip(speed=TOUCH_TIP_SPEED, v_offset=TOUCH_TIP_DEPTH) + pipette.aspirate(1, blow_out_pos) # trailing air-gap to avoid droplets + if not same_tip: + if RETURN_TIP: + pipette.return_tip() + else: + pipette.drop_tip() + + +def _transfer_diluent( + ctx: ProtocolContext, + pipette: InstrumentContext, + tips: Labware, + reservoir: Labware, + plate: Labware, +) -> None: + diluent_info = _build_diluent_info() + if not diluent_info: + return + diluent_vol, dest_per_src = diluent_info + pipette.configure_for_volume(diluent_vol) + pipette.pick_up_tip(tips.next_tip(pipette.channels)) + for src, destinations in dest_per_src.items(): + _transfer( + ctx, + diluent_vol, + pipette, + tips, + reservoir, + plate, + src, # type: ignore[arg-type] + destinations, # type: ignore[arg-type] + same_tip=True, + push_out=DILUENT_PUSH_OUT, + touch_tip=False, + volume_already_in_plate=0, + ) + if RETURN_TIP: + pipette.return_tip() + else: + pipette.drop_tip() + + +def _transfer_dye( + ctx: ProtocolContext, + pipette: InstrumentContext, + tips: Labware, + reservoir: Labware, + plate: Labware, +) -> None: + for test in TEST_SOURCES: + _transfer( + ctx, + TEST_VOLUME, + pipette, + tips, + reservoir, + plate, + test["source"], # type: ignore[arg-type] + test["destinations"], # type: ignore[arg-type] + push_out=TEST_PUSH_OUT, + touch_tip=True, + volume_already_in_plate=_get_diluent_volume(), + ) + + +def run(ctx: ProtocolContext) -> None: + """Run.""" + # the target plate, handle with great care + plate = ctx.load_labware("corning_96_wellplate_360ul_flat", "D2") + + # dye tips, pipette, and reservoir + dye_tips = ctx.load_labware(TEST_TIPS, "B2") + dye_pipette = ctx.load_instrument(TEST_PIPETTE, "left") + dye_reservoir = ctx.load_labware( + SRC_LABWARE_BY_CHANNELS[dye_pipette.channels], "C2" + ) + _assign_starting_volumes_dye(ctx, dye_pipette, dye_reservoir) + + # diluent tips, pipette, and reservoir + if _get_diluent_volume(): + diluent_tips = ctx.load_labware("opentrons_flex_96_tiprack_200uL", "B3") + if "p1000_multi" in TEST_PIPETTE and ALLOW_TEST_PIPETTE_TO_TRANSFER_DILUENT: + diluent_pipette = dye_pipette # share the 8ch pipette + else: + diluent_pipette = ctx.load_instrument("flex_8channel_1000", "right") + diluent_labware = SRC_LABWARE_BY_CHANNELS[diluent_pipette.channels] + if dye_reservoir.load_name == diluent_labware: + reservoir_diluent = dye_reservoir # share the 12-row reservoir + else: + reservoir_diluent = ctx.load_labware( + SRC_LABWARE_BY_CHANNELS[diluent_pipette.channels], "C3" + ) + _assign_starting_volumes_diluent(ctx, dye_pipette, reservoir_diluent) + + # transfer diluent + _transfer_diluent(ctx, diluent_pipette, diluent_tips, reservoir_diluent, plate) + + # transfer dye + _transfer_dye(ctx, dye_pipette, dye_tips, dye_reservoir, plate) From 8255d3b7088bb6655b284cdf7ee0ae561a41ad27 Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 7 Feb 2024 09:38:46 -0500 Subject: [PATCH 45/53] partial tip pick up script --- .../scripts/96_channel_parital_pickup.py | 557 ++++++++++++++++++ 1 file changed, 557 insertions(+) create mode 100644 hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py diff --git a/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py b/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py new file mode 100644 index 00000000000..1e11da63533 --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py @@ -0,0 +1,557 @@ +"""Partial Tip Pick up For the 96 Channel.""" +import argparse +# import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os, sys +import termios +import tty +import json + +from opentrons.hardware_control.motion_utilities import target_position_from_plunger +from hardware_testing.opentrons_api.types import ( + OT3Mount, + Axis, + Point, + CriticalPoint, +) +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + move_plunger_absolute_ot3, + get_plunger_positions_ot3, + update_pick_up_current, + update_pick_up_speed, + update_pick_up_distance, + update_drop_tip_current, + update_drop_tip_speed, + _get_pipette_from_mount, +) + +from opentrons.config.types import LiquidProbeSettings + +from hardware_testing import data +from hardware_testing.drivers.mark10 import Mark10 +from hardware_testing.drivers import mitutoyo_digimatic_indicator + +aspirate_depth = 10 +dispense_depth = 3 +liquid_retract_dist = 12 +liquid_retract_speed = 5 +retract_dist = 100 +retract_speed = 60 + +leak_test_time = 30 +test_volume = 1000 + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + + +def file_setup(test_data, details): + today = datetime.date.today() + test_name = "{}-pick_up-up-test-{}Amps".format( + details[0], # Pipette model + details[1], # Motor Current + ) + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + + +def dial_indicator_setup(port): + gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator(port=port) + gauge.connect() + return gauge + + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") + return position + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + + print( + "Coordinates: ", + round(position[Axis.X], 2), + ",", + round(position[Axis.Y], 2), + ",", + round(position[Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + + +async def countdown(count_time: float): + """ + This function loops through a countdown before checking the leak visually + """ + time_suspend = 0 + while time_suspend < count_time: + await asyncio.sleep(1) + time_suspend += 1 + print(f"Remaining: {count_time-time_suspend} (s)", end="") + print("\r", end="") + print("") + + +async def update_pickup_tip_speed(api, mount, speed) -> None: + """Update drop-tip current.""" + pipette = _get_pipette_from_mount(api, mount) + config_model = pipette.pick_up_configurations + config_model.speed = speed + pipette.pick_up_configurations = config_model + print(pipette.pick_up_configurations) + +async def move_to_point(api, mount, point, cp): + home_pos = api.get_instrument_max_height(mount, cp) + pos = await api.current_position_ot3(mount, refresh=True, critical_point = cp) + await api.move_to(mount, + Point(pos[Axis.X], + pos[Axis.Y], + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + point.z)) + +def load_config_(filename: str) -> Dict: + """This function loads a given config file""" + try: + with open(filename, 'r') as file: + data = json.load(file) + except FileNotFoundError: + print('Warning: {0} not found'.format(filename)) + data = {} + except json.decoder.JSONDecodeError: + print('Error: {0} is corrupt'.format(filename)) + data = {} + return data + +def save_config_(filename: str, data: str) -> Dict: + """This function saves a given config file with data""" + try: + with open(filename, 'w') as file: + json.dump( + data, file, sort_keys=True, indent=4, separators=(',', ': ') + ) + except FileNotFoundError: + print('Warning: {0} not found'.format(filename)) + data = {} + except json.decoder.JSONDecodeError: + print('Error: {0} is corrupt'.format(filename)) + data = {} + return data + +async def calibrate_tiprack(api, home_position, mount): + cp = CriticalPoint.NOZZLE + + tiprack_loc = Point( + deck_slot['deck_slot'][args.tiprack_slot]['X'], + deck_slot['deck_slot'][args.tiprack_slot]['Y'], + deck_slot['deck_slot'][args.tiprack_slot]['Z']) + print(tiprack_loc) + print("Calibrate for Pick up tip") + await move_to_point(api, mount, tiprack_loc, cp) + current_position = await api.current_position_ot3(mount, cp) + tiprack_loc = await jog(api, current_position, cp) + tiprack_loc = Point(tiprack_loc[Axis.X], + tiprack_loc[Axis.Y], + tiprack_loc[Axis.by_mount(mount)]) + await api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size], + presses = 1, + increment = 0, + motor_pick_up = False) + await api.home([Axis.Z_L]) + cp = CriticalPoint.TIP + await asyncio.sleep(1) + home_with_tip = await api.current_position(mount, cp) + print("Calibrate Drop Tip Position") + drop_tip_loc = await jog(api, home_with_tip, cp) + drop_tip_loc = Point(drop_tip_loc[Axis.X], + drop_tip_loc[Axis.Y], + drop_tip_loc[Axis.by_mount(mount)]) + return tiprack_loc, drop_tip_loc + +async def _main() -> None: + today = datetime.date.today() + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + await asyncio.sleep(1) + await hw_api.cache_instruments() + pipette_model = hw_api.get_all_attached_instr()[OT3Mount.LEFT]["pipette_id"] + dial_data = {"Column_1": None, "Column_2": None, "Column_3": None, + "Column_4": None, "Column_5": None, "Column_6": None, + "Column_7": None, "Column_8": None, "Column_9": None, + "Column_10": None, "Column_11": None, "Column_12": None} + m_current = float(input("motor_current in amps: ")) + # pick_up_speed = float(input("pick up tip speed in mm/s: ")) + details = [pipette_model, m_current] + test_n, test_f = file_setup(dial_data, details) + file_name = "/home/root/.opentrons/testing_data/pickup_tip_test/pu_96_pipette_%s-%s.csv" % ( + m_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + ) + lp_file_name = '/var/{}-P-{}_Z-{}-{}.csv'.format( pipette_model, + args.plunger_speed, + args.mount_speed, + today.strftime("%b-%d-%Y")) + liquid_probe_settings = LiquidProbeSettings( + # starting_mount_height = 100, + max_z_distance = args.max_z_distance, + min_z_distance = args.min_z_distance, + mount_speed = args.mount_speed, + plunger_speed = args.plunger_speed, + sensor_threshold_pascals = args.sensor_threshold, + expected_liquid_height = args.expected_liquid_height, + log_pressure = args.log_pressure, + aspirate_while_sensing = False, + auto_zero_sensor = False, + num_baseline_reads = 10, + data_file = lp_file_name, + ) + try: + await hw_api.home() + await asyncio.sleep(1) + await hw_api.home_plunger(mount) + await hw_api.set_lights(rails=True) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + print(plunger_pos) + home_position = await hw_api.current_position_ot3(mount) + start_time = time.perf_counter() + m_current = float(input("motor_current in amps: ")) + pick_up_speed = float(input("pick up tip speed in mm/s: ")) + hw_api.clamp_tip_speed = float(input("clamp pick up Speed: ")) + pick_up_distance = float(input("pick up distance in mm: ")) + await update_pick_up_current(hw_api, mount, m_current) + await update_pick_up_speed(hw_api, mount, pick_up_speed) + await update_pick_up_distance(hw_api, mount, pick_up_distance) + # Calibrate to tiprack + if (args.calibrate): + pickup_loc, droptip_loc = await calibrate_tiprack(hw_api, home_position, mount) + print(pickup_loc) + deck_slot['deck_slot'][args.tiprack_slot][Axis.X.name] = pickup_loc.x + deck_slot['deck_slot'][args.tiprack_slot][Axis.Y.name] = pickup_loc.y + deck_slot['deck_slot'][args.tiprack_slot]['Z'] = pickup_loc.z + save_config_(path+cal_fn, deck_slot) + + await hw_api.home_z(mount) + cp = CriticalPoint.TIP + home_w_tip = await hw_api.current_position_ot3(mount, cp) + # Calibrate Dial Indicator with single tip + if (args.calibrate): + # cp = CriticalPoint.TIP + initial_dial_loc = Point( + deck_slot['deck_slot'][args.dial_slot]['X'], + deck_slot['deck_slot'][args.dial_slot]['Y'], + home_w_tip[Axis.by_mount(mount)]) + print("Move to Dial Indicator") + await move_to_point(hw_api, mount, initial_dial_loc, cp) + current_position = await hw_api.current_position_ot3(mount, cp) + dial_loc = await jog(hw_api, current_position, cp) + dial_loc = Point(dial_loc[Axis.X], + dial_loc[Axis.Y], + dial_loc[Axis.by_mount(mount)]) + deck_slot['deck_slot'][args.dial_slot][Axis.X.name] = dial_loc.x + deck_slot['deck_slot'][args.dial_slot][Axis.Y.name] = dial_loc.y + deck_slot['deck_slot'][args.dial_slot]['Z'] = dial_loc.z + save_config_(path+cal_fn, deck_slot) + if (args.trough): + cp = CriticalPoint.TIP + trough_loc = Point(deck_slot['deck_slot'][args.trough_slot]['X'], + deck_slot['deck_slot'][args.trough_slot]['Y'], + home_w_tip[Axis.by_mount(mount)]) + print("Move to Trough") + await move_to_point(hw_api, mount, trough_loc, cp) + current_position = await hw_api.current_position_ot3(mount, cp) + trough_loc = await jog(hw_api, current_position, cp) + trough_loc = Point(trough_loc[Axis.X], + trough_loc[Axis.Y], + trough_loc[Axis.by_mount(mount)]) + deck_slot['deck_slot'][args.trough_slot][Axis.X.name] = dial_loc.x + deck_slot['deck_slot'][args.trough_slot][Axis.Y.name] = dial_loc.y + deck_slot['deck_slot'][args.trough_slot]['Z'] = dial_loc.z + save_config_(path+cal_fn, deck_slot) + + num_of_columns = int(input("How many Columns: ")) + num_of_rows = int(input("Number of Rows: ")) + tips_to_use = (num_of_rows * num_of_columns) + # tips_to_use = (num_of_columns * 8) + while True: + measurements = [] + tip_count = 0 + x_offset = 0 + y_offset = 0 + cp = CriticalPoint.TIP + if args.dial_indicator: + for tip in range(1, tips_to_use + 1): + cp = CriticalPoint.TIP + tip_count += 1 + tip_position = Point(dial_loc[0] + x_offset, + dial_loc[1] + y_offset, + dial_loc[2]) + await move_to_point(hw_api, mount, tip_position, cp) + await asyncio.sleep(1) + tip_measurement = gauge.read() + print("tip-",tip_count, "(mm): " ,tip_measurement, end="") + print("\r", end="") + measurements.append(tip_measurement) + if tip_count % num_of_columns == 0: + d_str = '' + for m in measurements: + d_str += str(m) + ',' + d_str = d_str[:-1] + '\n' + print(f"{d_str}") + data.append_data_to_file(test_n, test_f, d_str) + # Reset Measurements list + measurements = [] + print("\r\n") + x_offset -= 9 + # if tip_count % num_of_column == 0: + if tip_count % num_of_columns == 0: + y_offset += 9 + if tip_count % num_of_columns == 0: + x_offset = 0 + + if args.trough: + await hw_api.prepare_for_aspirate(mount) + await move_to_point(hw_api, mount, trough_loc, cp) + await hw_api.aspirate(mount, test_volume) + await hw_api.home_z(mount) + await countdown(leak_test_time) + await move_to_point(hw_api, mount, trough_loc, cp) + await hw_api.dispense(mount) + # await hw_api.home_z(mount) + hw_api.clamp_drop_tip_speed = float(input("Drop tip speed: ")) + await update_drop_tip_speed(hw_api, mount, hw_api.clamp_drop_tip_speed ) + cp = CriticalPoint.TIP + await move_to_point(hw_api, mount, droptip_loc, cp) + input("Feel the Tip!") + await hw_api.drop_tip(mount) + await hw_api.home_z(mount) + + m_current = float(input("motor_current in amps: ")) + pick_up_speed = float(input("prep pick up tip speed in mm/s: ")) + # Pick up distance i originally used was 16.5 + pick_up_distance = float(input("pick up distance in mm: ")) + hw_api.clamp_tip_speed = float(input("clamp pick up Speed: ")) + num_of_columns = int(input("How many Columns: ")) + num_of_rows = int(input("Number of Rows: ")) + tips_to_use = (num_of_rows * num_of_columns) + # tips_to_use = num_of_columns * 8 + await update_pick_up_current(hw_api, mount, m_current) + await update_pick_up_speed(hw_api, mount, pick_up_speed) + await update_pick_up_distance(hw_api, mount, pick_up_distance) + cp = CriticalPoint.NOZZLE + if args.columns: + column = float(input("How many Columns to Move: ")) + column = column*9 + pickup_loc = Point(pickup_loc[0] - column, + pickup_loc[1], + pickup_loc[2]) + else: + row = float(input("How many Row to Move: ")) + row = row*9 + pickup_loc = Point(pickup_loc[0], + pickup_loc[1] + row, + pickup_loc[2]) + await move_to_point(hw_api, mount, pickup_loc, cp) + await hw_api.pick_up_tip(mount, + tip_length=tip_length[args.tip_size], + presses = 1, + increment = 0, + motor_pick_up = False) + await hw_api.home_z(mount.LEFT) + cp = CriticalPoint.TIP + current_position = await hw_api.current_position_ot3(mount, cp) + this_position = await jog(hw_api, current_position, cp) + input("Press Enter to continue") + + except KeyboardInterrupt: + await hw_api.disengage_axes([Axis.X, Axis.Y]) + finally: + await hw_api.disengage_axes([Axis.X, Axis.Y]) + await hw_api.clean_up() + + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--trough", action="store_true") + parser.add_argument("--tiprack", action="store_true") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C2") + parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") + parser.add_argument("--dial_indicator", action="store_true") + parser.add_argument("--calibrate", action="store_true") + parser.add_argument("--columns", action="store_true") + parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") + parser.add_argument("--max_z_distance", type=float, default=40) + parser.add_argument("--min_z_distance", type=float, default=5) + parser.add_argument("--mount_speed", type=float, default=5) + parser.add_argument("--plunger_speed", type=float, default=10) + parser.add_argument( + "--sensor_threshold", type=float, default=100, help="Threshold in Pascals" + ) + parser.add_argument("--expected_liquid_height", type=int, default=0) + parser.add_argument("--log_pressure", action="store_true") + parser.add_argument( + "--dial_port", type=str, default="/dev/ttyUSB0", help="Dial indicator Port" + ) + args = parser.parse_args() + path = '/data/testing_data/' + cal_fn = 'calibrations.json' + if args.calibrate: + with open(path + cal_fn, 'r') as openfile: + deck_slot = json.load(openfile) + print(deck_slot) + else: + with open(path + cal_fn, 'r') as openfile: + deck_slot = json.load(openfile) + tip_length = {"T1K": 95.7, "T200": 58.35, "T50": 57.9} + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + + if args.dial_indicator: + gauge = dial_indicator_setup(port=args.dial_port) + asyncio.run(_main()) From 00ccc2c8a05973ccacda0a5061dd9fbd267c8da6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 09:49:53 -0500 Subject: [PATCH 46/53] fix(app-testing): snapshot failure capture (#14439) addition of ignoreTipConfiguration flags --- ...x_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_Smoke].json | 14 ++++++++++++++ ...M_TC_MB_2_16_DeckConfiguration1_NoModules].json | 2 ++ ..._16_AnalysisError_DropLabwareIntoTrashBin].json | 1 + ...[OT2_P300M_P20S_TC_HS_TM_2_16_SmokeTestV3].json | 11 +++++++++++ ...IPPER_HS_TM_TC_MB_2_16_DeckConfiguration1].json | 2 ++ 5 files changed, 30 insertions(+) diff --git a/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2a185c4e1c][Flex_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_Smoke].json b/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2a185c4e1c][Flex_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_Smoke].json index c638710d9ea..f363e79201f 100644 --- a/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2a185c4e1c][Flex_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_Smoke].json +++ b/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2a185c4e1c][Flex_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_Smoke].json @@ -8271,6 +8271,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -8373,6 +8374,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -8475,6 +8477,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -8577,6 +8580,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -8679,6 +8683,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -8781,6 +8786,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -8883,6 +8889,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -8985,6 +8992,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -9087,6 +9095,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -9189,6 +9198,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -9291,6 +9301,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -9393,6 +9404,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -9641,6 +9653,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": false, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -10316,6 +10329,7 @@ "addressableAreaName": "movableTrashB3", "alternateDropLocation": false, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, diff --git a/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2a32a763f5][Flex_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_DeckConfiguration1_NoModules].json b/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2a32a763f5][Flex_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_DeckConfiguration1_NoModules].json index 29699064add..e1749edf244 100644 --- a/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2a32a763f5][Flex_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_DeckConfiguration1_NoModules].json +++ b/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2a32a763f5][Flex_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_DeckConfiguration1_NoModules].json @@ -10514,6 +10514,7 @@ "addressableAreaName": "movableTrashC1", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -10758,6 +10759,7 @@ "addressableAreaName": "movableTrashD1", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, diff --git a/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[5eb46a4f85][Flex_P1000_96_GRIPPER_2_16_AnalysisError_DropLabwareIntoTrashBin].json b/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[5eb46a4f85][Flex_P1000_96_GRIPPER_2_16_AnalysisError_DropLabwareIntoTrashBin].json index 93b099eae94..af05109c1e0 100644 --- a/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[5eb46a4f85][Flex_P1000_96_GRIPPER_2_16_AnalysisError_DropLabwareIntoTrashBin].json +++ b/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[5eb46a4f85][Flex_P1000_96_GRIPPER_2_16_AnalysisError_DropLabwareIntoTrashBin].json @@ -1257,6 +1257,7 @@ "addressableAreaName": "movableTrashC3", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, diff --git a/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[a23a1de3ce][OT2_P300M_P20S_TC_HS_TM_2_16_SmokeTestV3].json b/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[a23a1de3ce][OT2_P300M_P20S_TC_HS_TM_2_16_SmokeTestV3].json index 49b64623b97..0c7c361123c 100644 --- a/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[a23a1de3ce][OT2_P300M_P20S_TC_HS_TM_2_16_SmokeTestV3].json +++ b/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[a23a1de3ce][OT2_P300M_P20S_TC_HS_TM_2_16_SmokeTestV3].json @@ -11102,6 +11102,7 @@ "addressableAreaName": "fixedTrash", "alternateDropLocation": false, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -11137,6 +11138,7 @@ "addressableAreaName": "fixedTrash", "alternateDropLocation": false, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -11216,6 +11218,7 @@ "addressableAreaName": "fixedTrash", "alternateDropLocation": false, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -11295,6 +11298,7 @@ "addressableAreaName": "fixedTrash", "alternateDropLocation": false, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -11374,6 +11378,7 @@ "addressableAreaName": "fixedTrash", "alternateDropLocation": false, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -11453,6 +11458,7 @@ "addressableAreaName": "fixedTrash", "alternateDropLocation": false, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -11532,6 +11538,7 @@ "addressableAreaName": "fixedTrash", "alternateDropLocation": false, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -11561,6 +11568,7 @@ "addressableAreaName": "fixedTrash", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -14985,6 +14993,7 @@ "addressableAreaName": "fixedTrash", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -15186,6 +15195,7 @@ "addressableAreaName": "fixedTrash", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -15263,6 +15273,7 @@ "addressableAreaName": "fixedTrash", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, diff --git a/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[afe15b729c][Flex_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_DeckConfiguration1].json b/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[afe15b729c][Flex_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_DeckConfiguration1].json index caffbd78786..1bb7131a414 100644 --- a/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[afe15b729c][Flex_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_DeckConfiguration1].json +++ b/app-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[afe15b729c][Flex_P1000_96_GRIPPER_HS_TM_TC_MB_2_16_DeckConfiguration1].json @@ -12283,6 +12283,7 @@ "addressableAreaName": "movableTrashC1", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, @@ -12527,6 +12528,7 @@ "addressableAreaName": "movableTrashD1", "alternateDropLocation": true, "forceDirect": false, + "ignoreTipConfiguration": true, "offset": { "x": 0.0, "y": 0.0, From 99392de1643babe199cdfb0deb46dd612644d858 Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 7 Feb 2024 15:30:12 -0500 Subject: [PATCH 47/53] partial tip test script --- .../scripts/96_channel_parital_pickup.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py b/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py index 1e11da63533..00189b66d9a 100644 --- a/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py +++ b/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py @@ -349,6 +349,46 @@ async def _main() -> None: await update_pick_up_current(hw_api, mount, m_current) await update_pick_up_speed(hw_api, mount, pick_up_speed) await update_pick_up_distance(hw_api, mount, pick_up_distance) + if (args.calibrate): + cp = CriticalPoint.NOZZLE + home_w_tip = await hw_api.current_position_ot3(mount, cp) + initial_dial_loc = Point( + deck_slot['deck_slot'][args.dial_slot]['X'], + deck_slot['deck_slot'][args.dial_slot]['Y'], + home_w_tip[Axis.by_mount(mount)] + ) + print("Move Nozzle to Dial Indicator") + await move_to_point(hw_api, mount, initial_dial_loc, cp) + current_position = await hw_api.current_position_ot3(mount, cp) + nozzle_loc = await jog(hw_api, current_position_ot3(mount, cp)) + number_of_channels = 96 + for tip in range(1, number_of_channels + 1): + cp = CriticalPoint.NOZZLE + nozzle_count += 1 + nozzle_position = Point(nozzle_loc[0] + x_offset, + nozzle_loc[1] + y_offset, + nozzle_loc[2]) + await move_to_point(hw_api, mount, nozzle_position, cp) + await asyncio.sleep(1) + nozzle_measurement = gauge.read() + print("nozzle-",nozzle_count, "(mm): " , nozzle_measurement, end="") + print("\r", end="") + measurements.append(nozzle_measurement) + if tip_count % num_of_columns == 0: + d_str = '' + for m in measurements: + d_str += str(m) + ',' + d_str = d_str[:-1] + '\n' + print(f"{d_str}") + data.append_data_to_file(test_n, test_f, d_str) + # Reset Measurements list + measurements = [] + print("\r\n") + x_offset -= 9 + if tip_count % num_of_columns == 0: + y_offset += 9 + if tip_count % num_of_columns == 0: + x_offset = 0 # Calibrate to tiprack if (args.calibrate): pickup_loc, droptip_loc = await calibrate_tiprack(hw_api, home_position, mount) From bed4d64b392e55bef5dbcc52166cf5662b0cd0eb Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 8 Feb 2024 13:47:50 -0500 Subject: [PATCH 48/53] made changes --- .../hardware_testing/scripts/96_channel_parital_pickup.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py b/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py index 00189b66d9a..af71f6b7f35 100644 --- a/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py +++ b/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py @@ -27,8 +27,7 @@ update_pick_up_current, update_pick_up_speed, update_pick_up_distance, - update_drop_tip_current, - update_drop_tip_speed, + # update_drop_tip_speed, _get_pipette_from_mount, ) @@ -485,8 +484,8 @@ async def _main() -> None: await move_to_point(hw_api, mount, trough_loc, cp) await hw_api.dispense(mount) # await hw_api.home_z(mount) - hw_api.clamp_drop_tip_speed = float(input("Drop tip speed: ")) - await update_drop_tip_speed(hw_api, mount, hw_api.clamp_drop_tip_speed ) + # hw_api.clamp_drop_tip_speed = float(input("Drop tip speed: ")) + # await update_drop_tip_speed(hw_api, mount, hw_api.clamp_drop_tip_speed ) cp = CriticalPoint.TIP await move_to_point(hw_api, mount, droptip_loc, cp) input("Feel the Tip!") From e4a70311b53e5cca08ee760e1bb83e58ca706504 Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 8 Feb 2024 14:09:37 -0500 Subject: [PATCH 49/53] a few changes --- .../scripts/96_channel_parital_pickup.py | 1 - .../scripts/calibrations.json | 52 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 hardware-testing/hardware_testing/scripts/calibrations.json diff --git a/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py b/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py index af71f6b7f35..4e5af735837 100644 --- a/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py +++ b/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py @@ -12,7 +12,6 @@ import tty import json -from opentrons.hardware_control.motion_utilities import target_position_from_plunger from hardware_testing.opentrons_api.types import ( OT3Mount, Axis, diff --git a/hardware-testing/hardware_testing/scripts/calibrations.json b/hardware-testing/hardware_testing/scripts/calibrations.json new file mode 100644 index 00000000000..2f1451c0c51 --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/calibrations.json @@ -0,0 +1,52 @@ +{ + "deck_slot": { + "A1": {"X": 13.42, + "Y": 394.92, + "Z": 110 + }, + "A2": {"X": 177.32, + "Y": 394.92, + "Z": 110 + }, + "A3": {"X": 341.03, + "Y": 394.0, + "Z": 110 + }, + "B1": {"X": 13.42, + "Y": 288.42, + "Z": 110 + }, + "B2": {"X": 177.32, + "Y": 181.92, + "Z": 110 + }, + "B3": {"X": 341.03, + "Y": 288.92, + "Z": 110 + }, + "C1": {"X": 13.42, + "Y": 181.92, + "Z": 110 + }, + "C2": {"X": 177.32, + "Y": 181.92, + "Z": 110 + }, + "C3": {"X": 177.32, + "Y": 181.92, + "Z": 110 + }, + "D1": {"X": 13.42, + "Y": 75.5, + "Z": 110 + }, + "D2": {"X": 177.32, + "Y": 75.5, + "Z": 110 + }, + "D1": {"X": 341.03, + "Y": 75.5, + "Z": 110 + } + } +} From 413d816dc062dd22d639821a14407081bc048b11 Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 15 Feb 2024 14:21:37 -0500 Subject: [PATCH 50/53] fixed errors --- .../opentrons_api/helpers_ot3.py | 8 ++++--- .../scripts/96_channel_parital_pickup.py | 21 ++++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py index 95befbfb65b..f6920eb62f3 100644 --- a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py +++ b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py @@ -578,16 +578,18 @@ async def update_pick_up_distance( ) -> None: """Update pick-up-tip current.""" pipette = _get_pipette_from_mount(api, mount) - config_model = pipette.pick_up_configurations.press_fit + # print(pipette.pick_up_configurations) + config_model = pipette.pick_up_configurations + print(config_model) config_model.distance = distance - pipette.pick_up_configurations.press_fit = config_model + pipette.pick_up_configurations = config_model async def update_pick_up_speed( api: OT3API, mount: OT3Mount, speed: float = 5.0 ) -> None: """Update pick-up-tip current.""" pipette = _get_pipette_from_mount(api, mount) - config_model = pipette.pick_up_configurations + config_model = pipette.pick_up_configurations.press_fit config_model.speed = speed pipette.pick_up_configurations = config_model diff --git a/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py b/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py index 4e5af735837..a2e16660e79 100644 --- a/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py +++ b/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py @@ -334,7 +334,7 @@ async def _main() -> None: try: await hw_api.home() await asyncio.sleep(1) - await hw_api.home_plunger(mount) + # await hw_api.home_plunger(mount) await hw_api.set_lights(rails=True) plunger_pos = get_plunger_positions_ot3(hw_api, mount) print(plunger_pos) @@ -358,21 +358,26 @@ async def _main() -> None: print("Move Nozzle to Dial Indicator") await move_to_point(hw_api, mount, initial_dial_loc, cp) current_position = await hw_api.current_position_ot3(mount, cp) - nozzle_loc = await jog(hw_api, current_position_ot3(mount, cp)) + nozzle_loc = await jog(hw_api, current_position, cp) number_of_channels = 96 + nozzle_count = 0 + x_offset = 0 + y_offset = 0 + measurements = [] + num_of_columns = 12 for tip in range(1, number_of_channels + 1): cp = CriticalPoint.NOZZLE nozzle_count += 1 - nozzle_position = Point(nozzle_loc[0] + x_offset, - nozzle_loc[1] + y_offset, - nozzle_loc[2]) + nozzle_position = Point(nozzle_loc[Axis.X] + x_offset, + nozzle_loc[Axis.Y] + y_offset, + nozzle_loc[Axis.by_mount(mount)]) await move_to_point(hw_api, mount, nozzle_position, cp) await asyncio.sleep(1) nozzle_measurement = gauge.read() print("nozzle-",nozzle_count, "(mm): " , nozzle_measurement, end="") print("\r", end="") measurements.append(nozzle_measurement) - if tip_count % num_of_columns == 0: + if nozzle_count % num_of_columns == 0: d_str = '' for m in measurements: d_str += str(m) + ',' @@ -383,9 +388,9 @@ async def _main() -> None: measurements = [] print("\r\n") x_offset -= 9 - if tip_count % num_of_columns == 0: + if nozzle_count % num_of_columns == 0: y_offset += 9 - if tip_count % num_of_columns == 0: + if nozzle_count % num_of_columns == 0: x_offset = 0 # Calibrate to tiprack if (args.calibrate): From f39a68fb3f1ad3140bbbed65c85754652124922d Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 28 Feb 2024 14:46:55 -0500 Subject: [PATCH 51/53] change name --- .../scripts/96_channel_parital_pickup.py | 600 ------------------ 1 file changed, 600 deletions(-) delete mode 100644 hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py diff --git a/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py b/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py deleted file mode 100644 index a2e16660e79..00000000000 --- a/hardware-testing/hardware_testing/scripts/96_channel_parital_pickup.py +++ /dev/null @@ -1,600 +0,0 @@ -"""Partial Tip Pick up For the 96 Channel.""" -import argparse -# import ast -import asyncio -import csv -import time -from typing import Tuple, Dict, Optional -from threading import Thread -import datetime -import os, sys -import termios -import tty -import json - -from hardware_testing.opentrons_api.types import ( - OT3Mount, - Axis, - Point, - CriticalPoint, -) -from hardware_testing.opentrons_api.helpers_ot3 import ( - build_async_ot3_hardware_api, - home_ot3, - move_plunger_absolute_ot3, - get_plunger_positions_ot3, - update_pick_up_current, - update_pick_up_speed, - update_pick_up_distance, - # update_drop_tip_speed, - _get_pipette_from_mount, -) - -from opentrons.config.types import LiquidProbeSettings - -from hardware_testing import data -from hardware_testing.drivers.mark10 import Mark10 -from hardware_testing.drivers import mitutoyo_digimatic_indicator - -aspirate_depth = 10 -dispense_depth = 3 -liquid_retract_dist = 12 -liquid_retract_speed = 5 -retract_dist = 100 -retract_speed = 60 - -leak_test_time = 30 -test_volume = 1000 - -def dict_keys_to_line(dict): - return str.join(",", list(dict.keys())) + "\n" - - -def file_setup(test_data, details): - today = datetime.date.today() - test_name = "{}-pick_up-up-test-{}Amps".format( - details[0], # Pipette model - details[1], # Motor Current - ) - test_header = dict_keys_to_line(test_data) - test_tag = "-{}".format(today.strftime("%b-%d-%Y")) - test_id = data.create_run_id() - test_path = data.create_folder_for_test_data(test_name) - test_file = data.create_file_name(test_name, test_id, test_tag) - data.append_data_to_file(test_name, test_file, test_header) - print("FILE PATH = ", test_path) - print("FILE NAME = ", test_file) - return test_name, test_file - - -def dial_indicator_setup(port): - gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator(port=port) - gauge.connect() - return gauge - - -def getch(): - """ - fd: file descriptor stdout, stdin, stderr - This functions gets a single input keyboard character from the user - """ - - def _getch(): - fd = sys.stdin.fileno() - old_settings = termios.tcgetattr(fd) - try: - tty.setraw(fd) - ch = sys.stdin.read(1) - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - return ch - - return _getch() - - -async def jog(api, position, cp) -> Dict[Axis, float]: - step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] - step_length_index = 3 - step = step_size[step_length_index] - xy_speed = 60 - za_speed = 65 - information_str = """ - Click >> i << to move up - Click >> k << to move down - Click >> a << to move left - Click >> d << to move right - Click >> w << to move forward - Click >> s << to move back - Click >> + << to Increase the length of each step - Click >> - << to decrease the length of each step - Click >> Enter << to save position - Click >> q << to quit the test script - """ - print(information_str) - while True: - input = getch() - if input == "a": - # minus x direction - sys.stdout.flush() - await api.move_rel( - mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed - ) - - elif input == "d": - # plus x direction - sys.stdout.flush() - await api.move_rel( - mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed - ) - - elif input == "w": - # minus y direction - sys.stdout.flush() - await api.move_rel( - mount, Point(0, step_size[step_length_index], 0), speed=xy_speed - ) - - elif input == "s": - # plus y direction - sys.stdout.flush() - await api.move_rel( - mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed - ) - - elif input == "i": - sys.stdout.flush() - await api.move_rel( - mount, Point(0, 0, step_size[step_length_index]), speed=za_speed - ) - - elif input == "k": - sys.stdout.flush() - await api.move_rel( - mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed - ) - - elif input == "q": - sys.stdout.flush() - print("TEST CANCELLED") - quit() - - elif input == "+": - sys.stdout.flush() - step_length_index = step_length_index + 1 - if step_length_index >= 7: - step_length_index = 7 - step = step_size[step_length_index] - - elif input == "-": - sys.stdout.flush() - step_length_index = step_length_index - 1 - if step_length_index <= 0: - step_length_index = 0 - step = step_size[step_length_index] - - elif input == "\r": - sys.stdout.flush() - position = await api.current_position_ot3( - mount, refresh=True, critical_point=cp - ) - print("\r\n") - return position - position = await api.current_position_ot3( - mount, refresh=True, critical_point=cp - ) - - print( - "Coordinates: ", - round(position[Axis.X], 2), - ",", - round(position[Axis.Y], 2), - ",", - round(position[Axis.by_mount(mount)], 2), - " Motor Step: ", - step_size[step_length_index], - end="", - ) - print("\r", end="") - - -async def countdown(count_time: float): - """ - This function loops through a countdown before checking the leak visually - """ - time_suspend = 0 - while time_suspend < count_time: - await asyncio.sleep(1) - time_suspend += 1 - print(f"Remaining: {count_time-time_suspend} (s)", end="") - print("\r", end="") - print("") - - -async def update_pickup_tip_speed(api, mount, speed) -> None: - """Update drop-tip current.""" - pipette = _get_pipette_from_mount(api, mount) - config_model = pipette.pick_up_configurations - config_model.speed = speed - pipette.pick_up_configurations = config_model - print(pipette.pick_up_configurations) - -async def move_to_point(api, mount, point, cp): - home_pos = api.get_instrument_max_height(mount, cp) - pos = await api.current_position_ot3(mount, refresh=True, critical_point = cp) - await api.move_to(mount, - Point(pos[Axis.X], - pos[Axis.Y], - home_pos)) - await api.move_to(mount, - Point(point.x, - point.y, - home_pos)) - await api.move_to(mount, - Point(point.x, - point.y, - point.z)) - -def load_config_(filename: str) -> Dict: - """This function loads a given config file""" - try: - with open(filename, 'r') as file: - data = json.load(file) - except FileNotFoundError: - print('Warning: {0} not found'.format(filename)) - data = {} - except json.decoder.JSONDecodeError: - print('Error: {0} is corrupt'.format(filename)) - data = {} - return data - -def save_config_(filename: str, data: str) -> Dict: - """This function saves a given config file with data""" - try: - with open(filename, 'w') as file: - json.dump( - data, file, sort_keys=True, indent=4, separators=(',', ': ') - ) - except FileNotFoundError: - print('Warning: {0} not found'.format(filename)) - data = {} - except json.decoder.JSONDecodeError: - print('Error: {0} is corrupt'.format(filename)) - data = {} - return data - -async def calibrate_tiprack(api, home_position, mount): - cp = CriticalPoint.NOZZLE - - tiprack_loc = Point( - deck_slot['deck_slot'][args.tiprack_slot]['X'], - deck_slot['deck_slot'][args.tiprack_slot]['Y'], - deck_slot['deck_slot'][args.tiprack_slot]['Z']) - print(tiprack_loc) - print("Calibrate for Pick up tip") - await move_to_point(api, mount, tiprack_loc, cp) - current_position = await api.current_position_ot3(mount, cp) - tiprack_loc = await jog(api, current_position, cp) - tiprack_loc = Point(tiprack_loc[Axis.X], - tiprack_loc[Axis.Y], - tiprack_loc[Axis.by_mount(mount)]) - await api.pick_up_tip( - mount, tip_length=tip_length[args.tip_size], - presses = 1, - increment = 0, - motor_pick_up = False) - await api.home([Axis.Z_L]) - cp = CriticalPoint.TIP - await asyncio.sleep(1) - home_with_tip = await api.current_position(mount, cp) - print("Calibrate Drop Tip Position") - drop_tip_loc = await jog(api, home_with_tip, cp) - drop_tip_loc = Point(drop_tip_loc[Axis.X], - drop_tip_loc[Axis.Y], - drop_tip_loc[Axis.by_mount(mount)]) - return tiprack_loc, drop_tip_loc - -async def _main() -> None: - today = datetime.date.today() - hw_api = await build_async_ot3_hardware_api( - is_simulating=args.simulate, use_defaults=True - ) - await asyncio.sleep(1) - await hw_api.cache_instruments() - pipette_model = hw_api.get_all_attached_instr()[OT3Mount.LEFT]["pipette_id"] - dial_data = {"Column_1": None, "Column_2": None, "Column_3": None, - "Column_4": None, "Column_5": None, "Column_6": None, - "Column_7": None, "Column_8": None, "Column_9": None, - "Column_10": None, "Column_11": None, "Column_12": None} - m_current = float(input("motor_current in amps: ")) - # pick_up_speed = float(input("pick up tip speed in mm/s: ")) - details = [pipette_model, m_current] - test_n, test_f = file_setup(dial_data, details) - file_name = "/home/root/.opentrons/testing_data/pickup_tip_test/pu_96_pipette_%s-%s.csv" % ( - m_current, - datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), - ) - lp_file_name = '/var/{}-P-{}_Z-{}-{}.csv'.format( pipette_model, - args.plunger_speed, - args.mount_speed, - today.strftime("%b-%d-%Y")) - liquid_probe_settings = LiquidProbeSettings( - # starting_mount_height = 100, - max_z_distance = args.max_z_distance, - min_z_distance = args.min_z_distance, - mount_speed = args.mount_speed, - plunger_speed = args.plunger_speed, - sensor_threshold_pascals = args.sensor_threshold, - expected_liquid_height = args.expected_liquid_height, - log_pressure = args.log_pressure, - aspirate_while_sensing = False, - auto_zero_sensor = False, - num_baseline_reads = 10, - data_file = lp_file_name, - ) - try: - await hw_api.home() - await asyncio.sleep(1) - # await hw_api.home_plunger(mount) - await hw_api.set_lights(rails=True) - plunger_pos = get_plunger_positions_ot3(hw_api, mount) - print(plunger_pos) - home_position = await hw_api.current_position_ot3(mount) - start_time = time.perf_counter() - m_current = float(input("motor_current in amps: ")) - pick_up_speed = float(input("pick up tip speed in mm/s: ")) - hw_api.clamp_tip_speed = float(input("clamp pick up Speed: ")) - pick_up_distance = float(input("pick up distance in mm: ")) - await update_pick_up_current(hw_api, mount, m_current) - await update_pick_up_speed(hw_api, mount, pick_up_speed) - await update_pick_up_distance(hw_api, mount, pick_up_distance) - if (args.calibrate): - cp = CriticalPoint.NOZZLE - home_w_tip = await hw_api.current_position_ot3(mount, cp) - initial_dial_loc = Point( - deck_slot['deck_slot'][args.dial_slot]['X'], - deck_slot['deck_slot'][args.dial_slot]['Y'], - home_w_tip[Axis.by_mount(mount)] - ) - print("Move Nozzle to Dial Indicator") - await move_to_point(hw_api, mount, initial_dial_loc, cp) - current_position = await hw_api.current_position_ot3(mount, cp) - nozzle_loc = await jog(hw_api, current_position, cp) - number_of_channels = 96 - nozzle_count = 0 - x_offset = 0 - y_offset = 0 - measurements = [] - num_of_columns = 12 - for tip in range(1, number_of_channels + 1): - cp = CriticalPoint.NOZZLE - nozzle_count += 1 - nozzle_position = Point(nozzle_loc[Axis.X] + x_offset, - nozzle_loc[Axis.Y] + y_offset, - nozzle_loc[Axis.by_mount(mount)]) - await move_to_point(hw_api, mount, nozzle_position, cp) - await asyncio.sleep(1) - nozzle_measurement = gauge.read() - print("nozzle-",nozzle_count, "(mm): " , nozzle_measurement, end="") - print("\r", end="") - measurements.append(nozzle_measurement) - if nozzle_count % num_of_columns == 0: - d_str = '' - for m in measurements: - d_str += str(m) + ',' - d_str = d_str[:-1] + '\n' - print(f"{d_str}") - data.append_data_to_file(test_n, test_f, d_str) - # Reset Measurements list - measurements = [] - print("\r\n") - x_offset -= 9 - if nozzle_count % num_of_columns == 0: - y_offset += 9 - if nozzle_count % num_of_columns == 0: - x_offset = 0 - # Calibrate to tiprack - if (args.calibrate): - pickup_loc, droptip_loc = await calibrate_tiprack(hw_api, home_position, mount) - print(pickup_loc) - deck_slot['deck_slot'][args.tiprack_slot][Axis.X.name] = pickup_loc.x - deck_slot['deck_slot'][args.tiprack_slot][Axis.Y.name] = pickup_loc.y - deck_slot['deck_slot'][args.tiprack_slot]['Z'] = pickup_loc.z - save_config_(path+cal_fn, deck_slot) - - await hw_api.home_z(mount) - cp = CriticalPoint.TIP - home_w_tip = await hw_api.current_position_ot3(mount, cp) - # Calibrate Dial Indicator with single tip - if (args.calibrate): - # cp = CriticalPoint.TIP - initial_dial_loc = Point( - deck_slot['deck_slot'][args.dial_slot]['X'], - deck_slot['deck_slot'][args.dial_slot]['Y'], - home_w_tip[Axis.by_mount(mount)]) - print("Move to Dial Indicator") - await move_to_point(hw_api, mount, initial_dial_loc, cp) - current_position = await hw_api.current_position_ot3(mount, cp) - dial_loc = await jog(hw_api, current_position, cp) - dial_loc = Point(dial_loc[Axis.X], - dial_loc[Axis.Y], - dial_loc[Axis.by_mount(mount)]) - deck_slot['deck_slot'][args.dial_slot][Axis.X.name] = dial_loc.x - deck_slot['deck_slot'][args.dial_slot][Axis.Y.name] = dial_loc.y - deck_slot['deck_slot'][args.dial_slot]['Z'] = dial_loc.z - save_config_(path+cal_fn, deck_slot) - if (args.trough): - cp = CriticalPoint.TIP - trough_loc = Point(deck_slot['deck_slot'][args.trough_slot]['X'], - deck_slot['deck_slot'][args.trough_slot]['Y'], - home_w_tip[Axis.by_mount(mount)]) - print("Move to Trough") - await move_to_point(hw_api, mount, trough_loc, cp) - current_position = await hw_api.current_position_ot3(mount, cp) - trough_loc = await jog(hw_api, current_position, cp) - trough_loc = Point(trough_loc[Axis.X], - trough_loc[Axis.Y], - trough_loc[Axis.by_mount(mount)]) - deck_slot['deck_slot'][args.trough_slot][Axis.X.name] = dial_loc.x - deck_slot['deck_slot'][args.trough_slot][Axis.Y.name] = dial_loc.y - deck_slot['deck_slot'][args.trough_slot]['Z'] = dial_loc.z - save_config_(path+cal_fn, deck_slot) - - num_of_columns = int(input("How many Columns: ")) - num_of_rows = int(input("Number of Rows: ")) - tips_to_use = (num_of_rows * num_of_columns) - # tips_to_use = (num_of_columns * 8) - while True: - measurements = [] - tip_count = 0 - x_offset = 0 - y_offset = 0 - cp = CriticalPoint.TIP - if args.dial_indicator: - for tip in range(1, tips_to_use + 1): - cp = CriticalPoint.TIP - tip_count += 1 - tip_position = Point(dial_loc[0] + x_offset, - dial_loc[1] + y_offset, - dial_loc[2]) - await move_to_point(hw_api, mount, tip_position, cp) - await asyncio.sleep(1) - tip_measurement = gauge.read() - print("tip-",tip_count, "(mm): " ,tip_measurement, end="") - print("\r", end="") - measurements.append(tip_measurement) - if tip_count % num_of_columns == 0: - d_str = '' - for m in measurements: - d_str += str(m) + ',' - d_str = d_str[:-1] + '\n' - print(f"{d_str}") - data.append_data_to_file(test_n, test_f, d_str) - # Reset Measurements list - measurements = [] - print("\r\n") - x_offset -= 9 - # if tip_count % num_of_column == 0: - if tip_count % num_of_columns == 0: - y_offset += 9 - if tip_count % num_of_columns == 0: - x_offset = 0 - - if args.trough: - await hw_api.prepare_for_aspirate(mount) - await move_to_point(hw_api, mount, trough_loc, cp) - await hw_api.aspirate(mount, test_volume) - await hw_api.home_z(mount) - await countdown(leak_test_time) - await move_to_point(hw_api, mount, trough_loc, cp) - await hw_api.dispense(mount) - # await hw_api.home_z(mount) - # hw_api.clamp_drop_tip_speed = float(input("Drop tip speed: ")) - # await update_drop_tip_speed(hw_api, mount, hw_api.clamp_drop_tip_speed ) - cp = CriticalPoint.TIP - await move_to_point(hw_api, mount, droptip_loc, cp) - input("Feel the Tip!") - await hw_api.drop_tip(mount) - await hw_api.home_z(mount) - - m_current = float(input("motor_current in amps: ")) - pick_up_speed = float(input("prep pick up tip speed in mm/s: ")) - # Pick up distance i originally used was 16.5 - pick_up_distance = float(input("pick up distance in mm: ")) - hw_api.clamp_tip_speed = float(input("clamp pick up Speed: ")) - num_of_columns = int(input("How many Columns: ")) - num_of_rows = int(input("Number of Rows: ")) - tips_to_use = (num_of_rows * num_of_columns) - # tips_to_use = num_of_columns * 8 - await update_pick_up_current(hw_api, mount, m_current) - await update_pick_up_speed(hw_api, mount, pick_up_speed) - await update_pick_up_distance(hw_api, mount, pick_up_distance) - cp = CriticalPoint.NOZZLE - if args.columns: - column = float(input("How many Columns to Move: ")) - column = column*9 - pickup_loc = Point(pickup_loc[0] - column, - pickup_loc[1], - pickup_loc[2]) - else: - row = float(input("How many Row to Move: ")) - row = row*9 - pickup_loc = Point(pickup_loc[0], - pickup_loc[1] + row, - pickup_loc[2]) - await move_to_point(hw_api, mount, pickup_loc, cp) - await hw_api.pick_up_tip(mount, - tip_length=tip_length[args.tip_size], - presses = 1, - increment = 0, - motor_pick_up = False) - await hw_api.home_z(mount.LEFT) - cp = CriticalPoint.TIP - current_position = await hw_api.current_position_ot3(mount, cp) - this_position = await jog(hw_api, current_position, cp) - input("Press Enter to continue") - - except KeyboardInterrupt: - await hw_api.disengage_axes([Axis.X, Axis.Y]) - finally: - await hw_api.disengage_axes([Axis.X, Axis.Y]) - await hw_api.clean_up() - - -if __name__ == "__main__": - slot_locs = [ - "A1", - "A2", - "A3", - "B1", - "B2", - "B3:", - "C1", - "C2", - "C3", - "D1", - "D2", - "D3", - ] - parser = argparse.ArgumentParser() - parser.add_argument("--simulate", action="store_true") - parser.add_argument("--trough", action="store_true") - parser.add_argument("--tiprack", action="store_true") - parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") - parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") - parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C2") - parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") - parser.add_argument("--dial_indicator", action="store_true") - parser.add_argument("--calibrate", action="store_true") - parser.add_argument("--columns", action="store_true") - parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") - parser.add_argument("--max_z_distance", type=float, default=40) - parser.add_argument("--min_z_distance", type=float, default=5) - parser.add_argument("--mount_speed", type=float, default=5) - parser.add_argument("--plunger_speed", type=float, default=10) - parser.add_argument( - "--sensor_threshold", type=float, default=100, help="Threshold in Pascals" - ) - parser.add_argument("--expected_liquid_height", type=int, default=0) - parser.add_argument("--log_pressure", action="store_true") - parser.add_argument( - "--dial_port", type=str, default="/dev/ttyUSB0", help="Dial indicator Port" - ) - args = parser.parse_args() - path = '/data/testing_data/' - cal_fn = 'calibrations.json' - if args.calibrate: - with open(path + cal_fn, 'r') as openfile: - deck_slot = json.load(openfile) - print(deck_slot) - else: - with open(path + cal_fn, 'r') as openfile: - deck_slot = json.load(openfile) - tip_length = {"T1K": 95.7, "T200": 58.35, "T50": 57.9} - if args.mount == "left": - mount = OT3Mount.LEFT - else: - mount = OT3Mount.RIGHT - - if args.dial_indicator: - gauge = dial_indicator_setup(port=args.dial_port) - asyncio.run(_main()) From 09c86e65052fa38c9339fb3cd993cad75732469c Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 28 Feb 2024 14:47:13 -0500 Subject: [PATCH 52/53] changed name of file --- .../scripts/96_channel_partial_pickup.py | 613 ++++++++++++++++++ 1 file changed, 613 insertions(+) create mode 100644 hardware-testing/hardware_testing/scripts/96_channel_partial_pickup.py diff --git a/hardware-testing/hardware_testing/scripts/96_channel_partial_pickup.py b/hardware-testing/hardware_testing/scripts/96_channel_partial_pickup.py new file mode 100644 index 00000000000..2c2a6396957 --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/96_channel_partial_pickup.py @@ -0,0 +1,613 @@ +"""Partial Tip Pick up For the 96 Channel.""" +import argparse +# import ast +import asyncio +import csv +import time +from typing import Tuple, Dict, Optional +from threading import Thread +import datetime +import os, sys +import termios +import tty +import json + +from hardware_testing.opentrons_api.types import ( + OT3Mount, + Axis, + Point, + CriticalPoint, +) +from hardware_testing.opentrons_api.helpers_ot3 import ( + build_async_ot3_hardware_api, + home_ot3, + move_plunger_absolute_ot3, + get_plunger_positions_ot3, + update_pick_up_current, + update_pick_up_speed, + update_pick_up_distance, + # update_drop_tip_speed, + _get_pipette_from_mount, +) + +from opentrons.config.types import LiquidProbeSettings + +from hardware_testing import data +from hardware_testing.drivers.mark10 import Mark10 +from hardware_testing.drivers import mitutoyo_digimatic_indicator + +aspirate_depth = 10 +dispense_depth = 3 +liquid_retract_dist = 12 +liquid_retract_speed = 5 +retract_dist = 100 +retract_speed = 60 + +leak_test_time = 30 +test_volume = 1000 + +def dict_keys_to_line(dict): + return str.join(",", list(dict.keys())) + "\n" + + +def file_setup(test_data, details): + today = datetime.date.today() + test_name = "{}-pick_up-up-test-{}Amps".format( + details[0], # Pipette model + details[1], # Motor Current + ) + test_header = dict_keys_to_line(test_data) + test_tag = "-{}".format(today.strftime("%b-%d-%Y")) + test_id = data.create_run_id() + test_path = data.create_folder_for_test_data(test_name) + test_file = data.create_file_name(test_name, test_id, test_tag) + data.append_data_to_file(test_name, test_file, test_header) + print("FILE PATH = ", test_path) + print("FILE NAME = ", test_file) + return test_name, test_file + + +def dial_indicator_setup(port): + gauge = mitutoyo_digimatic_indicator.Mitutoyo_Digimatic_Indicator(port=port) + gauge.connect() + return gauge + + +def getch(): + """ + fd: file descriptor stdout, stdin, stderr + This functions gets a single input keyboard character from the user + """ + + def _getch(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + return _getch() + + +async def jog(api, position, cp) -> Dict[Axis, float]: + step_size = [0.01, 0.05, 0.1, 0.5, 1, 10, 20, 50] + step_length_index = 3 + step = step_size[step_length_index] + xy_speed = 60 + za_speed = 65 + information_str = """ + Click >> i << to move up + Click >> k << to move down + Click >> a << to move left + Click >> d << to move right + Click >> w << to move forward + Click >> s << to move back + Click >> + << to Increase the length of each step + Click >> - << to decrease the length of each step + Click >> Enter << to save position + Click >> q << to quit the test script + """ + print(information_str) + while True: + input = getch() + if input == "a": + # minus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(-step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "d": + # plus x direction + sys.stdout.flush() + await api.move_rel( + mount, Point(step_size[step_length_index], 0, 0), speed=xy_speed + ) + + elif input == "w": + # minus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "s": + # plus y direction + sys.stdout.flush() + await api.move_rel( + mount, Point(0, -step_size[step_length_index], 0), speed=xy_speed + ) + + elif input == "i": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, step_size[step_length_index]), speed=za_speed + ) + + elif input == "k": + sys.stdout.flush() + await api.move_rel( + mount, Point(0, 0, -step_size[step_length_index]), speed=za_speed + ) + + elif input == "q": + sys.stdout.flush() + print("TEST CANCELLED") + quit() + + elif input == "+": + sys.stdout.flush() + step_length_index = step_length_index + 1 + if step_length_index >= 7: + step_length_index = 7 + step = step_size[step_length_index] + + elif input == "-": + sys.stdout.flush() + step_length_index = step_length_index - 1 + if step_length_index <= 0: + step_length_index = 0 + step = step_size[step_length_index] + + elif input == "\r": + sys.stdout.flush() + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + print("\r\n") + return position + position = await api.current_position_ot3( + mount, refresh=True, critical_point=cp + ) + + print( + "Coordinates: ", + round(position[Axis.X], 2), + ",", + round(position[Axis.Y], 2), + ",", + round(position[Axis.by_mount(mount)], 2), + " Motor Step: ", + step_size[step_length_index], + end="", + ) + print("\r", end="") + + +async def countdown(count_time: float): + """ + This function loops through a countdown before checking the leak visually + """ + time_suspend = 0 + while time_suspend < count_time: + await asyncio.sleep(1) + time_suspend += 1 + print(f"Remaining: {count_time-time_suspend} (s)", end="") + print("\r", end="") + print("") + + +async def update_pickup_tip_speed(api, mount, speed) -> None: + """Update drop-tip current.""" + pipette = _get_pipette_from_mount(api, mount) + config_model = pipette.pick_up_configurations + config_model.speed = speed + pipette.pick_up_configurations = config_model + print(pipette.pick_up_configurations) + +async def move_to_point(api, mount, point, cp): + home_pos = api.get_instrument_max_height(mount, cp) + pos = await api.current_position_ot3(mount, refresh=True, critical_point = cp) + await api.move_to(mount, + Point(pos[Axis.X], + pos[Axis.Y], + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + home_pos)) + await api.move_to(mount, + Point(point.x, + point.y, + point.z)) + +def load_config_(filename: str) -> Dict: + """This function loads a given config file""" + try: + with open(filename, 'r') as file: + data = json.load(file) + except FileNotFoundError: + print('Warning: {0} not found'.format(filename)) + data = {} + except json.decoder.JSONDecodeError: + print('Error: {0} is corrupt'.format(filename)) + data = {} + return data + +def save_config_(filename: str, data: str) -> Dict: + """This function saves a given config file with data""" + try: + with open(filename, 'w') as file: + json.dump( + data, file, sort_keys=True, indent=4, separators=(',', ': ') + ) + except FileNotFoundError: + print('Warning: {0} not found'.format(filename)) + data = {} + except json.decoder.JSONDecodeError: + print('Error: {0} is corrupt'.format(filename)) + data = {} + return data + +async def calibrate_tiprack(api, home_position, mount): + cp = CriticalPoint.NOZZLE + + tiprack_loc = Point( + deck_slot['deck_slot'][args.tiprack_slot]['X'], + deck_slot['deck_slot'][args.tiprack_slot]['Y'], + deck_slot['deck_slot'][args.tiprack_slot]['Z']) + print(tiprack_loc) + print("Calibrate for Pick up tip") + await move_to_point(api, mount, tiprack_loc, cp) + current_position = await api.current_position_ot3(mount, cp) + tiprack_loc = await jog(api, current_position, cp) + tiprack_loc = Point(tiprack_loc[Axis.X], + tiprack_loc[Axis.Y], + tiprack_loc[Axis.by_mount(mount)]) + await api.pick_up_tip( + mount, tip_length=tip_length[args.tip_size], + presses = 1, + increment = 0) + await api.home([Axis.Z_L]) + cp = CriticalPoint.TIP + await asyncio.sleep(1) + home_with_tip = await api.current_position(mount, cp) + print("Calibrate Drop Tip Position") + drop_tip_loc = await jog(api, home_with_tip, cp) + drop_tip_loc = Point(drop_tip_loc[Axis.X], + drop_tip_loc[Axis.Y], + drop_tip_loc[Axis.by_mount(mount)]) + return tiprack_loc, drop_tip_loc + +async def update_nozzle_manager(api, mount, tip_count): + if args.nozzles == 1: + await api.update_nozzle_configuration_for_mount(OT3Mount.LEFT, 'A1', 'A1') + elif args.nozzles == 8: + await api.update_nozzle_configuration_for_mount(OT3Mount.LEFT, 'A1', 'H1') + elif args.nozzles == 16: + await api.update_nozzle_configuration_for_mount(OT3Mount.LEFT, 'A1', 'H2') + elif args.nozzles == 24: + await api.update_nozzle_configuration_for_mount(OT3Mount.LEFT, 'A1', 'H1') + elif args.nozzles == 12: + await api.update_nozzle_configuration_for_mount(OT3Mount.LEFT, 'A1', 'A12') + +async def _main() -> None: + today = datetime.date.today() + hw_api = await build_async_ot3_hardware_api( + is_simulating=args.simulate, use_defaults=True + ) + await asyncio.sleep(1) + await hw_api.cache_instruments() + pipette_model = hw_api.get_all_attached_instr()[OT3Mount.LEFT]["pipette_id"] + dial_data = {"Column_1": None, "Column_2": None, "Column_3": None, + "Column_4": None, "Column_5": None, "Column_6": None, + "Column_7": None, "Column_8": None, "Column_9": None, + "Column_10": None, "Column_11": None, "Column_12": None} + m_current = float(input("motor_current in amps: ")) + await update_nozzle_manager(hw_api, OT3Mount.LEFT, args.nozzles) + instrument = hw_api._pipette_handler.get_pipette(OT3Mount.LEFT) + # pick_up_speed = float(input("pick up tip speed in mm/s: ")) + details = [pipette_model, m_current] + test_n, test_f = file_setup(dial_data, details) + file_name = "/home/root/.opentrons/testing_data/pickup_tip_test/pu_96_pipette_%s-%s.csv" % ( + m_current, + datetime.datetime.now().strftime("%m-%d-%y_%H-%M"), + ) + lp_file_name = '/var/{}-P-{}_Z-{}-{}.csv'.format( pipette_model, + args.plunger_speed, + args.mount_speed, + today.strftime("%b-%d-%Y")) + liquid_probe_settings = LiquidProbeSettings( + # starting_mount_height = 100, + max_z_distance = args.max_z_distance, + min_z_distance = args.min_z_distance, + mount_speed = args.mount_speed, + plunger_speed = args.plunger_speed, + sensor_threshold_pascals = args.sensor_threshold, + expected_liquid_height = args.expected_liquid_height, + log_pressure = args.log_pressure, + aspirate_while_sensing = False, + auto_zero_sensor = False, + num_baseline_reads = 10, + data_file = lp_file_name, + ) + try: + await hw_api.home() + await asyncio.sleep(1) + # await hw_api.home_plunger(mount) + await hw_api.set_lights(rails=True) + plunger_pos = get_plunger_positions_ot3(hw_api, mount) + print(plunger_pos) + home_position = await hw_api.current_position_ot3(mount) + start_time = time.perf_counter() + m_current = float(input("motor_current in amps: ")) + pick_up_speed = float(input("pick up tip speed in mm/s: ")) + hw_api.clamp_tip_speed = float(input("clamp pick up Speed: ")) + pick_up_distance = float(input("pick up distance in mm: ")) + await update_pick_up_current(hw_api, mount, m_current) + await update_pick_up_speed(hw_api, mount, pick_up_speed) + await update_pick_up_distance(hw_api, mount, pick_up_distance) + # if (args.calibrate): + # cp = CriticalPoint.NOZZLE + # home_w_tip = await hw_api.current_position_ot3(mount, cp) + # initial_dial_loc = Point( + # deck_slot['deck_slot'][args.dial_slot]['X'], + # deck_slot['deck_slot'][args.dial_slot]['Y'], + # home_w_tip[Axis.by_mount(mount)] + # ) + # print("Move Nozzle to Dial Indicator") + # await move_to_point(hw_api, mount, initial_dial_loc, cp) + # current_position = await hw_api.current_position_ot3(mount, cp) + # nozzle_loc = await jog(hw_api, current_position, cp) + # number_of_channels = 96 + # nozzle_count = 0 + # x_offset = 0 + # y_offset = 0 + # measurements = [] + # num_of_columns = 12 + # for tip in range(1, number_of_channels + 1): + # cp = CriticalPoint.NOZZLE + # nozzle_count += 1 + # nozzle_position = Point(nozzle_loc[Axis.X] + x_offset, + # nozzle_loc[Axis.Y] + y_offset, + # nozzle_loc[Axis.by_mount(mount)]) + # await move_to_point(hw_api, mount, nozzle_position, cp) + # await asyncio.sleep(1) + # nozzle_measurement = gauge.read() + # print("nozzle-",nozzle_count, "(mm): " , nozzle_measurement, end="") + # print("\r", end="") + # measurements.append(nozzle_measurement) + # if nozzle_count % num_of_columns == 0: + # d_str = '' + # for m in measurements: + # d_str += str(m) + ',' + # d_str = d_str[:-1] + '\n' + # print(f"{d_str}") + # data.append_data_to_file(test_n, test_f, d_str) + # # Reset Measurements list + # measurements = [] + # print("\r\n") + # x_offset -= 9 + # if nozzle_count % num_of_columns == 0: + # y_offset += 9 + # if nozzle_count % num_of_columns == 0: + # x_offset = 0 + # Calibrate to tiprack + if (args.calibrate): + pickup_loc, droptip_loc = await calibrate_tiprack(hw_api, home_position, mount) + print(pickup_loc) + deck_slot['deck_slot'][args.tiprack_slot][Axis.X.name] = pickup_loc.x + deck_slot['deck_slot'][args.tiprack_slot][Axis.Y.name] = pickup_loc.y + deck_slot['deck_slot'][args.tiprack_slot]['Z'] = pickup_loc.z + save_config_(path+cal_fn, deck_slot) + + await hw_api.home_z(mount) + cp = CriticalPoint.TIP + home_w_tip = await hw_api.current_position_ot3(mount, cp) + # Calibrate Dial Indicator with single tip + if (args.calibrate): + # cp = CriticalPoint.TIP + initial_dial_loc = Point( + deck_slot['deck_slot'][args.dial_slot]['X'], + deck_slot['deck_slot'][args.dial_slot]['Y'], + home_w_tip[Axis.by_mount(mount)]) + print("Move to Dial Indicator") + await move_to_point(hw_api, mount, initial_dial_loc, cp) + current_position = await hw_api.current_position_ot3(mount, cp) + dial_loc = await jog(hw_api, current_position, cp) + dial_loc = Point(dial_loc[Axis.X], + dial_loc[Axis.Y], + dial_loc[Axis.by_mount(mount)]) + deck_slot['deck_slot'][args.dial_slot][Axis.X.name] = dial_loc.x + deck_slot['deck_slot'][args.dial_slot][Axis.Y.name] = dial_loc.y + deck_slot['deck_slot'][args.dial_slot]['Z'] = dial_loc.z + save_config_(path+cal_fn, deck_slot) + if (args.trough): + cp = CriticalPoint.TIP + trough_loc = Point(deck_slot['deck_slot'][args.trough_slot]['X'], + deck_slot['deck_slot'][args.trough_slot]['Y'], + home_w_tip[Axis.by_mount(mount)]) + print("Move to Trough") + await move_to_point(hw_api, mount, trough_loc, cp) + current_position = await hw_api.current_position_ot3(mount, cp) + trough_loc = await jog(hw_api, current_position, cp) + trough_loc = Point(trough_loc[Axis.X], + trough_loc[Axis.Y], + trough_loc[Axis.by_mount(mount)]) + deck_slot['deck_slot'][args.trough_slot][Axis.X.name] = dial_loc.x + deck_slot['deck_slot'][args.trough_slot][Axis.Y.name] = dial_loc.y + deck_slot['deck_slot'][args.trough_slot]['Z'] = dial_loc.z + save_config_(path+cal_fn, deck_slot) + + num_of_columns = int(input("How many Columns: ")) + num_of_rows = int(input("Number of Rows: ")) + tips_to_use = (num_of_rows * num_of_columns) + # tips_to_use = (num_of_columns * 8) + while True: + measurements = [] + tip_count = 0 + x_offset = 0 + y_offset = 0 + cp = CriticalPoint.TIP + if args.dial_indicator: + for tip in range(1, tips_to_use + 1): + cp = CriticalPoint.TIP + tip_count += 1 + tip_position = Point(dial_loc[0] + x_offset, + dial_loc[1] + y_offset, + dial_loc[2]) + await move_to_point(hw_api, mount, tip_position, cp) + await asyncio.sleep(1) + tip_measurement = gauge.read() + print("tip-",tip_count, "(mm): " ,tip_measurement, end="") + print("\r", end="") + measurements.append(tip_measurement) + if tip_count % num_of_columns == 0: + d_str = '' + for m in measurements: + d_str += str(m) + ',' + d_str = d_str[:-1] + '\n' + print(f"{d_str}") + data.append_data_to_file(test_n, test_f, d_str) + # Reset Measurements list + measurements = [] + print("\r\n") + x_offset -= 9 + # if tip_count % num_of_column == 0: + if tip_count % num_of_columns == 0: + y_offset += 9 + if tip_count % num_of_columns == 0: + x_offset = 0 + + if args.trough: + await hw_api.prepare_for_aspirate(mount) + await move_to_point(hw_api, mount, trough_loc, cp) + await hw_api.aspirate(mount, test_volume) + await hw_api.home_z(mount) + await countdown(leak_test_time) + await move_to_point(hw_api, mount, trough_loc, cp) + await hw_api.dispense(mount) + # await hw_api.home_z(mount) + # hw_api.clamp_drop_tip_speed = float(input("Drop tip speed: ")) + # await update_drop_tip_speed(hw_api, mount, hw_api.clamp_drop_tip_speed ) + cp = CriticalPoint.TIP + await move_to_point(hw_api, mount, droptip_loc, cp) + input("Feel the Tip!") + await hw_api.drop_tip(mount) + await hw_api.home_z(mount) + + m_current = float(input("motor_current in amps: ")) + pick_up_speed = float(input("prep pick up tip speed in mm/s: ")) + # Pick up distance i originally used was 16.5 + pick_up_distance = float(input("pick up distance in mm: ")) + hw_api.clamp_tip_speed = float(input("clamp pick up Speed: ")) + num_of_columns = int(input("How many Columns: ")) + num_of_rows = int(input("Number of Rows: ")) + tips_to_use = (num_of_rows * num_of_columns) + # tips_to_use = num_of_columns * 8 + await update_pick_up_current(hw_api, mount, m_current) + await update_pick_up_speed(hw_api, mount, pick_up_speed) + await update_pick_up_distance(hw_api, mount, pick_up_distance) + cp = CriticalPoint.NOZZLE + if args.columns: + column = float(input("How many Columns to Move: ")) + column = column*9 + pickup_loc = Point(pickup_loc[0] - column, + pickup_loc[1], + pickup_loc[2]) + else: + row = float(input("How many Row to Move: ")) + row = row*9 + pickup_loc = Point(pickup_loc[0], + pickup_loc[1] + row, + pickup_loc[2]) + await move_to_point(hw_api, mount, pickup_loc, cp) + await hw_api.pick_up_tip(mount, + tip_length=tip_length[args.tip_size], + presses = 1, + increment = 0) + await hw_api.home_z(mount.LEFT) + cp = CriticalPoint.TIP + current_position = await hw_api.current_position_ot3(mount, cp) + this_position = await jog(hw_api, current_position, cp) + input("Press Enter to continue") + + except KeyboardInterrupt: + await hw_api.disengage_axes([Axis.X, Axis.Y]) + finally: + await hw_api.disengage_axes([Axis.X, Axis.Y]) + await hw_api.clean_up() + + +if __name__ == "__main__": + slot_locs = [ + "A1", + "A2", + "A3", + "B1", + "B2", + "B3:", + "C1", + "C2", + "C3", + "D1", + "D2", + "D3", + ] + parser = argparse.ArgumentParser() + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--trough", action="store_true") + parser.add_argument("--tiprack", action="store_true") + parser.add_argument("--mount", type=str, choices=["left", "right"], default="left") + parser.add_argument("--tiprack_slot", type=str, choices=slot_locs, default="B2") + parser.add_argument("--dial_slot", type=str, choices=slot_locs, default="C2") + parser.add_argument("--trough_slot", type=str, choices=slot_locs, default="B3") + parser.add_argument("--dial_indicator", action="store_true") + parser.add_argument("--calibrate", action="store_true") + parser.add_argument("--columns", action="store_true") + parser.add_argument("--tip_size", type=str, default="T1K", help="Tip Size") + parser.add_argument("--max_z_distance", type=float, default=40) + parser.add_argument("--nozzles", type=int, default=96) + parser.add_argument("--min_z_distance", type=float, default=5) + parser.add_argument("--mount_speed", type=float, default=5) + parser.add_argument("--plunger_speed", type=float, default=10) + parser.add_argument( + "--sensor_threshold", type=float, default=100, help="Threshold in Pascals" + ) + parser.add_argument("--expected_liquid_height", type=int, default=0) + parser.add_argument("--log_pressure", action="store_true") + parser.add_argument( + "--dial_port", type=str, default="/dev/ttyUSB0", help="Dial indicator Port" + ) + args = parser.parse_args() + path = '/data/testing_data/' + cal_fn = 'calibrations.json' + if args.calibrate: + with open(path + cal_fn, 'r') as openfile: + deck_slot = json.load(openfile) + print(deck_slot) + else: + with open(path + cal_fn, 'r') as openfile: + deck_slot = json.load(openfile) + tip_length = {"T1K": 95.7, "T200": 58.35, "T50": 57.9} + if args.mount == "left": + mount = OT3Mount.LEFT + else: + mount = OT3Mount.RIGHT + + if args.dial_indicator: + gauge = dial_indicator_setup(port=args.dial_port) + asyncio.run(_main()) From 56e8c825dbbd8053f8f724da8b15f12378267f42 Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 28 Feb 2024 14:55:56 -0500 Subject: [PATCH 53/53] press fit --- .../hardware_testing/opentrons_api/helpers_ot3.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py index f6920eb62f3..9c58dd6da7c 100644 --- a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py +++ b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py @@ -557,11 +557,12 @@ async def update_pick_up_current( ) -> None: """Update pick-up-tip current.""" pipette = _get_pipette_from_mount(api, mount) - config_model = pipette.pick_up_configurations.press_fit - config_model.current_by_tip_count = { - k: current for k in config_model.current_by_tip_count.keys() + config_model = pipette.pick_up_configurations + print(f'config: {config_model}') + config_model.press_fit.current_by_tip_count = { + k: current for k in config_model.press_fit.current_by_tip_count.keys() } - pipette.pick_up_configurations.press_fit = config_model + pipette.pick_up_configurations = config_model async def update_drop_tip_current( api: OT3API, mount: OT3Mount, current: float = 1.0