Esempio n. 1
0
def test_aspirate_move_to(old_aspiration, robot, instruments):
    # TODO: it seems like this test is checking that the aspirate point is
    # TODO: *fully* at the bottom of the well, which isn't the expected
    # TODO: behavior of aspirate when a location is not specified. This should
    # TODO: be split into two tests--one for this behavior (specifying a place)
    # TODO: and another one for the default
    robot.reset()
    tip_rack = containers_load(robot, 'tiprack-200ul', '3')
    p300 = instruments.P300_Single(mount='left', tip_racks=[tip_rack])
    p300.pick_up_tip()

    x, y, z = (161.0, 116.7, 0.0)
    plate = containers_load(robot, '96-flat', '1')
    well = plate[0]
    pos = well.from_center(x=0, y=0, z=-1, reference=plate)
    location = (plate, pos)

    robot.poses = p300._move(robot.poses, x=x, y=y, z=z)
    robot.calibrate_container_with_instrument(plate, p300, False)

    p300.aspirate(100, location)
    current_pos = pose_tracker.absolute(robot.poses, p300.instrument_actuator)

    assert isclose(current_pos, (6.9, 0.0, 0.0)).all()

    current_pos = pose_tracker.absolute(robot.poses, p300)
    assert isclose(current_pos, (161, 116.7, 10.5)).all()
Esempio n. 2
0
def _move_mount(robot, mount, point):
    """
    The carriage moves the mount in the Z axis, and the gantry moves in X and Y

    Mount movements do not have the same protections calculated in to an
    existing `move` command like Pipette does, so the safest thing is to home
    the Z axis, then move in X and Y, then move down to the specified Z height
    """
    carriage = robot._actuators[mount]['carriage']

    # Home both carriages, to prevent collisions and to ensure that the other
    # mount doesn't block the one being moved (mount moves are primarily for
    # changing pipettes, so we don't want the other pipette blocking access)
    robot.poses = carriage.home(robot.poses)
    other_mount = 'left' if mount == 'right' else 'right'
    robot.poses = robot._actuators[other_mount]['carriage'].home(robot.poses)

    robot.gantry.move(
        robot.poses, x=point[0], y=point[1])
    robot.poses = carriage.move(
        robot.poses, z=point[2])

    # These x and y values are hard to interpret because of some internals of
    # pose tracker. It's mostly z that matters for this operation anyway
    x, y, _ = tuple(
        pose_tracker.absolute(
            robot.poses, robot._actuators[mount]['carriage']))
    _, _, z = tuple(
        pose_tracker.absolute(
            robot.poses, robot.gantry))
    new_position = (x, y, z)
    return "Move complete. New position: {}".format(new_position)
Esempio n. 3
0
def test_dispense_move_to(old_aspiration):
    # TODO: same as for aspirate
    robot.reset()
    tip_rack = containers_load(robot, 'tiprack-200ul', '3')
    p300 = instruments.P300_Single(
                   mount='left',
                   tip_racks=[tip_rack])

    x, y, z = (161.0, 116.7, 0.0)
    plate = containers_load(robot, '96-flat', '1')
    well = plate[0]
    pos = well.from_center(x=0, y=0, z=-1, reference=plate)
    location = (plate, pos)

    robot.poses = p300._move(robot.poses, x=x, y=y, z=z)
    robot.calibrate_container_with_instrument(plate, p300, False)

    p300.pick_up_tip()
    p300.aspirate(100, location)
    p300.dispense(100, location)
    current_pos = pose_tracker.absolute(
        robot.poses,
        p300.instrument_actuator)
    assert (current_pos == (1.5, 0.0, 0.0)).all()

    current_pos = pose_tracker.absolute(robot.poses, p300)
    assert isclose(current_pos, (161,  116.7,   10.5)).all()
def test_fixed_trash():
    robot.reset()
    p300 = instruments.P300_Single(mount='right')
    print(pose_tracker.absolute(robot.poses, p300))
    p300.move_to(p300.trash_container)
    print(pose_tracker.absolute(robot.poses, p300))
    assert isclose(pose_tracker.absolute(robot.poses, p300),
                   (355.0, 361.43, 85.0)).all()
Esempio n. 5
0
 def _test_offset(x, y, z):
     robot.config = robot.config._replace(mount_offset=(x, y, z))
     robot.reset()
     left = instruments.P300_Single(mount='left')
     right = instruments.P300_Single(mount='right')
     robot.home()
     left_pos = pose_tracker.absolute(robot.poses, left)
     right_pos = pose_tracker.absolute(robot.poses, right)
     assert left_pos[0] == (right_pos[0] + x)
     assert left_pos[1] == (right_pos[1] + y)
     assert left_pos[2] == (right_pos[2] + z)
