mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-17 13:01:12 +00:00
* Add LaiYu Liquid device integration and tests Introduce LaiYu Liquid device implementation, including backend, controllers, drivers, configuration, and resource files. Add hardware connection, tip pickup, and simplified test scripts, as well as experiment and registry configuration for LaiYu Liquid. Documentation and .gitignore for the device are also included. * feat(LaiYu_Liquid): 重构设备模块结构并添加硬件文档 refactor: 重新组织LaiYu_Liquid模块目录结构 docs: 添加SOPA移液器和步进电机控制指令文档 fix: 修正设备配置中的最大体积默认值 test: 新增工作台配置测试用例 chore: 删除过时的测试脚本和配置文件 * add * 重构: 将 LaiYu_Liquid.py 重命名为 laiyu_liquid_main.py 并更新所有导入引用 - 使用 git mv 将 LaiYu_Liquid.py 重命名为 laiyu_liquid_main.py - 更新所有相关文件中的导入引用 - 保持代码功能不变,仅改善命名一致性 - 测试确认所有导入正常工作 * 修复: 在 core/__init__.py 中添加 LaiYuLiquidBackend 导出 - 添加 LaiYuLiquidBackend 到导入列表 - 添加 LaiYuLiquidBackend 到 __all__ 导出列表 - 确保所有主要类都可以正确导入 * 修复大小写文件夹名字
36 KiB
36 KiB
LaiYu液体处理设备硬件连接配置指南
📋 文档概述
本指南提供LaiYu液体处理设备的完整硬件连接配置方案,包括快速入门、详细配置、连接验证和故障排除。适用于设备初次安装、配置变更和问题诊断。
🚀 快速入门指南
基本配置步骤
-
确认硬件连接
- 将RS485转USB设备连接到计算机
- 确保XYZ控制器和移液器通过RS485总线连接
- 检查设备供电状态
-
获取串口信息
# macOS/Linux ls /dev/cu.* | grep usbserial # 常见输出: /dev/cu.usbserial-3130 -
基本配置参数
# 推荐的默认配置 config = LaiYuLiquidConfig( port="/dev/cu.usbserial-3130", # 🔧 替换为实际串口号 address=4, # 移液器地址(固定) baudrate=115200, # 推荐波特率 timeout=5.0 # 通信超时 ) -
快速连接测试
device = LaiYuLiquid(config) success = await device.setup() print(f"连接状态: {'成功' if success else '失败'}")
🏗️ 硬件架构详解
系统组成
LaiYu液体处理设备采用RS485总线架构,包含以下核心组件:
| 组件 | 通信协议 | 设备地址 | 默认波特率 | 功能描述 |
|---|---|---|---|---|
| XYZ三轴控制器 | RS485 (Modbus) | X轴=1, Y轴=2, Z轴=3 | 115200 | 三维运动控制 |
| SOPA移液器 | RS485 | 4 (推荐) | 115200 | 液体吸取分配 |
| RS485转USB | USB/串口 | - | 115200 | 通信接口转换 |
地址分配策略
RS485总线地址分配:
├── 地址 1: X轴步进电机 (自动分配)
├── 地址 2: Y轴步进电机 (自动分配)
├── 地址 3: Z轴步进电机 (自动分配)
├── 地址 4: SOPA移液器 (推荐配置)
└── 禁用地址: 47('/'), 69('E'), 91('[')
通信参数规范
| 参数 | XYZ控制器 | SOPA移液器 | 说明 |
|---|---|---|---|
| 数据位 | 8 | 8 | 固定值 |
| 停止位 | 1 | 1 | 固定值 |
| 校验位 | 无 | 无 | 固定值 |
| 流控制 | 无 | 无 | 固定值 |
⚙️ 配置参数详解
1. 核心配置类
LaiYuLiquidConfig 参数说明
@dataclass
class LaiYuLiquidConfig:
# === 通信参数 ===
port: str = "/dev/cu.usbserial-3130" # 串口设备路径
address: int = 4 # 移液器地址(推荐值)
baudrate: int = 115200 # 通信波特率(推荐值)
timeout: float = 5.0 # 通信超时时间(秒)
# === 工作台物理尺寸 ===
deck_width: float = 340.0 # 工作台宽度 (mm)
deck_height: float = 250.0 # 工作台高度 (mm)
deck_depth: float = 160.0 # 工作台深度 (mm)
# === 运动控制参数 ===
max_speed: float = 100.0 # 最大移动速度 (mm/s)
acceleration: float = 50.0 # 加速度 (mm/s²)
safe_height: float = 50.0 # 安全移动高度 (mm)
# === 移液参数 ===
max_volume: float = 1000.0 # 最大移液体积 (μL)
min_volume: float = 0.1 # 最小移液体积 (μL)
liquid_detection: bool = True # 启用液面检测
# === 枪头操作参数 ===
tip_pickup_speed: int = 30 # 取枪头速度 (rpm)
tip_pickup_acceleration: int = 500 # 取枪头加速度 (rpm/s)
tip_pickup_depth: float = 10.0 # 枪头插入深度 (mm)
tip_drop_height: float = 10.0 # 丢弃枪头高度 (mm)
2. 配置文件位置
A. 代码配置(推荐)
# 在Python代码中直接配置
from unilabos.devices.laiyu_liquid import LaiYuLiquidConfig
config = LaiYuLiquidConfig(
port="/dev/cu.usbserial-3130", # 🔧 修改为实际串口
address=4, # 🔧 移液器地址
baudrate=115200, # 🔧 通信波特率
timeout=5.0 # 🔧 超时时间
)
B. JSON配置文件
{
"laiyu_liquid_config": {
"port": "/dev/cu.usbserial-3130",
"address": 4,
"baudrate": 115200,
"timeout": 5.0,
"deck_width": 340.0,
"deck_height": 250.0,
"deck_depth": 160.0,
"max_speed": 100.0,
"acceleration": 50.0,
"safe_height": 50.0
}
}
C. 实验协议配置
// test/experiments/laiyu_liquid.json
{
"device_config": {
"type": "laiyu_liquid",
"config": {
"port": "/dev/cu.usbserial-3130",
"address": 4,
"baudrate": 115200
}
}
}
2. 串口设备识别
自动识别方法(推荐)
import serial.tools.list_ports
def find_laiyu_device():
"""自动查找LaiYu设备串口"""
ports = serial.tools.list_ports.comports()
for port in ports:
# 根据设备描述或VID/PID识别
if 'usbserial' in port.device.lower():
print(f"找到可能的设备: {port.device}")
print(f"描述: {port.description}")
print(f"硬件ID: {port.hwid}")
return port.device
return None
# 使用示例
device_port = find_laiyu_device()
if device_port:
print(f"检测到设备端口: {device_port}")
else:
print("未检测到设备")
手动识别方法
| 操作系统 | 命令 | 设备路径格式 |
|---|---|---|
| macOS | ls /dev/cu.* |
/dev/cu.usbserial-XXXX |
| Linux | ls /dev/ttyUSB* |
/dev/ttyUSB0 |
| Windows | 设备管理器 | COM3, COM4 等 |
macOS 详细识别
# 1. 列出所有USB串口设备
ls /dev/cu.usbserial-*
# 2. 查看USB设备详细信息
system_profiler SPUSBDataType | grep -A 10 "Serial"
# 3. 实时监控设备插拔
ls /dev/cu.* && echo "--- 请插入设备 ---" && sleep 3 && ls /dev/cu.*
Linux 详细识别
# 1. 列出串口设备
ls /dev/ttyUSB* /dev/ttyACM*
# 2. 查看设备信息
dmesg | grep -i "usb.*serial"
lsusb | grep -i "serial\|converter"
# 3. 查看设备属性
udevadm info --name=/dev/ttyUSB0 --attribute-walk
Windows 详细识别
# PowerShell命令
Get-WmiObject -Class Win32_SerialPort | Select-Object Name, DeviceID, Description
# 或在设备管理器中查看"端口(COM和LPT)"
3. 控制器特定配置
XYZ步进电机控制器
- 地址范围: 1-3 (X轴=1, Y轴=2, Z轴=3)
- 通信协议: Modbus RTU
- 波特率: 9600 或 115200
- 数据位: 8
- 停止位: 1
- 校验位: None
XYZ控制器配置 (controllers/xyz_controller.py)
XYZ控制器负责三轴运动控制,提供精确的位置控制和运动规划功能。
主要功能:
- 三轴独立控制(X、Y、Z轴)
- 位置精度控制
- 运动速度调节
- 安全限位检测
配置参数:
xyz_config = {
"port": "/dev/ttyUSB0", # 串口设备
"baudrate": 115200, # 波特率
"timeout": 1.0, # 通信超时
"max_speed": { # 最大速度限制
"x": 1000, # X轴最大速度
"y": 1000, # Y轴最大速度
"z": 500 # Z轴最大速度
},
"acceleration": 500, # 加速度
"home_position": [0, 0, 0] # 原点位置
}
def __init__(self, port: str, baudrate: int = 115200,
machine_config: Optional[MachineConfig] = None,
config_file: str = "machine_config.json",
auto_connect: bool = True):
"""
Args:
port: 串口端口 (如: "/dev/cu.usbserial-3130")
baudrate: 波特率 (默认: 115200)
machine_config: 机械配置参数
config_file: 配置文件路径
auto_connect: 是否自动连接
"""
SOPA移液器
- 地址: 通常为 4 或更高
- 通信协议: 自定义协议
- 波特率: 115200 (推荐)
- 响应时间: < 100ms
移液器控制器配置 (controllers/pipette_controller.py)
移液器控制器负责精确的液体吸取和分配操作,支持多种移液模式和参数配置。
主要功能:
- 精确体积控制
- 液面检测
- 枪头管理
- 速度调节
配置参数:
@dataclass
class SOPAConfig:
# 通信参数
port: str = "/dev/ttyUSB0" # 🔧 修改串口号
baudrate: int = 115200 # 🔧 修改波特率
address: int = 1 # 🔧 修改设备地址 (1-254)
timeout: float = 5.0 # 🔧 修改超时时间
comm_type: CommunicationType = CommunicationType.TERMINAL_DEBUG
🔍 连接验证与测试
1. 编程方式验证连接
创建测试脚本
#!/usr/bin/env python3
"""
LaiYu液体处理设备连接测试脚本
"""
import sys
import os
sys.path.append('/Users/dp/Documents/DPT/HuaiRou/Uni-Lab-OS')
from unilabos.devices.laiyu_liquid.core.LaiYu_Liquid import (
LaiYuLiquid, LaiYuLiquidConfig
)
def test_connection():
"""测试设备连接"""
# 🔧 修改这里的配置参数
config = LaiYuLiquidConfig(
port="/dev/cu.usbserial-3130", # 修改为你的串口号
address=1, # 修改为你的设备地址
baudrate=9600, # 修改为你的波特率
timeout=5.0
)
print("🔌 正在测试LaiYu液体处理设备连接...")
print(f"串口: {config.port}")
print(f"波特率: {config.baudrate}")
print(f"设备地址: {config.address}")
print("-" * 50)
try:
# 创建设备实例
device = LaiYuLiquid(config)
# 尝试连接和初始化
print("📡 正在连接设备...")
success = await device.setup()
if success:
print("✅ 设备连接成功!")
print(f"连接状态: {device.is_connected}")
print(f"初始化状态: {device.is_initialized}")
print(f"当前位置: {device.current_position}")
# 获取设备状态
status = device.get_status()
print("\n📊 设备状态:")
for key, value in status.items():
print(f" {key}: {value}")
else:
print("❌ 设备连接失败!")
print("请检查:")
print(" 1. 串口号是否正确")
print(" 2. 设备是否已连接并通电")
print(" 3. 波特率和设备地址是否匹配")
print(" 4. 串口是否被其他程序占用")
except Exception as e:
print(f"❌ 连接测试出错: {e}")
print("\n🔧 故障排除建议:")
print(" 1. 检查串口设备是否存在:")
print(" macOS: ls /dev/cu.*")
print(" Linux: ls /dev/ttyUSB* /dev/ttyACM*")
print(" 2. 检查设备权限:")
print(" sudo chmod 666 /dev/cu.usbserial-*")
print(" 3. 检查设备是否被占用:")
print(" lsof | grep /dev/cu.usbserial")
finally:
# 清理连接
if 'device' in locals():
await device.stop()
if __name__ == "__main__":
import asyncio
asyncio.run(test_connection())
2. 命令行验证工具
串口通信测试
# 安装串口调试工具
pip install pyserial
# 使用Python测试串口
python -c "
import serial
try:
ser = serial.Serial('/dev/cu.usbserial-3130', 9600, timeout=1)
print('串口连接成功:', ser.is_open)
ser.close()
except Exception as e:
print('串口连接失败:', e)
"
设备权限检查
# macOS/Linux 检查串口权限
ls -la /dev/cu.usbserial-*
# 如果权限不足,修改权限
sudo chmod 666 /dev/cu.usbserial-*
# 检查串口是否被占用
lsof | grep /dev/cu.usbserial
3. 连接状态指示器
设备提供多种方式检查连接状态:
A. 属性检查
device = LaiYuLiquid(config)
# 检查连接状态
print(f"设备已连接: {device.is_connected}")
print(f"设备已初始化: {device.is_initialized}")
print(f"枪头已安装: {device.tip_attached}")
print(f"当前位置: {device.current_position}")
print(f"当前体积: {device.current_volume}")
B. 状态字典
status = device.get_status()
print("完整设备状态:", status)
# 输出示例:
# {
# 'connected': True,
# 'initialized': True,
# 'position': (0.0, 0.0, 50.0),
# 'tip_attached': False,
# 'current_volume': 0.0,
# 'last_error': None
# }
🛠️ 故障排除指南
1. 连接问题诊断
🔍 问题诊断流程
def diagnose_connection_issues():
"""连接问题诊断工具"""
import serial.tools.list_ports
import serial
print("🔍 开始连接问题诊断...")
# 1. 检查串口设备
ports = list(serial.tools.list_ports.comports())
if not ports:
print("❌ 未检测到任何串口设备")
print("💡 解决方案:")
print(" - 检查USB连接线")
print(" - 确认设备电源")
print(" - 安装设备驱动")
return
print(f"✅ 检测到 {len(ports)} 个串口设备")
for port in ports:
print(f" 📍 {port.device}: {port.description}")
# 2. 测试串口访问权限
for port in ports:
try:
with serial.Serial(port.device, 9600, timeout=1):
print(f"✅ {port.device}: 访问权限正常")
except PermissionError:
print(f"❌ {port.device}: 权限不足")
print("💡 解决方案: sudo chmod 666 " + port.device)
except Exception as e:
print(f"⚠️ {port.device}: {e}")
# 运行诊断
diagnose_connection_issues()
🚫 常见连接错误
| 错误类型 | 症状 | 解决方案 |
|---|---|---|
| 设备未找到 | FileNotFoundError: No such file or directory |
1. 检查USB连接 2. 确认设备驱动 3. 重新插拔设备 |
| 权限不足 | PermissionError: Permission denied |
1. sudo chmod 666 /dev/ttyUSB02. 添加用户到dialout组 3. 使用sudo运行 |
| 设备占用 | SerialException: Device or resource busy |
1. 关闭其他程序 2. lsof /dev/ttyUSB0查找占用3. 重启系统 |
| 驱动问题 | 设备管理器显示未知设备 | 1. 安装CH340/CP210x驱动 2. 更新系统驱动 3. 使用原装USB线 |
2. 通信问题解决
📡 通信参数调试
def test_communication_parameters():
"""测试不同通信参数"""
import serial
port = "/dev/cu.usbserial-3130" # 修改为实际端口
baudrates = [9600, 19200, 38400, 57600, 115200]
for baudrate in baudrates:
print(f"🔄 测试波特率: {baudrate}")
try:
with serial.Serial(port, baudrate, timeout=2) as ser:
# 发送测试命令
test_cmd = b'\x01\x03\x00\x00\x00\x01\x84\x0A'
ser.write(test_cmd)
response = ser.read(100)
if response:
print(f" ✅ 成功: 收到 {len(response)} 字节")
print(f" 📦 数据: {response.hex()}")
return baudrate
else:
print(f" ❌ 无响应")
except Exception as e:
print(f" ❌ 错误: {e}")
return None
⚡ 通信故障排除
| 问题类型 | 症状 | 诊断方法 | 解决方案 |
|---|---|---|---|
| 通信超时 | TimeoutError |
检查波特率和设备地址 | 1. 调整超时时间 2. 验证波特率 3. 检查设备地址 |
| 数据校验错误 | CRCError |
检查数据完整性 | 1. 更换USB线 2. 降低波特率 3. 检查电磁干扰 |
| 协议错误 | 响应格式异常 | 验证命令格式 | 1. 检查协议版本 2. 确认设备类型 3. 更新固件 |
| 间歇性故障 | 时好时坏 | 监控连接稳定性 | 1. 检查连接线 2. 稳定电源 3. 减少干扰源 |
3. 设备功能问题
🎯 设备状态检查
def check_device_health():
"""设备健康状态检查"""
from unilabos.devices.laiyu_liquid import LaiYuLiquidConfig, LaiYuLiquidBackend
config = LaiYuLiquidConfig(
port="/dev/cu.usbserial-3130",
address=4,
baudrate=115200,
timeout=5.0
)
try:
backend = LaiYuLiquidBackend(config)
backend.connect()
# 检查项目
checks = {
"设备连接": lambda: backend.is_connected(),
"XYZ轴状态": lambda: backend.xyz_controller.get_all_positions(),
"移液器状态": lambda: backend.pipette_controller.get_status(),
"设备温度": lambda: backend.get_temperature(),
"错误状态": lambda: backend.get_error_status(),
}
print("🏥 设备健康检查报告")
print("=" * 40)
for check_name, check_func in checks.items():
try:
result = check_func()
print(f"✅ {check_name}: 正常")
if result:
print(f" 📊 数据: {result}")
except Exception as e:
print(f"❌ {check_name}: 异常 - {e}")
backend.disconnect()
except Exception as e:
print(f"❌ 无法连接设备: {e}")
4. 高级故障排除
🔧 日志分析工具
import logging
def setup_debug_logging():
"""设置调试日志"""
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('laiyu_debug.log'),
logging.StreamHandler()
]
)
# 启用串口通信日志
serial_logger = logging.getLogger('serial')
serial_logger.setLevel(logging.DEBUG)
print("🔍 调试日志已启用,日志文件: laiyu_debug.log")
📊 性能监控
def monitor_performance():
"""性能监控工具"""
import time
import psutil
print("📊 开始性能监控...")
start_time = time.time()
start_cpu = psutil.cpu_percent()
start_memory = psutil.virtual_memory().percent
# 执行设备操作
# ... 你的设备操作代码 ...
end_time = time.time()
end_cpu = psutil.cpu_percent()
end_memory = psutil.virtual_memory().percent
print(f"⏱️ 执行时间: {end_time - start_time:.2f} 秒")
print(f"💻 CPU使用: {end_cpu - start_cpu:.1f}%")
print(f"🧠 内存使用: {end_memory - start_memory:.1f}%")
📝 配置文件模板
1. 基础配置模板
标准配置(推荐)
from unilabos.devices.laiyu_liquid import LaiYuLiquidConfig, LaiYuLiquidBackend, LaiYuLiquid
# 创建标准配置
config = LaiYuLiquidConfig(
# === 通信参数 ===
port="/dev/cu.usbserial-3130", # 🔧 修改为实际串口
address=4, # 移液器地址(推荐)
baudrate=115200, # 通信波特率(推荐)
timeout=5.0, # 通信超时时间
# === 工作台尺寸 ===
deck_width=340.0, # 工作台宽度 (mm)
deck_height=250.0, # 工作台高度 (mm)
deck_depth=160.0, # 工作台深度 (mm)
# === 运动控制参数 ===
max_speed=100.0, # 最大移动速度 (mm/s)
acceleration=50.0, # 加速度 (mm/s²)
safe_height=50.0, # 安全移动高度 (mm)
# === 移液参数 ===
max_volume=1000.0, # 最大移液体积 (μL)
min_volume=0.1, # 最小移液体积 (μL)
liquid_detection=True, # 启用液面检测
# === 枪头操作参数 ===
tip_pickup_speed=30, # 取枪头速度 (rpm)
tip_pickup_acceleration=500, # 取枪头加速度 (rpm/s)
tip_pickup_depth=10.0, # 枪头插入深度 (mm)
tip_drop_height=10.0, # 丢弃枪头高度 (mm)
)
# 创建设备实例
backend = LaiYuLiquidBackend(config)
device = LaiYuLiquid(backend)
2. 高级配置模板
多设备配置
# 配置多个LaiYu设备
configs = {
"device_1": LaiYuLiquidConfig(
port="/dev/cu.usbserial-3130",
address=4,
baudrate=115200,
deck_width=340.0,
deck_height=250.0,
deck_depth=160.0
),
"device_2": LaiYuLiquidConfig(
port="/dev/cu.usbserial-3131",
address=4,
baudrate=115200,
deck_width=340.0,
deck_height=250.0,
deck_depth=160.0
)
}
# 创建设备实例
devices = {}
for name, config in configs.items():
backend = LaiYuLiquidBackend(config)
devices[name] = LaiYuLiquid(backend)
自定义参数配置
# 高精度移液配置
precision_config = LaiYuLiquidConfig(
port="/dev/cu.usbserial-3130",
address=4,
baudrate=115200,
timeout=10.0, # 增加超时时间
# 精密运动控制
max_speed=50.0, # 降低速度提高精度
acceleration=25.0, # 降低加速度
safe_height=30.0, # 降低安全高度
# 精密移液参数
max_volume=200.0, # 小体积移液
min_volume=0.5, # 提高最小体积
liquid_detection=True,
# 精密枪头操作
tip_pickup_speed=15, # 降低取枪头速度
tip_pickup_acceleration=250, # 降低加速度
tip_pickup_depth=8.0, # 减少插入深度
tip_drop_height=5.0, # 降低丢弃高度
)
3. 实验协议配置
JSON配置文件模板
{
"experiment_name": "LaiYu液体处理实验",
"version": "1.0",
"devices": {
"laiyu_liquid": {
"type": "LaiYu_Liquid",
"config": {
"port": "/dev/cu.usbserial-3130",
"address": 4,
"baudrate": 115200,
"timeout": 5.0,
"deck_width": 340.0,
"deck_height": 250.0,
"deck_depth": 160.0,
"max_speed": 100.0,
"acceleration": 50.0,
"safe_height": 50.0,
"max_volume": 1000.0,
"min_volume": 0.1,
"liquid_detection": true
}
}
},
"deck_layout": {
"tip_rack": {
"type": "tip_rack_96",
"position": [10, 10, 0],
"tips": "1000μL"
},
"source_plate": {
"type": "plate_96",
"position": [100, 10, 0],
"contents": "样品"
},
"dest_plate": {
"type": "plate_96",
"position": [200, 10, 0],
"contents": "目标"
}
}
}
4. 完整配置示例
{
"laiyu_liquid_config": {
"communication": {
"xyz_controller": {
"port": "/dev/cu.usbserial-3130",
"baudrate": 115200,
"timeout": 5.0
},
"pipette_controller": {
"port": "/dev/cu.usbserial-3131",
"baudrate": 115200,
"address": 4,
"timeout": 5.0
}
},
"mechanical": {
"deck_width": 340.0,
"deck_height": 250.0,
"deck_depth": 160.0,
"safe_height": 50.0
},
"motion": {
"max_speed": 100.0,
"acceleration": 50.0,
"tip_pickup_speed": 30,
"tip_pickup_acceleration": 500
},
"safety": {
"position_validation": true,
"emergency_stop_enabled": true,
"deck_width": 300.0,
"deck_height": 200.0,
"deck_depth": 100.0,
"safe_height": 50.0
}
}
}
5. 完整使用示例
基础移液操作
async def basic_pipetting_example():
"""基础移液操作示例"""
# 1. 设备初始化
config = LaiYuLiquidConfig(
port="/dev/cu.usbserial-3130",
address=4,
baudrate=115200
)
backend = LaiYuLiquidBackend(config)
device = LaiYuLiquid(backend)
try:
# 2. 设备设置
await device.setup()
print("✅ 设备初始化完成")
# 3. 回到原点
await device.home_all_axes()
print("✅ 轴归零完成")
# 4. 取枪头
tip_position = (50, 50, 10) # 枪头架位置
await device.pick_up_tip(tip_position)
print("✅ 取枪头完成")
# 5. 移液操作
source_pos = (100, 100, 15) # 源位置
dest_pos = (200, 200, 15) # 目标位置
volume = 100.0 # 移液体积 (μL)
await device.aspirate(volume, source_pos)
print(f"✅ 吸取 {volume}μL 完成")
await device.dispense(volume, dest_pos)
print(f"✅ 分配 {volume}μL 完成")
# 6. 丢弃枪头
trash_position = (300, 300, 20)
await device.drop_tip(trash_position)
print("✅ 丢弃枪头完成")
except Exception as e:
print(f"❌ 操作失败: {e}")
finally:
# 7. 清理资源
await device.cleanup()
print("✅ 设备清理完成")
# 运行示例
import asyncio
asyncio.run(basic_pipetting_example())
批量处理示例
async def batch_processing_example():
"""批量处理示例"""
config = LaiYuLiquidConfig(
port="/dev/cu.usbserial-3130",
address=4,
baudrate=115200
)
backend = LaiYuLiquidBackend(config)
device = LaiYuLiquid(backend)
try:
await device.setup()
await device.home_all_axes()
# 定义位置
tip_rack = [(50 + i*9, 50, 10) for i in range(12)] # 12个枪头位置
source_wells = [(100 + i*9, 100, 15) for i in range(12)] # 12个源孔
dest_wells = [(200 + i*9, 200, 15) for i in range(12)] # 12个目标孔
# 批量移液
for i in range(12):
print(f"🔄 处理第 {i+1} 个样品...")
# 取枪头
await device.pick_up_tip(tip_rack[i])
# 移液
await device.aspirate(50.0, source_wells[i])
await device.dispense(50.0, dest_wells[i])
# 丢弃枪头
await device.drop_tip((300, 300, 20))
print(f"✅ 第 {i+1} 个样品处理完成")
print("🎉 批量处理完成!")
except Exception as e:
print(f"❌ 批量处理失败: {e}")
finally:
await device.cleanup()
# 运行批量处理
asyncio.run(batch_processing_example())
🔧 调试与日志管理
1. 调试模式配置
启用全局调试
import logging
from unilabos.devices.laiyu_liquid import LaiYuLiquidConfig, LaiYuLiquidBackend
# 配置全局日志
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('laiyu_debug.log'),
logging.StreamHandler()
]
)
# 创建调试配置
debug_config = LaiYuLiquidConfig(
port="/dev/cu.usbserial-3130",
address=4,
baudrate=115200,
timeout=10.0, # 增加超时时间便于调试
debug_mode=True # 启用调试模式
)
分级日志配置
def setup_logging(log_level="INFO"):
"""设置分级日志"""
# 日志级别映射
levels = {
"DEBUG": logging.DEBUG,
"INFO": logging.INFO,
"WARNING": logging.WARNING,
"ERROR": logging.ERROR
}
# 创建日志记录器
logger = logging.getLogger('LaiYu_Liquid')
logger.setLevel(levels.get(log_level, logging.INFO))
# 文件处理器
file_handler = logging.FileHandler('laiyu_operations.log')
file_formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s'
)
file_handler.setFormatter(file_formatter)
# 控制台处理器
console_handler = logging.StreamHandler()
console_formatter = logging.Formatter('%(levelname)s - %(message)s')
console_handler.setFormatter(console_formatter)
logger.addHandler(file_handler)
logger.addHandler(console_handler)
return logger
# 使用示例
logger = setup_logging("DEBUG")
logger.info("开始LaiYu设备操作")
2. 通信监控
串口通信日志
def enable_serial_logging():
"""启用串口通信日志"""
import serial
# 创建串口日志记录器
serial_logger = logging.getLogger('serial.communication')
serial_logger.setLevel(logging.DEBUG)
# 创建专用的串口日志文件
serial_handler = logging.FileHandler('laiyu_serial.log')
serial_formatter = logging.Formatter(
'%(asctime)s - SERIAL - %(message)s'
)
serial_handler.setFormatter(serial_formatter)
serial_logger.addHandler(serial_handler)
print("📡 串口通信日志已启用: laiyu_serial.log")
return serial_logger
实时通信监控
class CommunicationMonitor:
"""通信监控器"""
def __init__(self):
self.sent_count = 0
self.received_count = 0
self.error_count = 0
self.start_time = time.time()
def log_sent(self, data):
"""记录发送数据"""
self.sent_count += 1
logging.debug(f"📤 发送 #{self.sent_count}: {data.hex()}")
def log_received(self, data):
"""记录接收数据"""
self.received_count += 1
logging.debug(f"📥 接收 #{self.received_count}: {data.hex()}")
def log_error(self, error):
"""记录错误"""
self.error_count += 1
logging.error(f"❌ 通信错误 #{self.error_count}: {error}")
def get_statistics(self):
"""获取统计信息"""
duration = time.time() - self.start_time
return {
"运行时间": f"{duration:.2f}秒",
"发送次数": self.sent_count,
"接收次数": self.received_count,
"错误次数": self.error_count,
"成功率": f"{((self.sent_count - self.error_count) / max(self.sent_count, 1) * 100):.1f}%"
}
3. 性能监控
操作性能分析
import time
import functools
def performance_monitor(operation_name):
"""性能监控装饰器"""
def decorator(func):
@functools.wraps(func)
async def wrapper(*args, **kwargs):
start_time = time.time()
start_memory = psutil.Process().memory_info().rss / 1024 / 1024 # MB
try:
result = await func(*args, **kwargs)
end_time = time.time()
end_memory = psutil.Process().memory_info().rss / 1024 / 1024 # MB
duration = end_time - start_time
memory_delta = end_memory - start_memory
logging.info(f"⏱️ {operation_name}: {duration:.3f}s, 内存变化: {memory_delta:+.1f}MB")
return result
except Exception as e:
end_time = time.time()
duration = end_time - start_time
logging.error(f"❌ {operation_name} 失败 ({duration:.3f}s): {e}")
raise
return wrapper
return decorator
# 使用示例
@performance_monitor("移液操作")
async def monitored_pipetting():
await device.aspirate(100.0, (100, 100, 15))
await device.dispense(100.0, (200, 200, 15))
系统资源监控
import psutil
import threading
import time
class SystemMonitor:
"""系统资源监控器"""
def __init__(self, interval=1.0):
self.interval = interval
self.monitoring = False
self.data = []
def start_monitoring(self):
"""开始监控"""
self.monitoring = True
self.monitor_thread = threading.Thread(target=self._monitor_loop)
self.monitor_thread.daemon = True
self.monitor_thread.start()
print("📊 系统监控已启动")
def stop_monitoring(self):
"""停止监控"""
self.monitoring = False
if hasattr(self, 'monitor_thread'):
self.monitor_thread.join()
print("📊 系统监控已停止")
def _monitor_loop(self):
"""监控循环"""
while self.monitoring:
cpu_percent = psutil.cpu_percent()
memory = psutil.virtual_memory()
self.data.append({
'timestamp': time.time(),
'cpu_percent': cpu_percent,
'memory_percent': memory.percent,
'memory_used_mb': memory.used / 1024 / 1024
})
time.sleep(self.interval)
def get_report(self):
"""生成监控报告"""
if not self.data:
return "无监控数据"
avg_cpu = sum(d['cpu_percent'] for d in self.data) / len(self.data)
avg_memory = sum(d['memory_percent'] for d in self.data) / len(self.data)
max_memory = max(d['memory_used_mb'] for d in self.data)
return f"""
📊 系统资源监控报告
==================
监控时长: {len(self.data) * self.interval:.1f}秒
平均CPU使用率: {avg_cpu:.1f}%
平均内存使用率: {avg_memory:.1f}%
峰值内存使用: {max_memory:.1f}MB
"""
# 使用示例
monitor = SystemMonitor()
monitor.start_monitoring()
# 执行设备操作
# ... 你的代码 ...
monitor.stop_monitoring()
print(monitor.get_report())
4. 错误追踪
异常处理和记录
import traceback
class ErrorTracker:
"""错误追踪器"""
def __init__(self):
self.errors = []
def log_error(self, operation, error, context=None):
"""记录错误"""
error_info = {
'timestamp': time.time(),
'operation': operation,
'error_type': type(error).__name__,
'error_message': str(error),
'traceback': traceback.format_exc(),
'context': context or {}
}
self.errors.append(error_info)
# 记录到日志
logging.error(f"❌ {operation} 失败: {error}")
logging.debug(f"错误详情: {error_info}")
def get_error_summary(self):
"""获取错误摘要"""
if not self.errors:
return "✅ 无错误记录"
error_types = {}
for error in self.errors:
error_type = error['error_type']
error_types[error_type] = error_types.get(error_type, 0) + 1
summary = f"❌ 共记录 {len(self.errors)} 个错误:\n"
for error_type, count in error_types.items():
summary += f" - {error_type}: {count} 次\n"
return summary
# 全局错误追踪器
error_tracker = ErrorTracker()
# 使用示例
try:
await device.move_to(x=1000, y=1000, z=100) # 可能超出范围
except Exception as e:
error_tracker.log_error("移动操作", e, {"target": (1000, 1000, 100)})
📚 总结
本文档提供了LaiYu液体处理设备的完整硬件连接配置指南,涵盖了从基础设置到高级故障排除的所有方面。
🎯 关键要点
- 标准配置: 使用
port="/dev/cu.usbserial-3130",address=4,baudrate=115200 - 设备架构: XYZ轴控制器(地址1-3) + SOPA移液器(地址4)
- 连接验证: 使用提供的测试脚本验证硬件连接
- 故障排除: 参考故障排除指南解决常见问题
- 性能监控: 启用日志和监控确保稳定运行
🔗 相关文档
📞 技术支持
如遇到问题,请:
- 检查硬件连接和配置
- 查看调试日志
- 参考故障排除指南
- 联系技术支持团队
最后更新: 2024年1月