|
11 | 11 | import re
|
12 | 12 | from typing import Callable, Dict, List, Literal, Optional, Sequence, Type, TypeVar, Union, cast
|
13 | 13 |
|
| 14 | +from pylabrobot import audio |
14 | 15 | from pylabrobot.liquid_handling.backends.hamilton.base import HamiltonLiquidHandler
|
15 | 16 | from pylabrobot.liquid_handling.errors import ChannelizedError
|
16 | 17 | from pylabrobot.liquid_handling.liquid_classes.hamilton import (
|
|
39 | 40 | from pylabrobot.resources.hamilton.hamilton_decks import STAR_SIZE_X, STARLET_SIZE_X
|
40 | 41 | from pylabrobot.resources.liquid import Liquid
|
41 | 42 | from pylabrobot.resources.ml_star import HamiltonTip, TipDropMethod, TipPickupMethod, TipSize
|
42 |
| -from pylabrobot import audio |
| 43 | +from pylabrobot.resources.utils import get_child_location |
| 44 | +from pylabrobot.utils.linalg import matrix_vector_multiply_3x3 |
43 | 45 |
|
44 | 46 | T = TypeVar("T")
|
45 | 47 |
|
@@ -1583,7 +1585,7 @@ async def aspirate(
|
1583 | 1585 | for wb, op in zip(well_bottoms, ops)]
|
1584 | 1586 | if lld_search_height is None:
|
1585 | 1587 | lld_search_height = [
|
1586 |
| - (wb + op.resource.get_size_z() + (2.7 if isinstance(op.resource, Well) else 5)) # ? |
| 1588 | + (wb + op.resource.get_absolute_size_z() + (2.7 if isinstance(op.resource, Well) else 5)) # ? |
1587 | 1589 | for wb, op in zip(well_bottoms, ops)
|
1588 | 1590 | ]
|
1589 | 1591 | else:
|
@@ -1845,7 +1847,7 @@ async def dispense(
|
1845 | 1847 | [ls + (op.liquid_height or 0) for ls, op in zip(well_bottoms, ops)]
|
1846 | 1848 | if lld_search_height is None:
|
1847 | 1849 | lld_search_height = [
|
1848 |
| - (wb + op.resource.get_size_z() + (2.7 if isinstance(op.resource, Well) else 5)) #? |
| 1850 | + (wb + op.resource.get_absolute_size_z() + (2.7 if isinstance(op.resource, Well) else 5)) #? |
1849 | 1851 | for wb, op in zip(well_bottoms, ops)
|
1850 | 1852 | ]
|
1851 | 1853 | else:
|
@@ -2378,11 +2380,11 @@ async def iswap_pick_up_resource(
|
2378 | 2380 |
|
2379 | 2381 | # Get center of source plate. Also gripping height and plate width.
|
2380 | 2382 | center = resource.get_absolute_location(x="c", y="c", z="b") + offset
|
2381 |
| - grip_height = center.z + resource.get_size_z() - pickup_distance_from_top |
| 2383 | + grip_height = center.z + resource.get_absolute_size_z() - pickup_distance_from_top |
2382 | 2384 | if grip_direction in (GripDirection.FRONT, GripDirection.BACK):
|
2383 |
| - plate_width = resource.get_size_x() |
| 2385 | + plate_width = resource.get_absolute_size_x() |
2384 | 2386 | elif grip_direction in (GripDirection.RIGHT, GripDirection.LEFT):
|
2385 |
| - plate_width = resource.get_size_y() |
| 2387 | + plate_width = resource.get_absolute_size_y() |
2386 | 2388 | else:
|
2387 | 2389 | raise ValueError("Invalid grip direction")
|
2388 | 2390 |
|
@@ -2435,7 +2437,7 @@ async def iswap_move_picked_up_resource(
|
2435 | 2437 | x_direction=0,
|
2436 | 2438 | y_position=round(center.y * 10),
|
2437 | 2439 | y_direction=0,
|
2438 |
| - z_position=round((location.z + resource.get_size_z() / 2) * 10), |
| 2440 | + z_position=round((location.z + resource.get_absolute_size_z() / 2) * 10), |
2439 | 2441 | z_direction=0,
|
2440 | 2442 | grip_direction={
|
2441 | 2443 | GripDirection.FRONT: 1,
|
@@ -2476,15 +2478,24 @@ async def iswap_release_picked_up_resource(
|
2476 | 2478 |
|
2477 | 2479 | assert self.iswap_installed, "iswap must be installed"
|
2478 | 2480 |
|
2479 |
| - # Get center of source plate. Also gripping height and plate width. |
2480 |
| - center = location + resource.rotated(z=rotation).center() + offset |
2481 |
| - grip_height = center.z + resource.get_size_z() - pickup_distance_from_top |
| 2481 | + # Get center of source plate in absolute space. |
| 2482 | + # The computation of the center has to be rotated so that the offset is in absolute space. |
| 2483 | + center_in_absolute_space = Coordinate(*matrix_vector_multiply_3x3( |
| 2484 | + resource.rotated(z=rotation).get_absolute_rotation().get_rotation_matrix(), |
| 2485 | + resource.center().vector() |
| 2486 | + )) |
| 2487 | + # This is when the resource is rotated (around its origin), but we also need to translate |
| 2488 | + # so that the left front bottom corner of the plate is lfb in absolute space, not local. |
| 2489 | + center_in_absolute_space += get_child_location(resource.rotated(z=rotation)) |
| 2490 | + |
| 2491 | + center = location + center_in_absolute_space + offset |
| 2492 | + grip_height = center.z + resource.get_absolute_size_z() - pickup_distance_from_top |
2482 | 2493 | # grip_direction here is the put_direction. We use `rotation` to cancel it out and get the
|
2483 | 2494 | # original grip direction. Hack.
|
2484 | 2495 | if grip_direction in (GripDirection.FRONT, GripDirection.BACK):
|
2485 |
| - plate_width = resource.rotated(z=rotation).get_size_x() |
| 2496 | + plate_width = resource.rotated(z=rotation).get_absolute_size_x() |
2486 | 2497 | elif grip_direction in (GripDirection.RIGHT, GripDirection.LEFT):
|
2487 |
| - plate_width = resource.rotated(z=rotation).get_size_y() |
| 2498 | + plate_width = resource.rotated(z=rotation).get_absolute_size_y() |
2488 | 2499 | else:
|
2489 | 2500 | raise ValueError("Invalid grip direction")
|
2490 | 2501 |
|
@@ -2540,8 +2551,8 @@ async def core_pick_up_resource(
|
2540 | 2551 |
|
2541 | 2552 | # Get center of source plate. Also gripping height and plate width.
|
2542 | 2553 | center = resource.get_absolute_location(x="c", y="c", z="b") + offset
|
2543 |
| - grip_height = center.z + resource.get_size_z() - pickup_distance_from_top |
2544 |
| - grip_width = resource.get_size_y() #grip width is y size of resource |
| 2554 | + grip_height = center.z + resource.get_absolute_size_z() - pickup_distance_from_top |
| 2555 | + grip_width = resource.get_absolute_size_y() #grip width is y size of resource |
2545 | 2556 |
|
2546 | 2557 | if self.core_parked:
|
2547 | 2558 | await self.get_core(p1=channel_1, p2=channel_2)
|
@@ -2625,8 +2636,8 @@ async def core_release_picked_up_resource(
|
2625 | 2636 |
|
2626 | 2637 | # Get center of destination location. Also gripping height and plate width.
|
2627 | 2638 | center = location + resource.center() + offset
|
2628 |
| - grip_height = center.z + resource.get_size_z() - pickup_distance_from_top |
2629 |
| - grip_width = resource.get_size_y() |
| 2639 | + grip_height = center.z + resource.get_absolute_size_z() - pickup_distance_from_top |
| 2640 | + grip_width = resource.get_absolute_size_y() |
2630 | 2641 |
|
2631 | 2642 | await self.core_put_plate(
|
2632 | 2643 | x_position=round(center.x * 10),
|
@@ -2691,7 +2702,7 @@ async def move_resource(
|
2691 | 2702 |
|
2692 | 2703 | previous_location = move.resource.get_absolute_location() + move.resource_offset
|
2693 | 2704 | minimum_traverse_height = 284.0
|
2694 |
| - previous_location.z = minimum_traverse_height - move.resource.get_size_z() / 2 |
| 2705 | + previous_location.z = minimum_traverse_height - move.resource.get_absolute_size_z() / 2 |
2695 | 2706 |
|
2696 | 2707 | for location in move.intermediate_locations:
|
2697 | 2708 | if use_arm == "iswap":
|
@@ -2793,9 +2804,9 @@ async def core_check_resource_exists_at_location_center(
|
2793 | 2804 | """
|
2794 | 2805 |
|
2795 | 2806 | center = location + resource.centers()[0] + offset
|
2796 |
| - y_width_to_gripper_bump = resource.get_size_y() - gripper_y_margin*2 |
2797 |
| - assert 9 <= y_width_to_gripper_bump <= round(resource.get_size_y()), \ |
2798 |
| - f"width between channels must be between 9 and {resource.get_size_y()} mm" \ |
| 2807 | + y_width_to_gripper_bump = resource.get_absolute_size_y() - gripper_y_margin*2 |
| 2808 | + assert 9 <= y_width_to_gripper_bump <= round(resource.get_absolute_size_y()), \ |
| 2809 | + f"width between channels must be between 9 and {resource.get_absolute_size_y()} mm" \ |
2799 | 2810 | " (i.e. the minimal distance between channels and the max y size of the resource"
|
2800 | 2811 |
|
2801 | 2812 | # Check if CoRe gripper currently in use
|
@@ -4325,7 +4336,7 @@ async def get_core(self, p1: int, p2: int):
|
4325 | 4336 | # This appears to be deck.get_size_x() - 562.5, but let's keep an explicit check so that we
|
4326 | 4337 | # can catch unknown deck sizes. Can the grippers exist at another location? If so, define it as
|
4327 | 4338 | # a resource on the robot deck and use deck.get_resource().get_absolute_location().
|
4328 |
| - deck_size = self.deck.get_size_x() |
| 4339 | + deck_size = self.deck.get_absolute_size_x() |
4329 | 4340 | if deck_size == STARLET_SIZE_X:
|
4330 | 4341 | xs = 7975 # 1360-797.5 = 562.5
|
4331 | 4342 | elif deck_size == STAR_SIZE_X:
|
@@ -4354,7 +4365,7 @@ async def get_core(self, p1: int, p2: int):
|
4354 | 4365 | async def put_core(self):
|
4355 | 4366 | """ Put CoRe gripper tool at wasteblock mount. """
|
4356 | 4367 | assert self.deck is not None, "must have deck defined to access CoRe grippers"
|
4357 |
| - deck_size = self.deck.get_size_x() |
| 4368 | + deck_size = self.deck.get_absolute_size_x() |
4358 | 4369 | if deck_size == STARLET_SIZE_X:
|
4359 | 4370 | xs = 7975
|
4360 | 4371 | elif deck_size == STAR_SIZE_X:
|
@@ -5591,7 +5602,7 @@ async def unload_carrier(self, carrier: Carrier):
|
5591 | 5602 | """ Use autoload to unload carrier. """
|
5592 | 5603 | # Identify carrier end rail
|
5593 | 5604 | track_width = 22.5
|
5594 |
| - carrier_width = carrier.get_absolute_location().x - 100 + carrier.get_size_x() |
| 5605 | + carrier_width = carrier.get_absolute_location().x - 100 + carrier.get_absolute_size_x() |
5595 | 5606 | carrier_end_rail = int(carrier_width / track_width)
|
5596 | 5607 | assert 1 <= carrier_end_rail <= 54, "carrier loading rail must be between 1 and 54"
|
5597 | 5608 |
|
@@ -5655,7 +5666,7 @@ async def load_carrier(
|
5655 | 5666 | }
|
5656 | 5667 | # Identify carrier end rail
|
5657 | 5668 | track_width = 22.5
|
5658 |
| - carrier_width = carrier.get_absolute_location().x - 100 + carrier.get_size_x() |
| 5669 | + carrier_width = carrier.get_absolute_location().x - 100 + carrier.get_absolute_size_x() |
5659 | 5670 | carrier_end_rail = int(carrier_width / track_width)
|
5660 | 5671 | assert 1 <= carrier_end_rail <= 54, "carrier loading rail must be between 1 and 54"
|
5661 | 5672 |
|
|
0 commit comments