Esempio n. 6
0
    def test_add_tip(self):
        """
        This deals with z accrual behavior during tip add/remove, when +/- get
        flipped in pose tracking logic
        """
        prior_position = pose_tracker.absolute(self.robot.poses, self.p200)
        self.p200._add_tip(42)
        self.p200._remove_tip(42)
        new_position = pose_tracker.absolute(self.robot.poses, self.p200)

        assert (new_position == prior_position).all()
def test_add_tip(local_test_pipette, robot):
    """
    This deals with z accrual behavior during tip add/remove, when +/- get
    flipped in pose tracking logic
    """
    trash, tiprack1, tiprack2, plate, p200 = local_test_pipette
    prior_position = pose_tracker.absolute(robot.poses, p200)
    p200._add_tip(42)
    p200._remove_tip(42)
    new_position = pose_tracker.absolute(robot.poses, p200)

    assert (new_position == prior_position).all()
async def test_jog_calibrate_top(
        dummy_db,
        main_router,
        model,
        monkeypatch):

    # Check that the old behavior remains the same without the feature flag
    from numpy import array, isclose
    from opentrons.trackers import pose_tracker
    import tempfile
    temp = tempfile.gettempdir()
    monkeypatch.setenv('USER_DEFN_ROOT', temp)

    robot = model.robot

    container = model.container._container
    container_coords1 = container.coordinates()
    pos1 = pose_tracker.absolute(robot.poses, container[0])
    coordinates1 = container[0].coordinates()

    main_router.calibration_manager.move_to(model.instrument, model.container)
    main_router.calibration_manager.jog(model.instrument, 1, 'x')
    main_router.calibration_manager.jog(model.instrument, 2, 'y')
    main_router.calibration_manager.jog(model.instrument, 3, 'z')

    main_router.calibration_manager.update_container_offset(
        model.container,
        model.instrument
    )

    container_coords2 = container.coordinates()
    pos2 = pose_tracker.absolute(robot.poses, container[0])
    coordinates2 = container[0].coordinates()

    assert isclose(
        array([*container_coords1]) + (1, 2, 3),
        array([*container_coords2])).all()
    assert isclose(pos1 + (1, 2, 3), pos2).all()
    assert isclose(
        array([*coordinates1]) + (1, 2, 3),
        array([*coordinates2])).all()

    main_router.calibration_manager.pick_up_tip(
        model.instrument,
        model.container
    )

    # NOTE: only check XY, as the instrument moves up after tip pickup
    assert isclose(
        pose_tracker.absolute(robot.poses, container[0])[:-1],
        pose_tracker.absolute(robot.poses, model.instrument._instrument)[:-1]
    ).all()
async def test_jog_calibrate_bottom_v1(
        dummy_db,
        main_router,
        model,
        calibrate_bottom_flag):

    # Check that the feature flag correctly implements calibrate to bottom
    from numpy import array, isclose
    from opentrons.trackers import pose_tracker

    robot = model.robot

    container = model.container._container
    pos1 = pose_tracker.change_base(
        robot.poses,
        src=container[0],
        dst=robot.deck)
    coordinates1 = container.coordinates()
    height = container['A1'].properties['height']

    main_router.calibration_manager.move_to(model.instrument, model.container)
    main_router.calibration_manager.jog(model.instrument, 1, 'x')
    main_router.calibration_manager.jog(model.instrument, 2, 'y')
    main_router.calibration_manager.jog(model.instrument, 3, 'z')
    main_router.calibration_manager.jog(model.instrument, -height, 'z')

    # Todo: make tests use a tmp dir instead of a real one
    main_router.calibration_manager.update_container_offset(
        model.container,
        model.instrument
    )

    pos2 = pose_tracker.absolute(robot.poses, container[0])
    coordinates2 = container.coordinates()

    assert isclose(pos1 + (1, 2, 3), pos2).all()
    assert isclose(
        array([*coordinates1]) + (1, 2, 3),
        array([*coordinates2])).all()

    main_router.calibration_manager.pick_up_tip(
        model.instrument,
        model.container
    )

    # NOTE: only check XY, as the instrument moves up after tip pickup
    assert isclose(
        pose_tracker.absolute(robot.poses, container[0])[:-1],
        pose_tracker.absolute(robot.poses, model.instrument._instrument)[:-1]
    ).all()
