夹爪添加

This commit is contained in:
zhangshixiang
2025-12-23 14:54:36 +08:00
parent 44fc80c70f
commit 3ad20c85a5
3 changed files with 41149 additions and 35 deletions

View File

@@ -6,6 +6,7 @@ import os
import socket import socket
import time import time
from typing import Any, List, Dict, Optional, Tuple, TypedDict, Union, Sequence, Iterator, Literal from typing import Any, List, Dict, Optional, Tuple, TypedDict, Union, Sequence, Iterator, Literal
from pylabrobot.liquid_handling.standard import GripDirection
from pylabrobot.liquid_handling import ( from pylabrobot.liquid_handling import (
LiquidHandlerBackend, LiquidHandlerBackend,
@@ -27,7 +28,7 @@ from pylabrobot.liquid_handling.standard import (
ResourceMove, ResourceMove,
ResourceDrop, ResourceDrop,
) )
from pylabrobot.resources import Tip, Deck, Plate, Well, TipRack, Resource, Container, Coordinate, TipSpot, Trash from pylabrobot.resources import ResourceHolder, ResourceStack, Tip, Deck, Plate, Well, TipRack, Resource, Container, Coordinate, TipSpot, Trash
from unilabos.devices.liquid_handling.liquid_handler_abstract import LiquidHandlerAbstract, SimpleReturn from unilabos.devices.liquid_handling.liquid_handler_abstract import LiquidHandlerAbstract, SimpleReturn
from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode
@@ -153,11 +154,12 @@ class PRCXI9300Handler(LiquidHandlerAbstract):
tablets_info = [] tablets_info = []
count = 0 count = 0
for child in deck.children: for child in deck.children:
if "Material" in child._unilabos_state: if child.children:
count += 1 if "Material" in child.children[0]._unilabos_state:
tablets_info.append( number = int(child.name.replace("T", ""))
WorkTablets(Number=count, Code=f"T{count}", Material=child._unilabos_state["Material"]) tablets_info.append(
) WorkTablets(Number=number, Code=f"T{number}", Material=child.children[0]._unilabos_state["Material"])
)
if is_9320: if is_9320:
print("当前设备是9320") print("当前设备是9320")
# 始终初始化 step_mode 属性 # 始终初始化 step_mode 属性
@@ -433,12 +435,46 @@ class PRCXI9300Handler(LiquidHandlerAbstract):
async def move_to(self, well: Well, dis_to_top: float = 0, channel: int = 0): async def move_to(self, well: Well, dis_to_top: float = 0, channel: int = 0):
return await super().move_to(well, dis_to_top, channel) return await super().move_to(well, dis_to_top, channel)
async def shaker_action(self, time: int, frequency: float): async def shaker_action(self, time: int, module_no: int, amplitude: int, is_wait: bool):
return await self._unilabos_backend.shaker_action(time, frequency) return await self._unilabos_backend.shaker_action(time, module_no, amplitude, is_wait)
async def heater_action(self, temperature: float, time: int): async def heater_action(self, temperature: float, time: int):
return await self._unilabos_backend.heater_action(temperature, time) return await self._unilabos_backend.heater_action(temperature, time)
async def move_plate(
self,
plate: Plate,
to: Union[ResourceStack, ResourceHolder, Resource, Coordinate],
intermediate_locations: Optional[List[Coordinate]] = None,
pickup_offset: Coordinate = Coordinate.zero(),
destination_offset: Coordinate = Coordinate.zero(),
drop_direction: GripDirection = GripDirection.FRONT,
pickup_direction: GripDirection = GripDirection.FRONT,
pickup_distance_from_top: float = 13.2 - 3.33,
**backend_kwargs,
):
if self._simulator:
return await self._simulate_handler.move_plate(
plate,
to,
intermediate_locations,
pickup_offset,
destination_offset,
drop_direction,
pickup_direction,
pickup_distance_from_top,
**backend_kwargs,
)
return await super().move_plate(
plate,
to,
intermediate_locations,
pickup_offset,
destination_offset,
drop_direction,
pickup_direction,
pickup_distance_from_top,
**backend_kwargs,
)
class PRCXI9300Backend(LiquidHandlerBackend): class PRCXI9300Backend(LiquidHandlerBackend):
"""PRCXI 9300 的后端实现,继承自 LiquidHandlerBackend。 """PRCXI 9300 的后端实现,继承自 LiquidHandlerBackend。
@@ -482,9 +518,42 @@ class PRCXI9300Backend(LiquidHandlerBackend):
self.debug = debug self.debug = debug
self.axis = "Left" self.axis = "Left"
async def shaker_action(self, time: int, frequency: float): async def shaker_action(self, time: int, module_no: int, amplitude: int, is_wait: bool):
print(f"\n\nShaker action: time={time}, frequency={frequency}\n\n") step = self.api_client.shaker_action(
# return await self.api_client.shaker_action(time, frequency) time=time,
module_no=module_no,
amplitude=amplitude,
is_wait=is_wait,
)
self.steps_todo_list.append(step)
return step
async def pick_up_resource(self, pickup: ResourcePickup):
resource=pickup.resource
offset=pickup.offset
pickup_distance_from_top=pickup.pickup_distance_from_top
direction=pickup.direction
plate_number = int(resource.parent.name.replace("T", ""))
is_whole_plate = True
balance_height = 0
step = self.api_client.clamp_jaw_pick_up(plate_number, is_whole_plate, balance_height)
self.steps_todo_list.append(step)
return step
async def drop_resource(self, drop: ResourceDrop):
resource=drop.resource
plate_number = int(resource.name.replace("T", ""))
is_whole_plate = True
balance_height = drop.pickup_distance_from_top
step = self.api_client.clamp_jaw_drop(plate_number, is_whole_plate, balance_height)
self.steps_todo_list.append(step)
return step
async def heater_action(self, temperature: float, time: int): async def heater_action(self, temperature: float, time: int):
print(f"\n\nHeater action: temperature={temperature}, time={time}\n\n") print(f"\n\nHeater action: temperature={temperature}, time={time}\n\n")
@@ -585,8 +654,8 @@ class PRCXI9300Backend(LiquidHandlerBackend):
plate_indexes = [] plate_indexes = []
for op in ops: for op in ops:
plate = op.resource.parent plate = op.resource.parent
deck = plate.parent deck = plate.parent.parent
plate_index = deck.children.index(plate) plate_index = deck.children.index(plate.parent)
# print(f"Plate index: {plate_index}, Plate name: {plate.name}") # print(f"Plate index: {plate_index}, Plate name: {plate.name}")
# print(f"Number of children in deck: {len(deck.children)}") # print(f"Number of children in deck: {len(deck.children)}")
@@ -639,7 +708,7 @@ class PRCXI9300Backend(LiquidHandlerBackend):
# 检查trash # # 检查trash #
if ops[0].resource.name == "trash": if ops[0].resource.name == "trash":
PlateNo = ops[0].resource.parent.children.index(ops[0].resource) + 1 PlateNo = ops[0].resource.parent.parent.children.index(ops[0].resource.parent) + 1
step = self.api_client.UnLoad( step = self.api_client.UnLoad(
axis=axis, axis=axis,
@@ -660,8 +729,8 @@ class PRCXI9300Backend(LiquidHandlerBackend):
plate_indexes = [] plate_indexes = []
for op in ops: for op in ops:
plate = op.resource.parent plate = op.resource.parent
deck = plate.parent deck = plate.parent.parent
plate_index = deck.children.index(plate) plate_index = deck.children.index(plate.parent)
plate_indexes.append(plate_index) plate_indexes.append(plate_index)
if len(set(plate_indexes)) != 1: if len(set(plate_indexes)) != 1:
raise ValueError( raise ValueError(
@@ -712,9 +781,9 @@ class PRCXI9300Backend(LiquidHandlerBackend):
plate_indexes = [] plate_indexes = []
for op in targets: for op in targets:
deck = op.parent.parent deck = op.parent.parent.parent
plate = op.parent plate = op.parent
plate_index = deck.children.index(plate) plate_index = deck.children.index(plate.parent)
plate_indexes.append(plate_index) plate_indexes.append(plate_index)
if len(set(plate_indexes)) != 1: if len(set(plate_indexes)) != 1:
@@ -765,8 +834,8 @@ class PRCXI9300Backend(LiquidHandlerBackend):
plate_indexes = [] plate_indexes = []
for op in ops: for op in ops:
plate = op.resource.parent plate = op.resource.parent
deck = plate.parent deck = plate.parent.parent
plate_index = deck.children.index(plate) plate_index = deck.children.index(plate.parent)
plate_indexes.append(plate_index) plate_indexes.append(plate_index)
if len(set(plate_indexes)) != 1: if len(set(plate_indexes)) != 1:
@@ -822,8 +891,8 @@ class PRCXI9300Backend(LiquidHandlerBackend):
plate_indexes = [] plate_indexes = []
for op in ops: for op in ops:
plate = op.resource.parent plate = op.resource.parent
deck = plate.parent deck = plate.parent.parent
plate_index = deck.children.index(plate) plate_index = deck.children.index(plate.parent)
plate_indexes.append(plate_index) plate_indexes.append(plate_index)
if len(set(plate_indexes)) != 1: if len(set(plate_indexes)) != 1:
@@ -877,14 +946,8 @@ class PRCXI9300Backend(LiquidHandlerBackend):
async def dispense96(self, dispense: Union[MultiHeadDispensePlate, MultiHeadDispenseContainer]): async def dispense96(self, dispense: Union[MultiHeadDispensePlate, MultiHeadDispenseContainer]):
raise NotImplementedError("The Opentrons backend does not support the 96 head.") raise NotImplementedError("The Opentrons backend does not support the 96 head.")
async def pick_up_resource(self, pickup: ResourcePickup):
raise NotImplementedError("The Opentrons backend does not support the robotic arm.")
async def move_picked_up_resource(self, move: ResourceMove): async def move_picked_up_resource(self, move: ResourceMove):
raise NotImplementedError("The Opentrons backend does not support the robotic arm.") pass
async def drop_resource(self, drop: ResourceDrop):
raise NotImplementedError("The Opentrons backend does not support the robotic arm.")
def can_pick_up_tip(self, channel_idx: int, tip: Tip) -> bool: def can_pick_up_tip(self, channel_idx: int, tip: Tip) -> bool:
return True # PRCXI9300Backend does not have tip compatibility issues return True # PRCXI9300Backend does not have tip compatibility issues
@@ -957,6 +1020,8 @@ class PRCXI9300Api:
start = False start = False
while not success: while not success:
status = self.step_state_list() status = self.step_state_list()
if len(status) == 1:
start = True
if status is None: if status is None:
break break
if len(status) == 0: if len(status) == 0:
@@ -1236,6 +1301,50 @@ class PRCXI9300Api:
"LiquidDispensingMethod": liquid_method, "LiquidDispensingMethod": liquid_method,
} }
def clamp_jaw_pick_up(self,
plate_no: int,
is_whole_plate: bool,
balance_height: int,
) -> Dict[str, Any]:
return {
"StepAxis": "ClampingJaw",
"Function": "DefectiveLift",
"PlateNo": plate_no,
"IsWholePlate": is_whole_plate,
"HoleRow": 1,
"HoleCol": 1,
"BalanceHeight": balance_height,
"PlateOrHoleNum": f"T{plate_no}"
}
def clamp_jaw_drop(
self,
plate_no: int,
is_whole_plate: bool,
balance_height: int,
) -> Dict[str, Any]:
return {
"StepAxis": "ClampingJaw",
"Function": "PutDown",
"PlateNo": plate_no,
"IsWholePlate": is_whole_plate,
"HoleRow": 1,
"HoleCol": 1,
"BalanceHeight": balance_height,
"PlateOrHoleNum": f"T{plate_no}"
}
def shaker_action(self, time: int, module_no: int, amplitude: int, is_wait: bool):
return {
"StepAxis": "Left",
"Function": "Shaking",
"AssistFun1": time,
"AssistFun2": module_no,
"AssistFun3": amplitude,
"AssistFun4": is_wait,
}
class DefaultLayout: class DefaultLayout:

View File

@@ -7730,7 +7730,9 @@ liquid_handler.prcxi:
feedback: {} feedback: {}
goal: {} goal: {}
goal_default: goal_default:
frequency: null amplitude: null
is_wait: null
module_no: null
time: null time: null
handles: {} handles: {}
placeholder_keys: {} placeholder_keys: {}
@@ -7741,13 +7743,19 @@ liquid_handler.prcxi:
feedback: {} feedback: {}
goal: goal:
properties: properties:
frequency: amplitude:
type: number type: integer
is_wait:
type: boolean
module_no:
type: integer
time: time:
type: integer type: integer
required: required:
- time - time
- frequency - module_no
- amplitude
- is_wait
type: object type: object
result: {} result: {}
required: required:
@@ -8414,6 +8422,341 @@ liquid_handler.prcxi:
title: LiquidHandlerMix title: LiquidHandlerMix
type: object type: object
type: LiquidHandlerMix type: LiquidHandlerMix
move_plate:
feedback: {}
goal:
destination_offset: destination_offset
drop_direction: drop_direction
get_direction: get_direction
intermediate_locations: intermediate_locations
pickup_direction: pickup_direction
pickup_offset: pickup_offset
plate: plate
put_direction: put_direction
resource_offset: resource_offset
to: to
goal_default:
destination_offset:
x: 0.0
y: 0.0
z: 0.0
drop_direction: ''
get_direction: ''
intermediate_locations:
- x: 0.0
y: 0.0
z: 0.0
pickup_direction: ''
pickup_distance_from_top: 0.0
pickup_offset:
x: 0.0
y: 0.0
z: 0.0
plate:
category: ''
children: []
config: ''
data: ''
id: ''
name: ''
parent: ''
pose:
orientation:
w: 1.0
x: 0.0
y: 0.0
z: 0.0
position:
x: 0.0
y: 0.0
z: 0.0
sample_id: ''
type: ''
put_direction: ''
resource_offset:
x: 0.0
y: 0.0
z: 0.0
to:
category: ''
children: []
config: ''
data: ''
id: ''
name: ''
parent: ''
pose:
orientation:
w: 1.0
x: 0.0
y: 0.0
z: 0.0
position:
x: 0.0
y: 0.0
z: 0.0
sample_id: ''
type: ''
handles: {}
placeholder_keys:
plate: unilabos_resources
to: unilabos_resources
result:
name: name
schema:
description: ''
properties:
feedback:
properties: {}
required: []
title: LiquidHandlerMovePlate_Feedback
type: object
goal:
properties:
destination_offset:
properties:
x:
type: number
y:
type: number
z:
type: number
required:
- x
- y
- z
title: destination_offset
type: object
drop_direction:
type: string
get_direction:
type: string
intermediate_locations:
items:
properties:
x:
type: number
y:
type: number
z:
type: number
required:
- x
- y
- z
title: intermediate_locations
type: object
type: array
pickup_direction:
type: string
pickup_distance_from_top:
type: number
pickup_offset:
properties:
x:
type: number
y:
type: number
z:
type: number
required:
- x
- y
- z
title: pickup_offset
type: object
plate:
properties:
category:
type: string
children:
items:
type: string
type: array
config:
type: string
data:
type: string
id:
type: string
name:
type: string
parent:
type: string
pose:
properties:
orientation:
properties:
w:
type: number
x:
type: number
y:
type: number
z:
type: number
required:
- x
- y
- z
- w
title: orientation
type: object
position:
properties:
x:
type: number
y:
type: number
z:
type: number
required:
- x
- y
- z
title: position
type: object
required:
- position
- orientation
title: pose
type: object
sample_id:
type: string
type:
type: string
required:
- id
- name
- sample_id
- children
- parent
- type
- category
- pose
- config
- data
title: plate
type: object
put_direction:
type: string
resource_offset:
properties:
x:
type: number
y:
type: number
z:
type: number
required:
- x
- y
- z
title: resource_offset
type: object
to:
properties:
category:
type: string
children:
items:
type: string
type: array
config:
type: string
data:
type: string
id:
type: string
name:
type: string
parent:
type: string
pose:
properties:
orientation:
properties:
w:
type: number
x:
type: number
y:
type: number
z:
type: number
required:
- x
- y
- z
- w
title: orientation
type: object
position:
properties:
x:
type: number
y:
type: number
z:
type: number
required:
- x
- y
- z
title: position
type: object
required:
- position
- orientation
title: pose
type: object
sample_id:
type: string
type:
type: string
required:
- id
- name
- sample_id
- children
- parent
- type
- category
- pose
- config
- data
title: to
type: object
required:
- plate
- to
- intermediate_locations
- resource_offset
- pickup_offset
- destination_offset
- pickup_direction
- drop_direction
- get_direction
- put_direction
- pickup_distance_from_top
title: LiquidHandlerMovePlate_Goal
type: object
result:
properties:
return_info:
type: string
success:
type: boolean
required:
- return_info
- success
title: LiquidHandlerMovePlate_Result
type: object
required:
- goal
title: LiquidHandlerMovePlate
type: object
type: LiquidHandlerMovePlate
pick_up_tips: pick_up_tips:
feedback: {} feedback: {}
goal: goal:

File diff suppressed because it is too large Load Diff