Esempio n. 10
0
async def move(request):
    """
    Moves the robot to the specified position as provided by the `control.info`
    endpoint response

    Post body must include the following keys:
    - 'target': either 'mount' or 'pipette'
    - 'point': a tuple of 3 floats for x, y, z
    - 'mount': must be 'left' or 'right'

    If 'target' is 'pipette', body must also contain:
    - 'model': must be a valid pipette model (as defined in `pipette_config`)
    """
    req = await request.text()
    data = json.loads(req)

    target, point, mount, model, message, error = _validate_move_data(data)
    if error:
        status = 400
    else:
        status = 200
        if target == 'mount':
            message = _move_mount(mount, point)
        elif target == 'pipette':
            pipette = _fetch_or_create_pipette(mount, model)
            pipette.move_to((robot.deck, point), strategy='arc')
            new_position = tuple(
                pose_tracker.absolute(pipette.robot.poses, pipette))
            message = "Move complete. New position: {}".format(new_position)

    return web.json_response({"message": message}, status=status)
Esempio n. 11
0
def test_retract(robot, instruments):
    robot.reset()
    plate = containers_load(robot, '96-flat', '1')
    p300 = instruments.P300_Single(mount='left')
    from opentrons.drivers.smoothie_drivers.driver_3_0 import HOMED_POSITION

    p300.move_to(plate[0].top())

    assert p300.previous_placeable == plate[0]
    current_pos = pose_tracker.absolute(robot.poses, p300)
    assert current_pos[2] == plate[0].coordinates()[2]

    p300.retract()

    assert p300.previous_placeable is None
    current_pos = pose_tracker.absolute(robot.poses, p300.instrument_mover)
    assert current_pos[2] == HOMED_POSITION['A']
Esempio n. 12
0
def test_move_head(robot):
    robot.move_head(x=100, y=0)
    assert isclose(
        pose_tracker.absolute(
            robot.poses,
            robot.gantry)[:2],
        (100, 0, 0)[:2]
    ).all()
Esempio n. 13
0
def test_move_head(virtual_smoothie_env):
    robot.reset()
    robot.move_head(x=100, y=0)
    assert isclose(
        pose_tracker.absolute(
            robot.poses,
            robot.gantry)[:2],
        (100, 0, 0)[:2]
    ).all()
Esempio n. 14
0
    def move_to(self, location, instrument, strategy='arc', **kwargs):
        """
        Move an instrument to a coordinate, container or a coordinate within
        a container.

        Parameters
        ----------
        location : one of the following:
            1. :class:`Placeable` (i.e. Container, Deck, Slot, Well) — will
            move to the origin of a container.
            2. :class:`Vector` move to the given coordinate in Deck coordinate
            system.
            3. (:class:`Placeable`, :class:`Vector`) move to a given coordinate
            within object's coordinate system.

        instrument :
            Instrument to move relative to. If ``None``, move relative to the
            center of a gantry.

        strategy : {'arc', 'direct'}
            ``arc`` : move to the point using arc trajectory
            avoiding obstacles.

            ``direct`` : move to the point in a straight line.
        """

        placeable, coordinates = containers.unpack_location(location)

        # because the top position is what is tracked,
        # this checks if coordinates doesn't equal top
        offset = subtract(coordinates, placeable.top()[1])

        if isinstance(placeable, containers.WellSeries):
            placeable = placeable[0]

        target = add(pose_tracker.absolute(self.poses, placeable),
                     offset.coordinates)

        if self._previous_instrument:
            if self._previous_instrument != instrument:
                self._previous_instrument.retract()
                # because we're switching pipettes, this ensures a large (safe)
                # Z arc height will be used for the new pipette
                self._prev_container = None

        self._previous_instrument = instrument

        if strategy == 'arc':
            arc_coords = self._create_arc(instrument, target, placeable)
            for coord in arc_coords:
                self.poses = instrument._move(self.poses, **coord)

        elif strategy == 'direct':
            position = {'x': target[0], 'y': target[1], 'z': target[2]}
            self.poses = instrument._move(self.poses, **position)
        else:
            raise RuntimeError('Unknown move strategy: {}'.format(strategy))
Esempio n. 15
0
def test_calibrate_labware(robot, instruments, labware, monkeypatch):
    import tempfile
    temp = tempfile.mkdtemp()
    monkeypatch.setenv('USER_DEFN_ROOT', temp)

    plate = labware.load('96-flat', '1')
    pip = instruments.P300_Single(mount='right')

    old_x, old_y, old_z = pose_tracker.absolute(robot.poses, plate[0])

    pip.move_to(plate[0])
    robot.poses = pip._jog(robot.poses, 'x', 1)
    robot.poses = pip._jog(robot.poses, 'y', 2)
    robot.poses = pip._jog(robot.poses, 'z', -3)
    robot.calibrate_container_with_instrument(plate, pip, save=False)
    new_pose = pose_tracker.absolute(robot.poses, plate[0])

    assert isclose(new_pose, (old_x + 1, old_y + 2, old_z - 3)).all()
Esempio n. 16
0
def test_robot_move_to(virtual_smoothie_env):
    robot.reset()
    robot.home()
    p300 = instruments.P300_Single(mount='right')
    robot.move_to((robot._deck, (100, 0, 0)), p300)
    assert isclose(
        pose_tracker.absolute(
            robot.poses,
            p300),
        (100, 0, 0)
    ).all()
Esempio n. 17
0
 def max_placeable_height_on_deck(self, placeable):
     """
     :param placeable:
     :return: Calibrated height of container in mm from
     deck as the reference point
     """
     offset = placeable.top()[1]
     placeable_coordinate = add(
         pose_tracker.absolute(self.poses, placeable), offset.coordinates)
     placeable_tallest_point = pose_tracker.max_z(self.poses, placeable)
     return placeable_coordinate[2] + placeable_tallest_point
Esempio n. 18
0
def test_pause_resume(model):
    """
    This test has to use an ugly work-around with the `simulating` member of
    the driver. When issuing movement commands in test, `simulating` should be
    True, but when testing whether `pause` actually pauses and `resume`
    resumes, `simulating` must be False.
    """
    from numpy import isclose
    from opentrons.trackers import pose_tracker
    from time import sleep

    pipette = model.instrument._instrument
    robot = model.robot

    robot.home()
    homed_coords = pose_tracker.absolute(robot.poses, pipette)

    robot._driver.simulating = False
    robot.pause()
    robot._driver.simulating = True

    def _move_head():
        robot.poses = pipette._move(robot.poses, x=100, y=0, z=0)

    thread = Thread(target=_move_head)
    thread.start()
    sleep(0.5)

    # Check against home coordinates before calling resume to ensure that robot
    # doesn't move while paused
    coords = pose_tracker.absolute(robot.poses, pipette)
    assert isclose(coords, homed_coords).all()

    robot._driver.simulating = False
    robot.resume()
    robot._driver.simulating = True
    thread.join()

    coords = pose_tracker.absolute(robot.poses, pipette)
    expected_coords = (100, 0, 0)
    assert isclose(coords, expected_coords).all()
Esempio n. 19
0
def test_trough_move_to():
    from opentrons.instruments.pipette_config import Y_OFFSET_MULTI
    robot.reset()
    tip_rack = containers_load(robot, 'tiprack-200ul', '3')
    p300 = instruments.P300_Single(mount='left', tip_racks=[tip_rack])

    trough = containers_load(robot, 'trough-12row', '1')
    p300.pick_up_tip()
    p300.move_to(trough)
    current_pos = pose_tracker.absolute(robot.poses, p300)

    assert isclose(current_pos, (14.34, 7.75 + 35 + Y_OFFSET_MULTI, 40)).all()
Esempio n. 20
0
def test_trough_move_to(robot, instruments):
    # TODO: new labware system should center multichannel pipettes within wells
    # TODO: (correct single-channel position currently corresponds to back-
    # TODO: most tip of multi-channel), so calculate against that
    robot.reset()
    tip_rack = containers_load(robot, 'tiprack-200ul', '3')
    p300 = instruments.P300_Single(mount='left', tip_racks=[tip_rack])

    trough = containers_load(robot, 'trough-12row', '1')
    p300.pick_up_tip()
    p300.move_to(trough)
    current_pos = pose_tracker.absolute(robot.poses, p300)
    assert isclose(current_pos, (0, 0, 38)).all()
Esempio n. 21
0
def test_calibrate_labware_new(
        virtual_smoothie_env, split_labware_def):
    robot.reset()

    plate = labware.load('96-flat', '5')
    pip = instruments.P300_Single(mount='right')

    old_x, old_y, old_z = pose_tracker.absolute(robot.poses, plate[0])

    pip.move_to(plate[0])
    robot.poses = pip._jog(robot.poses, 'x', 1)
    robot.poses = pip._jog(robot.poses, 'y', 2)
    robot.poses = pip._jog(robot.poses, 'z', -3)
    robot.calibrate_container_with_instrument(plate, pip, save=True)
    new_pose = pose_tracker.absolute(robot.poses, plate[0])

    assert isclose(new_pose, (old_x + 1, old_y + 2, old_z - 3)).all()

    # Ensure that slot offset is removed and new offset file is picked up
    plate2 = labware.load('96-flat', '1')
    new_pose2 = pose_tracker.absolute(robot.poses, plate2[0])
    assert isclose(new_pose2, (15.34, 76.24, 7.5)).all()
Esempio n. 22
0
def test_pipette_models_reach_max_volume(robot, instruments):

    for model in pipette_config.config_models:
        config = pipette_config.load(model)
        robot.reset()
        pipette = instruments._create_pipette_from_config(config=config,
                                                          mount='right',
                                                          name=model)

        pipette.tip_attached = True
        pipette.aspirate(pipette.max_volume)
        pos = pose_tracker.absolute(robot.poses, pipette.instrument_actuator)
        assert pos[0] < pipette.plunger_positions['top']
Esempio n. 23
0
def test_calibrate_multiple(robot, instruments, labware, offsets_tempdir):
    # Note: labware_name must be a v2 labware definition
    labware_name = 'agilent_1_reservoir_290ml'

    reservoir1 = labware.load(labware_name, '1')
    reservoir2 = labware.load(labware_name, '2')

    pip = instruments.P300_Single(mount='right')

    old_x1, old_y1, old_z1 = pose_tracker.absolute(robot.poses, reservoir1[0])
    old_x2, old_y2, old_z2 = pose_tracker.absolute(robot.poses, reservoir2[0])

    pip.move_to(reservoir1[0])
    delta_x1, delta_y1, delta_z1 = (1, 2, -3)
    robot.poses = pip._jog(robot.poses, 'x', delta_x1)
    robot.poses = pip._jog(robot.poses, 'y', delta_y1)
    robot.poses = pip._jog(robot.poses, 'z', delta_z1)
    robot.calibrate_container_with_instrument(reservoir1, pip, save=True)

    # Check pose tree, also check data on disk
    new_pose1 = pose_tracker.absolute(robot.poses, reservoir1[0])
    new_pose2 = pose_tracker.absolute(robot.poses, reservoir2[0])

    assert isclose(new_pose1, (
        old_x1 + delta_x1, old_y1 + delta_y1, old_z1 + delta_z1)).all()
    assert isclose(new_pose2, (
        old_x2 + delta_x1, old_y2 + delta_y1, old_z2 + delta_z1)).all()

    lw_hash = reservoir1.properties.get('labware_hash')
    new_offset1 = _look_up_offsets(lw_hash)
    expected1 = Point(delta_x1, delta_y1, delta_z1)
    assert isclose(new_offset1, expected1).all()

    pip.move_to(reservoir2[0])
    robot.poses = pip._jog(robot.poses, 'x', -1 * delta_x1)
    robot.poses = pip._jog(robot.poses, 'y', -1 * delta_y1)
    robot.poses = pip._jog(robot.poses, 'z', -1 * delta_z1)
    robot.calibrate_container_with_instrument(reservoir2, pip, save=True)

    # Check pose tree, also check data on disk
    new_pose1 = pose_tracker.absolute(robot.poses, reservoir1[0])
    new_pose2 = pose_tracker.absolute(robot.poses, reservoir2[0])

    assert isclose(new_pose1, (
        old_x1, old_y1, old_z1)).all()
    assert isclose(new_pose2, (
        old_x2, old_y2, old_z2)).all()

    lw_hash = reservoir1.properties.get('labware_hash')
    new_offset2 = _look_up_offsets(lw_hash)
    expected2 = Point(0, 0, 0)
    assert isclose(new_offset2, expected2).all()
Esempio n. 24
0
    def _create_arc(self, inst, destination, placeable=None):
        """
        Returns a list of coordinates to arrive to the destination coordinate
        """
        this_container = None
        if isinstance(placeable, containers.Well):
            this_container = placeable.get_parent()
        elif isinstance(placeable, containers.WellSeries):
            this_container = placeable.get_parent()
        elif isinstance(placeable, containers.Container):
            this_container = placeable

        if this_container and self._prev_container == this_container:
            # movements that stay within the same container do not need to
            # avoid other containers on the deck, so the travel height of
            # arced movements can be relative to just that one container
            arc_top = self.max_placeable_height_on_deck(this_container)
            arc_top += TIP_CLEARANCE_LABWARE
        elif self._use_safest_height:
            # bring the pipettes up as high as possible while calibrating
            arc_top = inst._max_deck_height()
        else:
            # bring pipette up above the tallest container currently on deck
            arc_top = self.max_deck_height() + TIP_CLEARANCE_DECK

        self._prev_container = this_container

        # if instrument is currently taller than arc_top, don't move down
        _, _, pip_z = pose_tracker.absolute(self.poses, inst)

        arc_top = max(arc_top, destination[2], pip_z)
        arc_top = min(arc_top, inst._max_deck_height())

        strategy = [{
            'z': arc_top
        }, {
            'x': destination[0],
            'y': destination[1]
        }, {
            'z': destination[2]
        }]

        return strategy
Esempio n. 25
0
def _move_pipette(robot, mount, model, point):
    pipette, _ = _fetch_or_create_pipette(robot, mount, model)
    pipette.move_to((robot.deck, point), strategy='arc')
    new_position = tuple(
        pose_tracker.absolute(pipette.robot.poses, pipette))
    return "Move complete. New position: {}".format(new_position)
Esempio n. 26
0
def probe_instrument(instrument, robot, tip_length=None) -> Point:
    robot.home()
    tp = robot.config.tip_probe
    if tip_length is None:
        tip_length = robot.config.tip_length[instrument.model]
    instrument._add_tip(tip_length)

    # probe_center is the point at the center of the switch pcb
    center = Point(*tp.center)

    hot_spots = robot_configs.calculate_tip_probe_hotspots(tip_length, tp)

    # The saved axis positions from limit switch response
    axis_pos = []

    safe_height = _calculate_safeheight(robot, tp.z_clearance.crossover)

    log.info("Moving to safe z: {}".format(safe_height))
    robot.poses = instrument._move(robot.poses, z=safe_height)

    for hs in hot_spots:
        x0 = tp.center[0] + hs.x_start_offs
        y0 = tp.center[1] + hs.y_start_offs
        z0 = hs.z_start_abs

        log.info("Moving to {}".format((x0, y0, z0)))
        robot.poses = instrument._move(robot.poses, x=x0, y=y0)
        robot.poses = instrument._move(robot.poses, z=z0)

        axis_index = 'xyz'.index(hs.axis)
        robot.poses = instrument._probe(robot.poses, hs.axis,
                                        hs.probe_distance)

        # Tip position is stored in accumulator and averaged for each axis
        # to be used for more accurate positioning for the next axis
        value = absolute(robot.poses, instrument)[axis_index]
        axis_pos.append(value)

        # after probing two points along the same axis
        # average them out, update center and clear accumulator
        # except Z, we're only probing that once
        if hs.axis == 'z':
            center = center._replace(**{hs.axis: axis_pos[0]})
            axis_pos.clear()
        elif len(axis_pos) == 2:
            center = center._replace(
                **{hs.axis: (axis_pos[0] + axis_pos[1]) / 2.0})

            axis_pos.clear()

        log.debug("Current axis positions for {}: {}".format(
            hs.axis, axis_pos))

        # Bounce back to release end stop
        sgn = hs.probe_distance / abs(hs.probe_distance)
        bounce = value + (tp.bounce_distance * -sgn)

        robot.poses = instrument._move(robot.poses, **{hs.axis: bounce})
        robot.poses = instrument._move(robot.poses, z=safe_height)

        log.debug("Updated center point tip probe {}".format(center))

    instrument._remove_tip(tip_length)

    return center
Esempio n. 27
0
async def test_transform_from_moves(async_client, monkeypatch):
    test_mount, test_model = ('left', 'p300_multi_v1')

    # test_mount, test_model = ('right', 'p300_single_v1')

    def dummy_read_model(mount):
        if mount == test_mount:
            return test_model
        else:
            return None

    monkeypatch.setattr(robot._driver, 'read_pipette_model', dummy_read_model)

    robot.reset()
    robot.home()

    # This is difficult to test without the `async_client` because it has to
    # take an `aiohttp.web.Request` object as a parameter instead of a dict
    resp = await async_client.post('/calibration/deck/start')
    start_res = await resp.json()
    token = start_res.get('token')

    assert start_res.get('pipette', {}).get('mount') == test_mount
    assert start_res.get('pipette', {}).get('model') == test_model

    res = await async_client.post('/calibration/deck',
                                  json={
                                      'token': token,
                                      'command': 'attach tip',
                                      'tipLength': 51.7
                                  })
    assert res.status == 200

    res = await async_client.post('/calibration/deck',
                                  json={
                                      'token': token,
                                      'command': 'move',
                                      'point': 'safeZ'
                                  })
    assert res.status == 200
    jogres = await async_client.post('/calibration/deck',
                                     json={
                                         'token': token,
                                         'command': 'jog',
                                         'axis': 'z',
                                         'direction': -1,
                                         'step': 4.5
                                     })
    assert jogres.status == 200
    res = await async_client.post('/calibration/deck',
                                  json={
                                      'token': token,
                                      'command': 'save z'
                                  })
    assert res.status == 200

    pipette = endpoints.session.pipettes[test_mount]

    res = await async_client.post('/calibration/deck',
                                  json={
                                      'token': token,
                                      'command': 'move',
                                      'point': '1'
                                  })
    assert res.status == 200
    pt1 = endpoints.safe_points().get('1')
    if 'multi' in test_model:
        expected1 = (pt1[0], pt1[1] + 2 * Y_OFFSET_MULTI, pt1[2])
    else:
        expected1 = pt1
    assert np.isclose(absolute(robot.poses, pipette), expected1).all()

    # Jog to calculated position for transform
    x_delta1 = 13.16824337 - dc.endpoints.safe_points()['1'][0]
    y_delta1 = 8.30855312 - dc.endpoints.safe_points()['1'][1]
    jogres = await async_client.post('/calibration/deck',
                                     json={
                                         'token': token,
                                         'command': 'jog',
                                         'axis': 'x',
                                         'direction': 1,
                                         'step': x_delta1
                                     })
    assert jogres.status == 200
    jogres = await async_client.post('/calibration/deck',
                                     json={
                                         'token': token,
                                         'command': 'jog',
                                         'axis': 'y',
                                         'direction': 1,
                                         'step': y_delta1
                                     })
    assert jogres.status == 200

    res = await async_client.post('/calibration/deck',
                                  json={
                                      'token': token,
                                      'command': 'save xy',
                                      'point': '1'
                                  })
    assert res.status == 200

    res = await async_client.post('/calibration/deck',
                                  json={
                                      'token': token,
                                      'command': 'move',
                                      'point': '2'
                                  })
    assert res.status == 200
    pt2 = endpoints.safe_points().get('2')
    if 'multi' in test_model:
        expected2 = (pt2[0], pt2[1] + 2 * Y_OFFSET_MULTI, pt2[2])
    else:
        expected2 = pt2
    assert np.isclose(absolute(robot.poses, pipette), expected2).all()

    # Jog to calculated position for transform
    x_delta2 = 380.50507635 - dc.endpoints.safe_points()['2'][0]
    y_delta2 = -23.82925545 - dc.endpoints.safe_points()['2'][1]
    jogres = await async_client.post('/calibration/deck',
                                     json={
                                         'token': token,
                                         'command': 'jog',
                                         'axis': 'x',
                                         'direction': 1,
                                         'step': x_delta2
                                     })
    assert jogres.status == 200
    jogres = await async_client.post('/calibration/deck',
                                     json={
                                         'token': token,
                                         'command': 'jog',
                                         'axis': 'y',
                                         'direction': 1,
                                         'step': y_delta2
                                     })
    assert jogres.status == 200

    res = await async_client.post('/calibration/deck',
                                  json={
                                      'token': token,
                                      'command': 'save xy',
                                      'point': '2'
                                  })
    assert res.status == 200

    res = await async_client.post('/calibration/deck',
                                  json={
                                      'token': token,
                                      'command': 'move',
                                      'point': '3'
                                  })
    assert res.status == 200
    pt3 = endpoints.safe_points().get('3')
    if 'multi' in test_model:
        expected3 = (pt3[0], pt3[1] + 2 * Y_OFFSET_MULTI, pt3[2])
    else:
        expected3 = pt3
    assert np.isclose(absolute(robot.poses, pipette), expected3).all()

    # Jog to calculated position for transform
    x_delta3 = 34.87002331 - dc.endpoints.safe_points()['3'][0]
    y_delta3 = 256.36103295 - dc.endpoints.safe_points()['3'][1]
    jogres = await async_client.post('/calibration/deck',
                                     json={
                                         'token': token,
                                         'command': 'jog',
                                         'axis': 'x',
                                         'direction': 1,
                                         'step': x_delta3
                                     })
    assert jogres.status == 200
    jogres = await async_client.post('/calibration/deck',
                                     json={
                                         'token': token,
                                         'command': 'jog',
                                         'axis': 'y',
                                         'direction': 1,
                                         'step': y_delta3
                                     })
    assert jogres.status == 200

    res = await async_client.post('/calibration/deck',
                                  json={
                                      'token': token,
                                      'command': 'save xy',
                                      'point': '3'
                                  })
    assert res.status == 200

    res = await async_client.post('/calibration/deck',
                                  json={
                                      'token': token,
                                      'command': 'save transform'
                                  })
    assert res.status == 200
    res = await async_client.post('/calibration/deck',
                                  json={
                                      'token': token,
                                      'command': 'release'
                                  })
    assert res.status == 200

    # This transform represents a 5 degree rotation, with a shift in x, y, & z.
    # Values for the points and expected transform come from a hand-crafted
    # transformation matrix and the points that would generate that matrix.
    cos_5deg_p = 0.99619469809
    sin_5deg_p = 0.08715574274
    sin_5deg_n = -sin_5deg_p
    const_zero = 0.0
    const_one_ = 1.0
    delta_x___ = 0.3
    delta_y___ = 0.4
    delta_z___ = 0.5
    expected_transform = [[cos_5deg_p, sin_5deg_p, const_zero, delta_x___],
                          [sin_5deg_n, cos_5deg_p, const_zero, delta_y___],
                          [const_zero, const_zero, const_one_, delta_z___],
                          [const_zero, const_zero, const_zero, const_one_]]

    actual_transform = robot.config.gantry_calibration
    from pprint import pprint
    print()
    print("Expected transform [type: {}]:".format(type(expected_transform)))
    pprint(expected_transform)

    print()
    print("Actual transform [type: {}]:".format(type(actual_transform)))
    pprint(actual_transform)
    assert np.allclose(actual_transform, expected_transform)
Esempio n. 28
0
 def tip_coords_absolute(self):
     xyz = pose_tracker.absolute(self.robot.poses, self)
     return Vector(xyz)
Esempio n. 29
0
def probe_instrument(instrument, robot, tip_length=None) -> Point:
    robot.home()

    if tip_length is None:
        tip_length = robot.config.tip_length[instrument.name]
    instrument._add_tip(tip_length)

    # probe_center is the point at the center of the switch pcb
    center = Point(*robot.config.probe_center)

    hot_spots = _calculate_hotspots(robot, tip_length, SWITCH_CLEARANCE,
                                    X_SWITCH_OFFSET_MM, Y_SWITCH_OFFSET_MM,
                                    Z_SWITCH_OFFSET_MM, Z_DECK_CLEARANCE,
                                    Z_PROBE_CLEARANCE, Z_PROBE_START_CLEARANCE)

    # The saved axis positions from limit switch response
    axis_pos = []

    safe_height = _calculate_safeheight(robot, Z_CROSSOVER_CLEARANCE)

    log.info("Moving to safe z: {}".format(safe_height))
    robot.poses = instrument._move(robot.poses, z=safe_height)

    for axis, x, y, z, distance in hot_spots:
        if axis == 'z':

            x = x + center.x
            y = y + center.y
            z = z + center.z
        else:
            x = x + center.x
            y = y + center.y

        log.info("Moving to {}".format((x, y, z)))
        robot.poses = instrument._move(robot.poses, x=x, y=y)
        robot.poses = instrument._move(robot.poses, z=z)

        axis_index = 'xyz'.index(axis)
        robot.poses = instrument._probe(robot.poses, axis, distance)

        # Tip position is stored in accumulator and averaged for each axis
        # to be used for more accurate positioning for the next axis
        value = absolute(robot.poses, instrument)[axis_index]
        axis_pos.append(value)

        # after probing two points along the same axis
        # average them out, update center and clear accumulator
        # except Z, we're only probing that once
        if axis == 'z':
            center = center._replace(**{axis: axis_pos[0]})
            axis_pos.clear()
        elif len(axis_pos) == 2:
            center = center._replace(
                **{axis: (axis_pos[0] + axis_pos[1]) / 2.0})

            axis_pos.clear()

        log.debug("Current axis positions for {}: {}".format(axis, axis_pos))

        # Bounce back to release end stop
        bounce = value + (BOUNCE_DISTANCE_MM * (-distance / abs(distance)))

        robot.poses = instrument._move(robot.poses, **{axis: bounce})
        robot.poses = instrument._move(robot.poses, z=safe_height)

        log.debug("Updated center point tip probe {}".format(center))

    instrument._remove_tip(tip_length)

    return center
Esempio n. 30
0
 def position(instrument):
     return pose_tracker.absolute(robot.poses, instrument._instrument)