mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-06 23:15:10 +00:00
Compare commits
10 Commits
prcix9320
...
52dee44835
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52dee44835 | ||
|
|
f8fd27ae37 | ||
|
|
e959c53075 | ||
|
|
961362eecc | ||
|
|
0c086519fd | ||
|
|
ee9248f7b2 | ||
|
|
d4f0155875 | ||
|
|
04941757bb | ||
|
|
c598886eea | ||
|
|
827d88d75a |
@@ -36,6 +36,7 @@ requirements:
|
||||
- conda-forge::python ==3.11.11
|
||||
- compilers
|
||||
- cmake
|
||||
- zstd
|
||||
- ninja
|
||||
- if: unix
|
||||
then:
|
||||
|
||||
@@ -170,12 +170,15 @@
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"volume": 1000.0,
|
||||
"reagent": "DMF"
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"current_volume": 1000.0,
|
||||
"reagent_name": "DMF"
|
||||
"liquids": [
|
||||
{
|
||||
"liquid_type": "DMF",
|
||||
"liquid_volume": 1000.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -191,12 +194,15 @@
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"volume": 1000.0,
|
||||
"reagent": "ethyl_acetate"
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"current_volume": 1000.0,
|
||||
"reagent_name": "ethyl_acetate"
|
||||
"liquids": [
|
||||
{
|
||||
"liquid_type": "ethyl_acetate",
|
||||
"liquid_volume": 1000.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -212,12 +218,15 @@
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"volume": 1000.0,
|
||||
"reagent": "hexane"
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"current_volume": 1000.0,
|
||||
"reagent_name": "hexane"
|
||||
"liquids": [
|
||||
{
|
||||
"liquid_type": "hexane",
|
||||
"liquid_volume": 1000.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -233,12 +242,15 @@
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"volume": 1000.0,
|
||||
"reagent": "methanol"
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"current_volume": 1000.0,
|
||||
"reagent_name": "methanol"
|
||||
"liquids": [
|
||||
{
|
||||
"liquid_type": "methanol",
|
||||
"liquid_volume": 1000.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -254,12 +266,15 @@
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"volume": 1000.0,
|
||||
"reagent": "water"
|
||||
"max_volume": 1000.0
|
||||
},
|
||||
"data": {
|
||||
"current_volume": 1000.0,
|
||||
"reagent_name": "water"
|
||||
"liquids": [
|
||||
{
|
||||
"liquid_type": "water",
|
||||
"liquid_volume": 1000.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -319,15 +334,15 @@
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"volume": 500.0,
|
||||
"max_volume": 500.0,
|
||||
"max_temp": 200.0,
|
||||
"min_temp": -20.0,
|
||||
"has_stirrer": true,
|
||||
"has_heater": true
|
||||
},
|
||||
"data": {
|
||||
"current_volume": 0.0,
|
||||
"current_temp": 25.0
|
||||
"liquids": [
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -404,10 +419,11 @@
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"volume": 2000.0
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"current_volume": 0.0
|
||||
"liquids": [
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -423,10 +439,11 @@
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"volume": 2000.0
|
||||
"max_volume": 2000.0
|
||||
},
|
||||
"data": {
|
||||
"current_volume": 0.0
|
||||
"liquids": [
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -632,10 +649,11 @@
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"volume": 250.0
|
||||
"max_volume": 250.0
|
||||
},
|
||||
"data": {
|
||||
"current_volume": 0.0
|
||||
"liquids": [
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -651,10 +669,11 @@
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"volume": 250.0
|
||||
"max_volume": 250.0
|
||||
},
|
||||
"data": {
|
||||
"current_volume": 0.0
|
||||
"liquids": [
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -670,10 +689,11 @@
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"volume": 250.0
|
||||
"max_volume": 250.0
|
||||
},
|
||||
"data": {
|
||||
"current_volume": 0.0
|
||||
"liquids": [
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -712,7 +732,7 @@
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"volume": 500.0,
|
||||
"max_volume": 500.0,
|
||||
"reagent": "sodium_chloride",
|
||||
"physical_state": "solid"
|
||||
},
|
||||
|
||||
254
test_dummy2_integration.py
Normal file
254
test_dummy2_integration.py
Normal file
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Dummy2 机械臂接入 UniLab 系统测试脚本
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import yaml
|
||||
import json
|
||||
|
||||
def test_device_model_files():
|
||||
"""测试设备模型文件是否完整"""
|
||||
print("=== 测试设备模型文件 ===")
|
||||
|
||||
device_path = "/Users/dp/Documents/Uni-Lab-OS/unilabos/device_mesh/devices/dummy2_robot"
|
||||
|
||||
required_files = [
|
||||
"macro_device.xacro",
|
||||
"dummy2_robot.json",
|
||||
"config/joint_limits.yaml",
|
||||
"config/default_kinematics.yaml",
|
||||
"config/physical_parameters.yaml",
|
||||
"config/visual_parameters.yaml"
|
||||
]
|
||||
|
||||
required_meshes = [
|
||||
"meshes/base_link.stl",
|
||||
"meshes/J1_1.stl",
|
||||
"meshes/J2_1.stl",
|
||||
"meshes/J3_1.stl",
|
||||
"meshes/J4_1.stl",
|
||||
"meshes/J5_1.stl",
|
||||
"meshes/J6_1.stl",
|
||||
"meshes/camera_1.stl"
|
||||
]
|
||||
|
||||
all_files = required_files + required_meshes
|
||||
missing_files = []
|
||||
|
||||
for file_path in all_files:
|
||||
full_path = os.path.join(device_path, file_path)
|
||||
if not os.path.exists(full_path):
|
||||
missing_files.append(file_path)
|
||||
else:
|
||||
print(f"✅ {file_path}")
|
||||
|
||||
if missing_files:
|
||||
print(f"❌ 缺少文件: {missing_files}")
|
||||
return False
|
||||
else:
|
||||
print("✅ 所有模型文件都存在")
|
||||
return True
|
||||
|
||||
def test_driver_file():
|
||||
"""测试驱动文件"""
|
||||
print("\n=== 测试驱动文件 ===")
|
||||
|
||||
driver_path = "/Users/dp/Documents/Uni-Lab-OS/unilabos/devices/ros_dev/moveit_interface.py"
|
||||
|
||||
if not os.path.exists(driver_path):
|
||||
print(f"❌ 驱动文件不存在: {driver_path}")
|
||||
return False
|
||||
|
||||
try:
|
||||
# 尝试导入驱动类
|
||||
sys.path.insert(0, os.path.dirname(driver_path))
|
||||
from unilabos.devices.ros_dev.moveit_interface import MoveitInterface
|
||||
print("✅ 驱动文件存在且可导入")
|
||||
|
||||
# 检查必要的方法
|
||||
required_methods = [
|
||||
'__init__',
|
||||
'post_init',
|
||||
'check_tf_update_actions',
|
||||
'resource_manager',
|
||||
'wait_for_resource_action',
|
||||
'moveit_joint_task',
|
||||
'moveit_task'
|
||||
]
|
||||
|
||||
missing_methods = []
|
||||
for method in required_methods:
|
||||
if not hasattr(MoveitInterface, method):
|
||||
missing_methods.append(method)
|
||||
|
||||
if missing_methods:
|
||||
print(f"❌ 驱动类缺少方法: {missing_methods}")
|
||||
return False
|
||||
else:
|
||||
print("✅ 驱动类包含所有必要方法")
|
||||
return True
|
||||
|
||||
except ImportError as e:
|
||||
print(f"❌ 驱动文件导入失败: {e}")
|
||||
return False
|
||||
|
||||
def test_registry_config():
|
||||
"""测试注册表配置"""
|
||||
print("\n=== 测试注册表配置 ===")
|
||||
|
||||
registry_path = "/Users/dp/Documents/Uni-Lab-OS/unilabos/registry/devices/robot_arm.yaml"
|
||||
|
||||
if not os.path.exists(registry_path):
|
||||
print(f"❌ 注册表文件不存在: {registry_path}")
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(registry_path, 'r', encoding='utf-8') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
if 'robotic_arm.Dummy2' not in config:
|
||||
print("❌ 注册表中没有找到 robotic_arm.Dummy2 配置")
|
||||
return False
|
||||
|
||||
dummy2_config = config['robotic_arm.Dummy2']
|
||||
|
||||
# 检查必要的配置项
|
||||
required_keys = [
|
||||
'category',
|
||||
'class',
|
||||
'description',
|
||||
'init_param_schema',
|
||||
'model',
|
||||
'version'
|
||||
]
|
||||
|
||||
missing_keys = []
|
||||
for key in required_keys:
|
||||
if key not in dummy2_config:
|
||||
missing_keys.append(key)
|
||||
|
||||
if missing_keys:
|
||||
print(f"❌ Dummy2配置缺少字段: {missing_keys}")
|
||||
return False
|
||||
|
||||
# 检查模块路径
|
||||
module_path = dummy2_config.get('class', {}).get('module')
|
||||
if module_path != 'unilabos.devices.ros_dev.moveit_interface:MoveitInterface':
|
||||
print(f"❌ 模块路径不正确: {module_path}")
|
||||
return False
|
||||
|
||||
# 检查动作定义
|
||||
actions = dummy2_config.get('class', {}).get('action_value_mappings', {})
|
||||
required_actions = [
|
||||
'auto-check_tf_update_actions',
|
||||
'auto-post_init',
|
||||
'auto-resource_manager',
|
||||
'auto-wait_for_resource_action',
|
||||
'auto-moveit_joint_task',
|
||||
'auto-moveit_task',
|
||||
'pick_and_place'
|
||||
]
|
||||
|
||||
missing_actions = []
|
||||
for action in required_actions:
|
||||
if action not in actions:
|
||||
missing_actions.append(action)
|
||||
|
||||
if missing_actions:
|
||||
print(f"❌ 缺少动作定义: {missing_actions}")
|
||||
return False
|
||||
|
||||
print("✅ 注册表配置完整且正确")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 注册表配置检查失败: {e}")
|
||||
return False
|
||||
|
||||
def test_can2eth_connectivity():
|
||||
"""测试CAN2ETH连接(可选)"""
|
||||
print("\n=== 测试CAN2ETH连接 ===")
|
||||
|
||||
try:
|
||||
import socket
|
||||
import struct
|
||||
|
||||
# 尝试连接CAN2ETH网关
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.settimeout(2.0)
|
||||
|
||||
can2eth_host = "192.168.8.88"
|
||||
can2eth_port = 8080
|
||||
|
||||
# 发送ping命令
|
||||
ping_cmd = struct.pack('>B', 0xFF)
|
||||
sock.sendto(ping_cmd, (can2eth_host, can2eth_port))
|
||||
|
||||
try:
|
||||
data, addr = sock.recvfrom(1024)
|
||||
if len(data) > 0:
|
||||
print(f"✅ CAN2ETH网关 {can2eth_host}:{can2eth_port} 连接成功")
|
||||
return True
|
||||
except socket.timeout:
|
||||
print(f"⚠️ CAN2ETH网关 {can2eth_host}:{can2eth_port} 无响应(可能未启动)")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ CAN2ETH连接测试失败: {e}")
|
||||
return False
|
||||
finally:
|
||||
if 'sock' in locals():
|
||||
sock.close()
|
||||
|
||||
def main():
|
||||
"""主测试函数"""
|
||||
print("🤖 Dummy2 机械臂接入 UniLab 系统测试")
|
||||
print("=" * 50)
|
||||
|
||||
tests = [
|
||||
("设备模型文件", test_device_model_files),
|
||||
("驱动文件", test_driver_file),
|
||||
("注册表配置", test_registry_config),
|
||||
("CAN2ETH连接", test_can2eth_connectivity)
|
||||
]
|
||||
|
||||
results = []
|
||||
|
||||
for test_name, test_func in tests:
|
||||
try:
|
||||
result = test_func()
|
||||
results.append((test_name, result))
|
||||
except Exception as e:
|
||||
print(f"❌ {test_name}测试异常: {e}")
|
||||
results.append((test_name, False))
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("📊 测试结果汇总:")
|
||||
|
||||
passed = 0
|
||||
total = len(results)
|
||||
|
||||
for test_name, result in results:
|
||||
status = "✅ 通过" if result else "❌ 失败"
|
||||
print(f" {test_name}: {status}")
|
||||
if result:
|
||||
passed += 1
|
||||
|
||||
print(f"\n总体结果: {passed}/{total} 项测试通过")
|
||||
|
||||
if passed == total:
|
||||
print("🎉 Dummy2 机械臂已成功接入 UniLab 系统!")
|
||||
print("\n📋 后续步骤:")
|
||||
print("1. 启动 CAN2ETH 服务: ros2 launch dummy2_can2eth dummy2_can2eth_server.launch.py")
|
||||
print("2. 在 UniLab 界面中添加 Dummy2 设备实例")
|
||||
print("3. 测试设备初始化和基本功能")
|
||||
else:
|
||||
print("⚠️ 还有一些问题需要解决才能完全接入")
|
||||
|
||||
return passed == total
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -160,8 +160,8 @@ def generate_filter_protocol(
|
||||
# 使用pump protocol转移液体到过滤器
|
||||
transfer_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=vessel_id, # 🔧 使用 vessel_id
|
||||
to_vessel=filter_device,
|
||||
from_vessel={"id": vessel_id}, # 🔧 使用 vessel_id
|
||||
to_vessel={"id": filter_device},
|
||||
volume=0.0, # 转移所有液体
|
||||
amount="",
|
||||
time=0.0,
|
||||
@@ -212,8 +212,8 @@ def generate_filter_protocol(
|
||||
# 构建过滤动作参数
|
||||
debug_print(" ⚙️ 构建过滤参数...")
|
||||
filter_kwargs = {
|
||||
"vessel": filter_device, # 过滤器设备
|
||||
"filtrate_vessel": filtrate_vessel_id, # 滤液容器(可能为空)
|
||||
"vessel": {"id": filter_device}, # 过滤器设备
|
||||
"filtrate_vessel": {"id": filtrate_vessel_id}, # 滤液容器(可能为空)
|
||||
"stir": kwargs.get("stir", False),
|
||||
"stir_speed": kwargs.get("stir_speed", 0.0),
|
||||
"temp": kwargs.get("temp", 25.0),
|
||||
@@ -244,7 +244,7 @@ def generate_filter_protocol(
|
||||
# === 收集滤液(如果需要)===
|
||||
debug_print("📍 步骤5: 收集滤液... 💧")
|
||||
|
||||
if filtrate_vessel:
|
||||
if filtrate_vessel_id and filtrate_vessel_id not in G.neighbors(filter_device):
|
||||
debug_print(f" 🧪 收集滤液: {filter_device} → {filtrate_vessel_id} 💧")
|
||||
|
||||
try:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -125,6 +125,29 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
"""
|
||||
debug_print(f"🔍 正在查找溶剂 '{solvent}' 的容器... 🧪")
|
||||
|
||||
# 第四步:通过数据中的试剂信息匹配
|
||||
debug_print(" 🧪 步骤1: 数据试剂信息匹配...")
|
||||
for node_id in G.nodes():
|
||||
debug_print(f"查找 id {node_id}, type={G.nodes[node_id].get('type')}, data={G.nodes[node_id].get('data', {})} 的容器...")
|
||||
if G.nodes[node_id].get('type') == 'container':
|
||||
vessel_data = G.nodes[node_id].get('data', {})
|
||||
|
||||
# 检查 data 中的 reagent_name 字段
|
||||
reagent_name = vessel_data.get('reagent_name', '').lower()
|
||||
if reagent_name and solvent.lower() == reagent_name:
|
||||
debug_print(f" 🎉 通过data.reagent_name匹配找到容器: {node_id} (试剂: {reagent_name}) ✨")
|
||||
return node_id
|
||||
|
||||
# 检查 data 中的液体信息
|
||||
liquids = vessel_data.get('liquid', []) or vessel_data.get('liquids', [])
|
||||
for liquid in liquids:
|
||||
if isinstance(liquid, dict):
|
||||
liquid_type = (liquid.get('liquid_type') or liquid.get('name', '')).lower()
|
||||
|
||||
if solvent.lower() == liquid_type or solvent.lower() in liquid_type:
|
||||
debug_print(f" 🎉 通过液体类型匹配找到容器: {node_id} (液体类型: {liquid_type}) ✨")
|
||||
return node_id
|
||||
|
||||
# 构建可能的容器名称
|
||||
possible_names = [
|
||||
f"flask_{solvent}",
|
||||
@@ -140,14 +163,14 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
debug_print(f"📋 候选容器名称: {possible_names[:3]}... (共{len(possible_names)}个) 📝")
|
||||
|
||||
# 第一步:通过容器名称匹配
|
||||
debug_print(" 🎯 步骤1: 精确名称匹配...")
|
||||
debug_print(" 🎯 步骤2: 精确名称匹配...")
|
||||
for vessel_name in possible_names:
|
||||
if vessel_name in G.nodes():
|
||||
debug_print(f" 🎉 通过名称匹配找到容器: {vessel_name} ✨")
|
||||
return vessel_name
|
||||
|
||||
# 第二步:通过模糊匹配(节点ID和名称)
|
||||
debug_print(" 🔍 步骤2: 模糊名称匹配...")
|
||||
debug_print(" 🔍 步骤3: 模糊名称匹配...")
|
||||
for node_id in G.nodes():
|
||||
if G.nodes[node_id].get('type') == 'container':
|
||||
node_name = G.nodes[node_id].get('name', '').lower()
|
||||
@@ -157,7 +180,7 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
return node_id
|
||||
|
||||
# 第三步:通过配置中的试剂信息匹配
|
||||
debug_print(" 🧪 步骤3: 配置试剂信息匹配...")
|
||||
debug_print(" 🧪 步骤4: 配置试剂信息匹配...")
|
||||
for node_id in G.nodes():
|
||||
if G.nodes[node_id].get('type') == 'container':
|
||||
# 检查 config 中的 reagent 字段
|
||||
@@ -168,28 +191,6 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
debug_print(f" 🎉 通过config.reagent匹配找到容器: {node_id} (试剂: {config_reagent}) ✨")
|
||||
return node_id
|
||||
|
||||
# 第四步:通过数据中的试剂信息匹配
|
||||
debug_print(" 🧪 步骤4: 数据试剂信息匹配...")
|
||||
for node_id in G.nodes():
|
||||
if G.nodes[node_id].get('type') == 'container':
|
||||
vessel_data = G.nodes[node_id].get('data', {})
|
||||
|
||||
# 检查 data 中的 reagent_name 字段
|
||||
reagent_name = vessel_data.get('reagent_name', '').lower()
|
||||
if reagent_name and solvent.lower() == reagent_name:
|
||||
debug_print(f" 🎉 通过data.reagent_name匹配找到容器: {node_id} (试剂: {reagent_name}) ✨")
|
||||
return node_id
|
||||
|
||||
# 检查 data 中的液体信息
|
||||
liquids = vessel_data.get('liquid', [])
|
||||
for liquid in liquids:
|
||||
if isinstance(liquid, dict):
|
||||
liquid_type = (liquid.get('liquid_type') or liquid.get('name', '')).lower()
|
||||
|
||||
if solvent.lower() in liquid_type:
|
||||
debug_print(f" 🎉 通过液体类型匹配找到容器: {node_id} (液体类型: {liquid_type}) ✨")
|
||||
return node_id
|
||||
|
||||
# 第五步:部分匹配(如果前面都没找到)
|
||||
debug_print(" 🔍 步骤5: 部分匹配...")
|
||||
for node_id in G.nodes():
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
dummy2_robot:
|
||||
kinematics:
|
||||
# DH parameters for Dummy2 6-DOF robot arm
|
||||
# [theta, d, a, alpha] for each joint
|
||||
joint_1: [0.0, 0.1, 0.0, 1.5708] # Base rotation
|
||||
joint_2: [0.0, 0.0, 0.2, 0.0] # Shoulder
|
||||
joint_3: [0.0, 0.0, 0.15, 0.0] # Elbow
|
||||
joint_4: [0.0, 0.1, 0.0, 1.5708] # Wrist roll
|
||||
joint_5: [0.0, 0.0, 0.0, -1.5708] # Wrist pitch
|
||||
joint_6: [0.0, 0.06, 0.0, 0.0] # Wrist yaw
|
||||
|
||||
# Tool center point offset from last joint
|
||||
tcp_offset:
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.04
|
||||
|
||||
# Workspace limits
|
||||
workspace:
|
||||
x_min: -0.5
|
||||
x_max: 0.5
|
||||
y_min: -0.5
|
||||
y_max: 0.5
|
||||
z_min: 0.0
|
||||
z_max: 0.6
|
||||
45
unilabos/device_mesh/devices/dummy2_robot/config/dummy2.srdf
Normal file
45
unilabos/device_mesh/devices/dummy2_robot/config/dummy2.srdf
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--This does not replace URDF, and is not an extension of URDF.
|
||||
This is a format for representing semantic information about the robot structure.
|
||||
A URDF file must exist for this robot as well, where the joints and the links that are referenced are defined
|
||||
-->
|
||||
<robot name="dummy2">
|
||||
<!--GROUPS: Representation of a set of joints and links. This can be useful for specifying DOF to plan for, defining arms, end effectors, etc-->
|
||||
<!--LINKS: When a link is specified, the parent joint of that link (if it exists) is automatically included-->
|
||||
<!--JOINTS: When a joint is specified, the child link of that joint (which will always exist) is automatically included-->
|
||||
<!--CHAINS: When a chain is specified, all the links along the chain (including endpoints) are included in the group. Additionally, all the joints that are parents to included links are also included. This means that joints along the chain and the parent joint of the base link are included in the group-->
|
||||
<!--SUBGROUPS: Groups can also be formed by referencing to already defined group names-->
|
||||
<group name="dummy2_arm">
|
||||
<joint name="virtual_joint"/>
|
||||
<joint name="Joint1"/>
|
||||
<joint name="Joint2"/>
|
||||
<joint name="Joint3"/>
|
||||
<joint name="Joint4"/>
|
||||
<joint name="Joint5"/>
|
||||
<joint name="Joint6"/>
|
||||
</group>
|
||||
<!--GROUP STATES: Purpose: Define a named state for a particular group, in terms of joint values. This is useful to define states like 'folded arms'-->
|
||||
<group_state name="home" group="dummy2_arm">
|
||||
<joint name="Joint1" value="0"/>
|
||||
<joint name="Joint2" value="0"/>
|
||||
<joint name="Joint3" value="0"/>
|
||||
<joint name="Joint4" value="0"/>
|
||||
<joint name="Joint5" value="0"/>
|
||||
<joint name="Joint6" value="0"/>
|
||||
</group_state>
|
||||
<!--VIRTUAL JOINT: Purpose: this element defines a virtual joint between a robot link and an external frame of reference (considered fixed with respect to the robot)-->
|
||||
<virtual_joint name="virtual_joint" type="fixed" parent_frame="world" child_link="base_link"/>
|
||||
<!--DISABLE COLLISIONS: By default it is assumed that any link of the robot could potentially come into collision with any other link in the robot. This tag disables collision checking between a specified pair of links. -->
|
||||
<disable_collisions link1="J1_1" link2="J2_1" reason="Adjacent"/>
|
||||
<disable_collisions link1="J1_1" link2="J3_1" reason="Never"/>
|
||||
<disable_collisions link1="J1_1" link2="J4_1" reason="Never"/>
|
||||
<disable_collisions link1="J1_1" link2="base_link" reason="Adjacent"/>
|
||||
<disable_collisions link1="J2_1" link2="J3_1" reason="Adjacent"/>
|
||||
<disable_collisions link1="J3_1" link2="J4_1" reason="Adjacent"/>
|
||||
<disable_collisions link1="J3_1" link2="J5_1" reason="Never"/>
|
||||
<disable_collisions link1="J3_1" link2="J6_1" reason="Never"/>
|
||||
<disable_collisions link1="J3_1" link2="base_link" reason="Never"/>
|
||||
<disable_collisions link1="J4_1" link2="J5_1" reason="Adjacent"/>
|
||||
<disable_collisions link1="J4_1" link2="J6_1" reason="Never"/>
|
||||
<disable_collisions link1="J5_1" link2="J6_1" reason="Adjacent"/>
|
||||
</robot>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="dummy2">
|
||||
<xacro:arg name="initial_positions_file" default="initial_positions.yaml" />
|
||||
|
||||
<!-- Import dummy2 urdf file -->
|
||||
<xacro:include filename="$(find dummy2_description)/urdf/dummy2.xacro" />
|
||||
|
||||
<!-- Import control_xacro -->
|
||||
<xacro:include filename="dummy2.ros2_control.xacro" />
|
||||
|
||||
|
||||
<xacro:dummy2_ros2_control name="FakeSystem" initial_positions_file="$(arg initial_positions_file)"/>
|
||||
|
||||
</robot>
|
||||
237
unilabos/device_mesh/devices/dummy2_robot/config/dummy2.xacro
Normal file
237
unilabos/device_mesh/devices/dummy2_robot/config/dummy2.xacro
Normal file
@@ -0,0 +1,237 @@
|
||||
<?xml version="1.0" ?>
|
||||
<robot name="dummy2" xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
|
||||
<xacro:include filename="$(find dummy2_description)/urdf/materials.xacro" />
|
||||
<xacro:include filename="$(find dummy2_description)/urdf/dummy2.trans" />
|
||||
<xacro:include filename="$(find dummy2_description)/urdf/dummy2.gazebo" />
|
||||
<link name="world" />
|
||||
<joint name="world_joint" type="fixed">
|
||||
<parent link="world" />
|
||||
<child link = "base_link" />
|
||||
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
|
||||
</joint>
|
||||
|
||||
<link name="base_link">
|
||||
<inertial>
|
||||
<origin xyz="0.00010022425916431473 -6.186605493937309e-05 0.05493640543484716" rpy="0 0 0"/>
|
||||
<mass value="1.2152141810431654"/>
|
||||
<inertia ixx="0.002105" iyy="0.002245" izz="0.002436" ixy="-0.0" iyz="-1.1e-05" ixz="0.0"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/base_link.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/base_link.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="J1_1">
|
||||
<inertial>
|
||||
<origin xyz="-0.00617659688932347 0.007029599744830012 0.012866826083045027" rpy="0 0 0"/>
|
||||
<mass value="0.1332774369186824"/>
|
||||
<inertia ixx="6e-05" iyy="5e-05" izz="8.8e-05" ixy="2.1e-05" iyz="-1.4e-05" ixz="8e-06"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="-0.0001 0.000289 -0.097579" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J1_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.0001 0.000289 -0.097579" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J1_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="J2_1">
|
||||
<inertial>
|
||||
<origin xyz="0.019335709221765855 0.0019392793940843159 0.07795928103332703" rpy="0 0 0"/>
|
||||
<mass value="1.9268013917303417"/>
|
||||
<inertia ixx="0.006165" iyy="0.006538" izz="0.00118" ixy="-3e-06" iyz="4.7e-05" ixz="0.0007"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="0.011539 -0.034188 -0.12478" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J2_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0.011539 -0.034188 -0.12478" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J2_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="J3_1">
|
||||
<inertial>
|
||||
<origin xyz="-0.010672101243726572 -0.02723871972304964 0.04876701375652198" rpy="0 0 0"/>
|
||||
<mass value="0.30531962155452225"/>
|
||||
<inertia ixx="0.00029" iyy="0.000238" izz="0.000191" ixy="-1.3e-05" iyz="4.1e-05" ixz="3e-05"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="-0.023811 -0.034188 -0.28278" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J3_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.023811 -0.034188 -0.28278" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J3_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="J4_1">
|
||||
<inertial>
|
||||
<origin xyz="-0.005237398377441591 0.06002028183461833 0.0005891767740203724" rpy="0 0 0"/>
|
||||
<mass value="0.14051172121899885"/>
|
||||
<inertia ixx="0.000245" iyy="7.9e-05" izz="0.00027" ixy="1.6e-05" iyz="-2e-06" ixz="1e-06"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="-0.010649 -0.038288 -0.345246" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J4_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.010649 -0.038288 -0.345246" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J4_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="J5_1">
|
||||
<inertial>
|
||||
<origin xyz="-0.014389813882964664 0.07305218143664277 -0.0009243405950149497" rpy="0 0 0"/>
|
||||
<mass value="0.7783315754227634"/>
|
||||
<inertia ixx="0.000879" iyy="0.000339" izz="0.000964" ixy="0.000146" iyz="1e-06" ixz="-5e-06"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="-0.031949 -0.148289 -0.345246" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J5_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.031949 -0.148289 -0.345246" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J5_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="J6_1">
|
||||
<inertial>
|
||||
<origin xyz="3.967160300787087e-07 0.0004995066702210837 1.4402781733924286e-07" rpy="0 0 0"/>
|
||||
<mass value="0.0020561527568204153"/>
|
||||
<inertia ixx="0.0" iyy="0.0" izz="0.0" ixy="0.0" iyz="0.0" ixz="0.0"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="-0.012127 -0.267789 -0.344021" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J6_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.012127 -0.267789 -0.344021" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J6_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="camera">
|
||||
<inertial>
|
||||
<origin xyz="-0.0006059984983273845 0.0005864706438700462 0.04601775357664567" rpy="0 0 0"/>
|
||||
<mass value="0.21961029019655884"/>
|
||||
<inertia ixx="2.9e-05" iyy="0.000206" izz="0.000198" ixy="-0.0" iyz="2e-06" ixz="-0.0"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="-0.012661 -0.239774 -0.37985" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/camera_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.012661 -0.239774 -0.37985" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/camera_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="Joint1" type="revolute">
|
||||
<origin xyz="0.0001 -0.000289 0.097579" rpy="0 0 0"/>
|
||||
<parent link="base_link"/>
|
||||
<child link="J1_1"/>
|
||||
<axis xyz="-0.0 -0.0 1.0"/>
|
||||
<limit upper="3.054326" lower="-3.054326" effort="100" velocity="100"/>
|
||||
</joint>
|
||||
|
||||
<joint name="Joint2" type="revolute">
|
||||
<origin xyz="-0.011639 0.034477 0.027201" rpy="0 0 0"/>
|
||||
<parent link="J1_1"/>
|
||||
<child link="J2_1"/>
|
||||
<axis xyz="1.0 0.0 -0.0"/>
|
||||
<limit upper="1.308997" lower="-2.007129" effort="100" velocity="100"/>
|
||||
</joint>
|
||||
|
||||
<joint name="Joint3" type="revolute">
|
||||
<origin xyz="0.03535 0.0 0.158" rpy="0 0 0"/>
|
||||
<parent link="J2_1"/>
|
||||
<child link="J3_1"/>
|
||||
<axis xyz="-1.0 0.0 -0.0"/>
|
||||
<limit upper="1.570796" lower="-1.047198" effort="100" velocity="100"/>
|
||||
</joint>
|
||||
|
||||
<joint name="Joint4" type="revolute">
|
||||
<origin xyz="-0.013162 0.0041 0.062466" rpy="0 0 0"/>
|
||||
<parent link="J3_1"/>
|
||||
<child link="J4_1"/>
|
||||
<axis xyz="0.0 1.0 -0.0"/>
|
||||
<limit upper="3.141593" lower="-3.141593" effort="100" velocity="100"/>
|
||||
</joint>
|
||||
|
||||
<joint name="Joint5" type="revolute">
|
||||
<origin xyz="0.0213 0.110001 0.0" rpy="0 0 0"/>
|
||||
<parent link="J4_1"/>
|
||||
<child link="J5_1"/>
|
||||
<axis xyz="-1.0 -0.0 -0.0"/>
|
||||
<limit upper="2.094395" lower="-1.919862" effort="100" velocity="100"/>
|
||||
</joint>
|
||||
|
||||
<joint name="Joint6" type="continuous">
|
||||
<origin xyz="-0.019822 0.1195 -0.001225" rpy="0 0 0"/>
|
||||
<parent link="J5_1"/>
|
||||
<child link="J6_1"/>
|
||||
<axis xyz="0.0 -1.0 0.0"/>
|
||||
</joint>
|
||||
|
||||
<joint name="camera" type="fixed">
|
||||
<origin xyz="-0.019988 0.091197 0.024883" rpy="0 0 0"/>
|
||||
<parent link="J5_1"/>
|
||||
<child link="camera"/>
|
||||
<axis xyz="1.0 -0.0 0.0"/>
|
||||
<limit upper="0.0" lower="0.0" effort="100" velocity="100"/>
|
||||
</joint>
|
||||
|
||||
</robot>
|
||||
@@ -0,0 +1,73 @@
|
||||
###############################################
|
||||
# Modify all parameters related to servoing here
|
||||
###############################################
|
||||
# adapt to dummy2 by Muzhxiaowen, check out the details on bilibili.com
|
||||
|
||||
use_gazebo: false # Whether the robot is started in a Gazebo simulation environment
|
||||
|
||||
## Properties of incoming commands
|
||||
command_in_type: "unitless" # "unitless"> in the range [-1:1], as if from joystick. "speed_units"> cmds are in m/s and rad/s
|
||||
scale:
|
||||
# Scale parameters are only used if command_in_type=="unitless"
|
||||
linear: 0.4 # Max linear velocity. Unit is [m/s]. Only used for Cartesian commands.
|
||||
rotational: 0.8 # Max angular velocity. Unit is [rad/s]. Only used for Cartesian commands.
|
||||
# Max joint angular/linear velocity. Only used for joint commands on joint_command_in_topic.
|
||||
joint: 0.5
|
||||
|
||||
# Optionally override Servo's internal velocity scaling when near singularity or collision (0.0 = use internal velocity scaling)
|
||||
# override_velocity_scaling_factor = 0.0 # valid range [0.0:1.0]
|
||||
|
||||
## Properties of outgoing commands
|
||||
publish_period: 0.034 # 1/Nominal publish rate [seconds]
|
||||
low_latency_mode: false # Set this to true to publish as soon as an incoming Twist command is received (publish_period is ignored)
|
||||
|
||||
# What type of topic does your robot driver expect?
|
||||
# Currently supported are std_msgs/Float64MultiArray or trajectory_msgs/JointTrajectory
|
||||
command_out_type: trajectory_msgs/JointTrajectory
|
||||
|
||||
# What to publish? Can save some bandwidth as most robots only require positions or velocities
|
||||
publish_joint_positions: true
|
||||
publish_joint_velocities: true
|
||||
publish_joint_accelerations: false
|
||||
|
||||
## Plugins for smoothing outgoing commands
|
||||
smoothing_filter_plugin_name: "online_signal_smoothing::ButterworthFilterPlugin"
|
||||
|
||||
# If is_primary_planning_scene_monitor is set to true, the Servo server's PlanningScene advertises the /get_planning_scene service,
|
||||
# which other nodes can use as a source for information about the planning environment.
|
||||
# NOTE: If a different node in your system is responsible for the "primary" planning scene instance (e.g. the MoveGroup node),
|
||||
# then is_primary_planning_scene_monitor needs to be set to false.
|
||||
is_primary_planning_scene_monitor: true
|
||||
|
||||
## MoveIt properties
|
||||
move_group_name: dummy2_arm # Often 'manipulator' or 'arm'
|
||||
planning_frame: base_link # The MoveIt planning frame. Often 'base_link' or 'world'
|
||||
|
||||
## Other frames
|
||||
ee_frame_name: J6_1 # The name of the end effector link, used to return the EE pose
|
||||
robot_link_command_frame: base_link # commands must be given in the frame of a robot link. Usually either the base or end effector
|
||||
|
||||
## Stopping behaviour
|
||||
incoming_command_timeout: 0.1 # Stop servoing if X seconds elapse without a new command
|
||||
# If 0, republish commands forever even if the robot is stationary. Otherwise, specify num. to publish.
|
||||
# Important because ROS may drop some messages and we need the robot to halt reliably.
|
||||
num_outgoing_halt_msgs_to_publish: 4
|
||||
|
||||
## Configure handling of singularities and joint limits
|
||||
lower_singularity_threshold: 170.0 # Start decelerating when the condition number hits this (close to singularity)
|
||||
hard_stop_singularity_threshold: 3000.0 # Stop when the condition number hits this
|
||||
joint_limit_margin: 0.1 # added as a buffer to joint limits [radians]. If moving quickly, make this larger.
|
||||
leaving_singularity_threshold_multiplier: 2.0 # Multiply the hard stop limit by this when leaving singularity (see https://github.com/ros-planning/moveit2/pull/620)
|
||||
|
||||
## Topic names
|
||||
cartesian_command_in_topic: ~/delta_twist_cmds # Topic for incoming Cartesian twist commands
|
||||
joint_command_in_topic: ~/delta_joint_cmds # Topic for incoming joint angle commands
|
||||
joint_topic: /joint_states
|
||||
status_topic: ~/status # Publish status to this topic
|
||||
command_out_topic: /dummy2_arm_controller/joint_trajectory # Publish outgoing commands here
|
||||
|
||||
## Collision checking for the entire robot body
|
||||
check_collisions: true # Check collisions?
|
||||
collision_check_rate: 10.0 # [Hz] Collision-checking can easily bog down a CPU if done too often.
|
||||
self_collision_proximity_threshold: 0.001 # Start decelerating when a self-collision is this far [m]
|
||||
scene_collision_proximity_threshold: 0.002 # Start decelerating when a scene collision is this far [m]
|
||||
@@ -0,0 +1,9 @@
|
||||
# Default initial positions for dummy2's ros2_control fake system
|
||||
|
||||
initial_positions:
|
||||
Joint1: 0
|
||||
Joint2: 0
|
||||
Joint3: 0
|
||||
Joint4: 0
|
||||
Joint5: 0
|
||||
Joint6: 0
|
||||
@@ -0,0 +1,40 @@
|
||||
# joint_limits.yaml allows the dynamics properties specified in the URDF to be overwritten or augmented as needed
|
||||
|
||||
# For beginners, we downscale velocity and acceleration limits.
|
||||
# You can always specify higher scaling factors (<= 1.0) in your motion requests. # Increase the values below to 1.0 to always move at maximum speed.
|
||||
default_velocity_scaling_factor: 0.1
|
||||
default_acceleration_scaling_factor: 0.1
|
||||
|
||||
# Specific joint properties can be changed with the keys [max_position, min_position, max_velocity, max_acceleration]
|
||||
# Joint limits can be turned off with [has_velocity_limits, has_acceleration_limits]
|
||||
joint_limits:
|
||||
joint_1:
|
||||
has_velocity_limits: true
|
||||
max_velocity: 2.0
|
||||
has_acceleration_limits: false
|
||||
max_acceleration: 0
|
||||
joint_2:
|
||||
has_velocity_limits: true
|
||||
max_velocity: 2.0
|
||||
has_acceleration_limits: false
|
||||
max_acceleration: 0
|
||||
joint_3:
|
||||
has_velocity_limits: true
|
||||
max_velocity: 2.0
|
||||
has_acceleration_limits: false
|
||||
max_acceleration: 0
|
||||
joint_4:
|
||||
has_velocity_limits: true
|
||||
max_velocity: 2.0
|
||||
has_acceleration_limits: false
|
||||
max_acceleration: 0
|
||||
joint_5:
|
||||
has_velocity_limits: true
|
||||
max_velocity: 2.0
|
||||
has_acceleration_limits: false
|
||||
max_acceleration: 0
|
||||
joint_6:
|
||||
has_velocity_limits: true
|
||||
max_velocity: 2.0
|
||||
has_acceleration_limits: false
|
||||
max_acceleration: 0
|
||||
@@ -0,0 +1,4 @@
|
||||
dummy2_arm:
|
||||
kinematics_solver: kdl_kinematics_plugin/KDLKinematicsPlugin
|
||||
kinematics_solver_search_resolution: 0.0050000000000000001
|
||||
kinematics_solver_timeout: 0.5
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
<xacro:macro name="dummy2_ros2_control" params="name initial_positions_file">
|
||||
<xacro:property name="initial_positions" value="${load_yaml(initial_positions_file)['initial_positions']}"/>
|
||||
|
||||
<ros2_control name="${name}" type="system">
|
||||
<hardware>
|
||||
<!-- By default, set up controllers for simulation. This won't work on real hardware -->
|
||||
<!-- <plugin>mock_components/GenericSystem</plugin> -->
|
||||
<plugin>dummy2_hardware/Dummy2Hardware</plugin>
|
||||
</hardware>
|
||||
<joint name="Joint1">
|
||||
<command_interface name="position"/>
|
||||
<state_interface name="position">
|
||||
<param name="initial_value">${initial_positions['Joint1']}</param>
|
||||
</state_interface>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
<joint name="Joint2">
|
||||
<command_interface name="position"/>
|
||||
<state_interface name="position">
|
||||
<param name="initial_value">${initial_positions['Joint2']}</param>
|
||||
</state_interface>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
<joint name="Joint3">
|
||||
<command_interface name="position"/>
|
||||
<state_interface name="position">
|
||||
<param name="initial_value">${initial_positions['Joint3']}</param>
|
||||
</state_interface>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
<joint name="Joint4">
|
||||
<command_interface name="position"/>
|
||||
<state_interface name="position">
|
||||
<param name="initial_value">${initial_positions['Joint4']}</param>
|
||||
</state_interface>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
<joint name="Joint5">
|
||||
<command_interface name="position"/>
|
||||
<state_interface name="position">
|
||||
<param name="initial_value">${initial_positions['Joint5']}</param>
|
||||
</state_interface>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
<joint name="Joint6">
|
||||
<command_interface name="position"/>
|
||||
<state_interface name="position">
|
||||
<param name="initial_value">${initial_positions['Joint6']}</param>
|
||||
</state_interface>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
|
||||
</ros2_control>
|
||||
</xacro:macro>
|
||||
</robot>
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"arm": {
|
||||
"joint_names": [
|
||||
"joint_1",
|
||||
"joint_2",
|
||||
"joint_3",
|
||||
"joint_4",
|
||||
"joint_5",
|
||||
"joint_6"
|
||||
],
|
||||
"base_link_name": "base_link",
|
||||
"end_effector_name": "tool_link"
|
||||
}
|
||||
}
|
||||
51
unilabos/device_mesh/devices/dummy2_robot/config/moveit.rviz
Normal file
51
unilabos/device_mesh/devices/dummy2_robot/config/moveit.rviz
Normal file
@@ -0,0 +1,51 @@
|
||||
Panels:
|
||||
- Class: rviz_common/Displays
|
||||
Name: Displays
|
||||
Property Tree Widget:
|
||||
Expanded:
|
||||
- /MotionPlanning1
|
||||
- Class: rviz_common/Help
|
||||
Name: Help
|
||||
- Class: rviz_common/Views
|
||||
Name: Views
|
||||
Visualization Manager:
|
||||
Displays:
|
||||
- Class: rviz_default_plugins/Grid
|
||||
Name: Grid
|
||||
Value: true
|
||||
- Class: moveit_rviz_plugin/MotionPlanning
|
||||
Name: MotionPlanning
|
||||
Planned Path:
|
||||
Loop Animation: true
|
||||
State Display Time: 0.05 s
|
||||
Trajectory Topic: display_planned_path
|
||||
Planning Scene Topic: monitored_planning_scene
|
||||
Robot Description: robot_description
|
||||
Scene Geometry:
|
||||
Scene Alpha: 1
|
||||
Scene Robot:
|
||||
Robot Alpha: 0.5
|
||||
Value: true
|
||||
Global Options:
|
||||
Fixed Frame: base_link
|
||||
Tools:
|
||||
- Class: rviz_default_plugins/Interact
|
||||
- Class: rviz_default_plugins/MoveCamera
|
||||
- Class: rviz_default_plugins/Select
|
||||
Value: true
|
||||
Views:
|
||||
Current:
|
||||
Class: rviz_default_plugins/Orbit
|
||||
Distance: 2.0
|
||||
Focal Point:
|
||||
X: -0.1
|
||||
Y: 0.25
|
||||
Z: 0.30
|
||||
Name: Current View
|
||||
Pitch: 0.5
|
||||
Target Frame: base_link
|
||||
Yaw: -0.623
|
||||
Window Geometry:
|
||||
Height: 975
|
||||
QMainWindow State: 000000ff00000000fd0000000100000000000002b400000375fc0200000005fb00000044004d006f00740069006f006e0050006c0061006e006e0069006e00670020002d0020005400720061006a006500630074006f0072007900200053006c00690064006500720000000000ffffffff0000004100fffffffb000000100044006900730070006c006100790073010000003d00000123000000c900fffffffb0000001c004d006f00740069006f006e0050006c0061006e006e0069006e00670100000166000001910000018800fffffffb0000000800480065006c0070000000029a0000006e0000006e00fffffffb0000000a0056006900650077007301000002fd000000b5000000a400ffffff000001f60000037500000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
|
||||
Width: 1200
|
||||
@@ -0,0 +1,21 @@
|
||||
# MoveIt uses this configuration for controller management
|
||||
|
||||
moveit_controller_manager: moveit_simple_controller_manager/MoveItSimpleControllerManager
|
||||
|
||||
moveit_simple_controller_manager:
|
||||
controller_names:
|
||||
- dummy2_arm_controller
|
||||
|
||||
dummy2_arm_controller:
|
||||
type: FollowJointTrajectory
|
||||
action_ns: follow_joint_trajectory
|
||||
default: true
|
||||
joints:
|
||||
- Joint1
|
||||
- Joint2
|
||||
- Joint3
|
||||
- Joint4
|
||||
- Joint5
|
||||
- Joint6
|
||||
action_ns: follow_joint_trajectory
|
||||
default: true
|
||||
@@ -0,0 +1,39 @@
|
||||
dummy2_robot:
|
||||
# Physical properties for each link
|
||||
link_masses:
|
||||
base_link: 5.0
|
||||
link_1: 3.0
|
||||
link_2: 2.5
|
||||
link_3: 2.0
|
||||
link_4: 1.5
|
||||
link_5: 1.0
|
||||
link_6: 0.5
|
||||
|
||||
# Center of mass for each link (relative to joint frame)
|
||||
link_com:
|
||||
base_link: [0.0, 0.0, 0.05]
|
||||
link_1: [0.0, 0.0, 0.05]
|
||||
link_2: [0.1, 0.0, 0.0]
|
||||
link_3: [0.08, 0.0, 0.0]
|
||||
link_4: [0.0, 0.0, 0.05]
|
||||
link_5: [0.0, 0.0, 0.03]
|
||||
link_6: [0.0, 0.0, 0.02]
|
||||
|
||||
# Moment of inertia matrices
|
||||
link_inertias:
|
||||
base_link: [0.02, 0.0, 0.0, 0.02, 0.0, 0.02]
|
||||
link_1: [0.01, 0.0, 0.0, 0.01, 0.0, 0.01]
|
||||
link_2: [0.008, 0.0, 0.0, 0.008, 0.0, 0.008]
|
||||
link_3: [0.006, 0.0, 0.0, 0.006, 0.0, 0.006]
|
||||
link_4: [0.004, 0.0, 0.0, 0.004, 0.0, 0.004]
|
||||
link_5: [0.002, 0.0, 0.0, 0.002, 0.0, 0.002]
|
||||
link_6: [0.001, 0.0, 0.0, 0.001, 0.0, 0.001]
|
||||
|
||||
# Motor specifications
|
||||
motor_specs:
|
||||
joint_1: { max_torque: 150.0, max_speed: 2.0, gear_ratio: 100 }
|
||||
joint_2: { max_torque: 150.0, max_speed: 2.0, gear_ratio: 100 }
|
||||
joint_3: { max_torque: 150.0, max_speed: 2.0, gear_ratio: 100 }
|
||||
joint_4: { max_torque: 50.0, max_speed: 2.0, gear_ratio: 50 }
|
||||
joint_5: { max_torque: 50.0, max_speed: 2.0, gear_ratio: 50 }
|
||||
joint_6: { max_torque: 25.0, max_speed: 2.0, gear_ratio: 25 }
|
||||
@@ -0,0 +1,6 @@
|
||||
# Limits for the Pilz planner
|
||||
cartesian_limits:
|
||||
max_trans_vel: 1.0
|
||||
max_trans_acc: 2.25
|
||||
max_trans_dec: -5.0
|
||||
max_rot_vel: 1.57
|
||||
@@ -0,0 +1,26 @@
|
||||
# This config file is used by ros2_control
|
||||
controller_manager:
|
||||
ros__parameters:
|
||||
update_rate: 100 # Hz
|
||||
|
||||
dummy2_arm_controller:
|
||||
type: joint_trajectory_controller/JointTrajectoryController
|
||||
|
||||
|
||||
joint_state_broadcaster:
|
||||
type: joint_state_broadcaster/JointStateBroadcaster
|
||||
|
||||
dummy2_arm_controller:
|
||||
ros__parameters:
|
||||
joints:
|
||||
- Joint1
|
||||
- Joint2
|
||||
- Joint3
|
||||
- Joint4
|
||||
- Joint5
|
||||
- Joint6
|
||||
command_interfaces:
|
||||
- position
|
||||
state_interfaces:
|
||||
- position
|
||||
- velocity
|
||||
@@ -0,0 +1,35 @@
|
||||
dummy2_robot:
|
||||
# Visual appearance settings
|
||||
materials:
|
||||
base_material:
|
||||
color: [0.8, 0.8, 0.8, 1.0] # Light gray
|
||||
metallic: 0.1
|
||||
roughness: 0.3
|
||||
|
||||
link_material:
|
||||
color: [0.2, 0.2, 0.8, 1.0] # Blue
|
||||
metallic: 0.3
|
||||
roughness: 0.2
|
||||
|
||||
joint_material:
|
||||
color: [0.6, 0.6, 0.6, 1.0] # Dark gray
|
||||
metallic: 0.5
|
||||
roughness: 0.1
|
||||
|
||||
camera_material:
|
||||
color: [0.1, 0.1, 0.1, 1.0] # Black
|
||||
metallic: 0.0
|
||||
roughness: 0.8
|
||||
|
||||
# Mesh scaling factors
|
||||
mesh_scale: [0.001, 0.001, 0.001] # Convert mm to m
|
||||
|
||||
# Collision geometry simplification
|
||||
collision_geometries:
|
||||
base_link: "cylinder" # radius: 0.08, height: 0.1
|
||||
link_1: "cylinder" # radius: 0.05, height: 0.15
|
||||
link_2: "box" # size: [0.2, 0.08, 0.08]
|
||||
link_3: "box" # size: [0.15, 0.06, 0.06]
|
||||
link_4: "cylinder" # radius: 0.03, height: 0.1
|
||||
link_5: "cylinder" # radius: 0.025, height: 0.06
|
||||
link_6: "cylinder" # radius: 0.02, height: 0.04
|
||||
37
unilabos/device_mesh/devices/dummy2_robot/joint_limit.yaml
Normal file
37
unilabos/device_mesh/devices/dummy2_robot/joint_limit.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
joint_limits:
|
||||
|
||||
joint_1:
|
||||
effort: 150
|
||||
velocity: 2.0
|
||||
lower: !degrees -180
|
||||
upper: !degrees 180
|
||||
|
||||
joint_2:
|
||||
effort: 150
|
||||
velocity: 2.0
|
||||
lower: !degrees -90
|
||||
upper: !degrees 90
|
||||
|
||||
joint_3:
|
||||
effort: 150
|
||||
velocity: 2.0
|
||||
lower: !degrees -90
|
||||
upper: !degrees 90
|
||||
|
||||
joint_4:
|
||||
effort: 50
|
||||
velocity: 2.0
|
||||
lower: !degrees -180
|
||||
upper: !degrees 180
|
||||
|
||||
joint_5:
|
||||
effort: 50
|
||||
velocity: 2.0
|
||||
lower: !degrees -90
|
||||
upper: !degrees 90
|
||||
|
||||
joint_6:
|
||||
effort: 25
|
||||
velocity: 2.0
|
||||
lower: !degrees -180
|
||||
upper: !degrees 180
|
||||
314
unilabos/device_mesh/devices/dummy2_robot/macro_device.xacro
Normal file
314
unilabos/device_mesh/devices/dummy2_robot/macro_device.xacro
Normal file
@@ -0,0 +1,314 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot xmlns:xacro="http://wiki.ros.org/xacro" name="dummy2_robot">
|
||||
<xacro:macro name="dummy2_robot" params="mesh_path:='' parent_link:='' station_name:='' device_name:='' fake_dev:='true' x:=0 y:=0 z:=0 rx:=0 ry:=0 r:=0">
|
||||
<!-- Read .yaml files from disk, load content into properties -->
|
||||
<xacro:property name= "joint_limit_parameters" value="${xacro.load_yaml(mesh_path + '/devices/dummy2_robot/joint_limit.yaml')}"/>
|
||||
|
||||
<!-- Extract subsections from yaml dictionaries -->
|
||||
<xacro:property name= "sec_limits" value="${joint_limit_parameters['joint_limits']}"/>
|
||||
|
||||
<!-- robot name parameter -->
|
||||
<xacro:arg name="name" default="dummy2"/>
|
||||
|
||||
<!-- parameters -->
|
||||
<xacro:arg name="tf_prefix" default="${station_name}${device_name}" />
|
||||
<xacro:arg name="joint_limit_params" default="${mesh_path}/devices/dummy2_robot/config/joint_limits.yaml"/>
|
||||
<xacro:arg name="kinematics_params" default="${mesh_path}/devices/dummy2_robot/config/default_kinematics.yaml"/>
|
||||
<xacro:arg name="physical_params" default="${mesh_path}/devices/dummy2_robot/config/physical_parameters.yaml"/>
|
||||
<xacro:arg name="visual_params" default="${mesh_path}/devices/dummy2_robot/config/visual_parameters.yaml"/>
|
||||
<xacro:arg name="transmission_hw_interface" default=""/>
|
||||
<xacro:arg name="safety_limits" default="false"/>
|
||||
<xacro:arg name="safety_pos_margin" default="0.15"/>
|
||||
<xacro:arg name="safety_k_position" default="20"/>
|
||||
|
||||
<!-- CAN2ETH related parameters -->
|
||||
<xacro:arg name="can2eth_ip" default="192.168.8.88" />
|
||||
<xacro:arg name="can2eth_port" default="8080" />
|
||||
|
||||
<!-- JOINTS LIMIT PARAMETERS -->
|
||||
<xacro:property name="limit_joint_1" value="${sec_limits['joint_1']}" />
|
||||
<xacro:property name="limit_joint_2" value="${sec_limits['joint_2']}" />
|
||||
<xacro:property name="limit_joint_3" value="${sec_limits['joint_3']}" />
|
||||
<xacro:property name="limit_joint_4" value="${sec_limits['joint_4']}" />
|
||||
<xacro:property name="limit_joint_5" value="${sec_limits['joint_5']}" />
|
||||
<xacro:property name="limit_joint_6" value="${sec_limits['joint_6']}" />
|
||||
|
||||
<!-- create link fixed to parent -->
|
||||
<joint name="${station_name}${device_name}base_link_joint" type="fixed">
|
||||
<origin xyz="${x} ${y} ${z}" rpy="${rx} ${ry} ${r}" />
|
||||
<parent link="${parent_link}"/>
|
||||
<child link="${station_name}${device_name}base_link"/>
|
||||
<axis xyz="0 0 0"/>
|
||||
</joint>
|
||||
|
||||
<!-- base_link -->
|
||||
<link name="${station_name}${device_name}base_link">
|
||||
<inertial>
|
||||
<origin xyz="0 0 0.05" rpy="0 0 0"/>
|
||||
<mass value="5.0"/>
|
||||
<inertia ixx="0.02" ixy="0" ixz="0" iyy="0.02" iyz="0" izz="0.02"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://${mesh_path}/devices/dummy2_robot/meshes/base_link.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="dummy2_base_material">
|
||||
<color rgba="0.8 0.8 0.8 1.0"/>
|
||||
</material>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0.05" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder radius="0.08" length="0.1"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<!-- Joint 1 -->
|
||||
<joint name="${station_name}${device_name}joint_1" type="revolute">
|
||||
<origin xyz="0 0 0.1" rpy="0 0 0"/>
|
||||
<parent link="${station_name}${device_name}base_link"/>
|
||||
<child link="${station_name}${device_name}link_1"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit lower="${limit_joint_1['lower']}" upper="${limit_joint_1['upper']}" effort="${limit_joint_1['effort']}" velocity="${limit_joint_1['velocity']}"/>
|
||||
<dynamics damping="0.0" friction="0.0"/>
|
||||
</joint>
|
||||
|
||||
<link name="${station_name}${device_name}link_1">
|
||||
<inertial>
|
||||
<origin xyz="0 0 0.05" rpy="0 0 0"/>
|
||||
<mass value="3.0"/>
|
||||
<inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.01"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://${mesh_path}/devices/dummy2_robot/meshes/J1_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="dummy2_link_material">
|
||||
<color rgba="0.2 0.2 0.8 1.0"/>
|
||||
</material>
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
<!-- Joint 2 -->
|
||||
<joint name="${station_name}${device_name}joint_2" type="revolute">
|
||||
<origin xyz="0 0 0.15" rpy="0 0 0"/>
|
||||
<parent link="${station_name}${device_name}link_1"/>
|
||||
<child link="${station_name}${device_name}link_2"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<limit lower="${limit_joint_2['lower']}" upper="${limit_joint_2['upper']}" effort="${limit_joint_2['effort']}" velocity="${limit_joint_2['velocity']}"/>
|
||||
<dynamics damping="0.0" friction="0.0"/>
|
||||
</joint>
|
||||
|
||||
<link name="${station_name}${device_name}link_2">
|
||||
<inertial>
|
||||
<origin xyz="0.1 0 0" rpy="0 0 0"/>
|
||||
<mass value="2.5"/>
|
||||
<inertia ixx="0.008" ixy="0" ixz="0" iyy="0.008" iyz="0" izz="0.008"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://${mesh_path}/devices/dummy2_robot/meshes/J2_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="dummy2_link_material"/>
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
<!-- Joint 3 -->
|
||||
<joint name="${station_name}${device_name}joint_3" type="revolute">
|
||||
<origin xyz="0.2 0 0" rpy="0 0 0"/>
|
||||
<parent link="${station_name}${device_name}link_2"/>
|
||||
<child link="${station_name}${device_name}link_3"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<limit lower="${limit_joint_3['lower']}" upper="${limit_joint_3['upper']}" effort="${limit_joint_3['effort']}" velocity="${limit_joint_3['velocity']}"/>
|
||||
<dynamics damping="0.0" friction="0.0"/>
|
||||
</joint>
|
||||
|
||||
<link name="${station_name}${device_name}link_3">
|
||||
<inertial>
|
||||
<origin xyz="0.08 0 0" rpy="0 0 0"/>
|
||||
<mass value="2.0"/>
|
||||
<inertia ixx="0.006" ixy="0" ixz="0" iyy="0.006" iyz="0" izz="0.006"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://${mesh_path}/devices/dummy2_robot/meshes/J3_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="dummy2_link_material"/>
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
<!-- Joint 4 -->
|
||||
<joint name="${station_name}${device_name}joint_4" type="revolute">
|
||||
<origin xyz="0.15 0 0" rpy="0 0 0"/>
|
||||
<parent link="${station_name}${device_name}link_3"/>
|
||||
<child link="${station_name}${device_name}link_4"/>
|
||||
<axis xyz="1 0 0"/>
|
||||
<limit lower="${limit_joint_4['lower']}" upper="${limit_joint_4['upper']}" effort="${limit_joint_4['effort']}" velocity="${limit_joint_4['velocity']}"/>
|
||||
<dynamics damping="0.0" friction="0.0"/>
|
||||
</joint>
|
||||
|
||||
<link name="${station_name}${device_name}link_4">
|
||||
<inertial>
|
||||
<origin xyz="0 0 0.05" rpy="0 0 0"/>
|
||||
<mass value="1.5"/>
|
||||
<inertia ixx="0.004" ixy="0" ixz="0" iyy="0.004" iyz="0" izz="0.004"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://${mesh_path}/devices/dummy2_robot/meshes/J4_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="dummy2_link_material"/>
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
<!-- Joint 5 -->
|
||||
<joint name="${station_name}${device_name}joint_5" type="revolute">
|
||||
<origin xyz="0 0 0.1" rpy="0 0 0"/>
|
||||
<parent link="${station_name}${device_name}link_4"/>
|
||||
<child link="${station_name}${device_name}link_5"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<limit lower="${limit_joint_5['lower']}" upper="${limit_joint_5['upper']}" effort="${limit_joint_5['effort']}" velocity="${limit_joint_5['velocity']}"/>
|
||||
<dynamics damping="0.0" friction="0.0"/>
|
||||
</joint>
|
||||
|
||||
<link name="${station_name}${device_name}link_5">
|
||||
<inertial>
|
||||
<origin xyz="0 0 0.03" rpy="0 0 0"/>
|
||||
<mass value="1.0"/>
|
||||
<inertia ixx="0.002" ixy="0" ixz="0" iyy="0.002" iyz="0" izz="0.002"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://${mesh_path}/devices/dummy2_robot/meshes/J5_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="dummy2_link_material"/>
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
<!-- Joint 6 (end effector) -->
|
||||
<joint name="${station_name}${device_name}joint_6" type="revolute">
|
||||
<origin xyz="0 0 0.06" rpy="0 0 0"/>
|
||||
<parent link="${station_name}${device_name}link_5"/>
|
||||
<child link="${station_name}${device_name}link_6"/>
|
||||
<axis xyz="1 0 0"/>
|
||||
<limit lower="${limit_joint_6['lower']}" upper="${limit_joint_6['upper']}" effort="${limit_joint_6['effort']}" velocity="${limit_joint_6['velocity']}"/>
|
||||
<dynamics damping="0.0" friction="0.0"/>
|
||||
</joint>
|
||||
|
||||
<link name="${station_name}${device_name}link_6">
|
||||
<inertial>
|
||||
<origin xyz="0 0 0.02" rpy="0 0 0"/>
|
||||
<mass value="0.5"/>
|
||||
<inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://${mesh_path}/devices/dummy2_robot/meshes/J6_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="dummy2_link_material"/>
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
<!-- Tool center point -->
|
||||
<joint name="${station_name}${device_name}tool_joint" type="fixed">
|
||||
<origin xyz="0 0 0.04" rpy="0 0 0"/>
|
||||
<parent link="${station_name}${device_name}link_6"/>
|
||||
<child link="${station_name}${device_name}tool_link"/>
|
||||
</joint>
|
||||
|
||||
<link name="${station_name}${device_name}tool_link"/>
|
||||
|
||||
<!-- Camera link (if needed) -->
|
||||
<joint name="${station_name}${device_name}camera_joint" type="fixed">
|
||||
<origin xyz="0.05 0 0.02" rpy="0 0 0"/>
|
||||
<parent link="${station_name}${device_name}link_6"/>
|
||||
<child link="${station_name}${device_name}camera_link"/>
|
||||
</joint>
|
||||
|
||||
<link name="${station_name}${device_name}camera_link">
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://${mesh_path}/devices/dummy2_robot/meshes/camera_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="dummy2_camera_material">
|
||||
<color rgba="0.1 0.1 0.1 1.0"/>
|
||||
</material>
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
<!-- ROS2 control (if needed) -->
|
||||
<xacro:unless value="${fake_dev}">
|
||||
<ros2_control name="${station_name}${device_name}ros2_control" type="system">
|
||||
<hardware>
|
||||
<plugin>dummy2_hardware_interface/Dummy2HardwareInterface</plugin>
|
||||
<param name="can2eth_ip">$(arg can2eth_ip)</param>
|
||||
<param name="can2eth_port">$(arg can2eth_port)</param>
|
||||
</hardware>
|
||||
|
||||
<joint name="${station_name}${device_name}joint_1">
|
||||
<command_interface name="position">
|
||||
<param name="min">${limit_joint_1['lower']}</param>
|
||||
<param name="max">${limit_joint_1['upper']}</param>
|
||||
</command_interface>
|
||||
<state_interface name="position"/>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
|
||||
<joint name="${station_name}${device_name}joint_2">
|
||||
<command_interface name="position">
|
||||
<param name="min">${limit_joint_2['lower']}</param>
|
||||
<param name="max">${limit_joint_2['upper']}</param>
|
||||
</command_interface>
|
||||
<state_interface name="position"/>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
|
||||
<joint name="${station_name}${device_name}joint_3">
|
||||
<command_interface name="position">
|
||||
<param name="min">${limit_joint_3['lower']}</param>
|
||||
<param name="max">${limit_joint_3['upper']}</param>
|
||||
</command_interface>
|
||||
<state_interface name="position"/>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
|
||||
<joint name="${station_name}${device_name}joint_4">
|
||||
<command_interface name="position">
|
||||
<param name="min">${limit_joint_4['lower']}</param>
|
||||
<param name="max">${limit_joint_4['upper']}</param>
|
||||
</command_interface>
|
||||
<state_interface name="position"/>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
|
||||
<joint name="${station_name}${device_name}joint_5">
|
||||
<command_interface name="position">
|
||||
<param name="min">${limit_joint_5['lower']}</param>
|
||||
<param name="max">${limit_joint_5['upper']}</param>
|
||||
</command_interface>
|
||||
<state_interface name="position"/>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
|
||||
<joint name="${station_name}${device_name}joint_6">
|
||||
<command_interface name="position">
|
||||
<param name="min">${limit_joint_6['lower']}</param>
|
||||
<param name="max">${limit_joint_6['upper']}</param>
|
||||
</command_interface>
|
||||
<state_interface name="position"/>
|
||||
<state_interface name="velocity"/>
|
||||
</joint>
|
||||
</ros2_control>
|
||||
</xacro:unless>
|
||||
|
||||
</xacro:macro>
|
||||
</robot>
|
||||
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/J1_1.stl
Normal file
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/J1_1.stl
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/J2_1.stl
Normal file
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/J2_1.stl
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/J3_1.stl
Normal file
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/J3_1.stl
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/J4_1.stl
Normal file
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/J4_1.stl
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/J5_1.stl
Normal file
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/J5_1.stl
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/J6_1.stl
Normal file
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/J6_1.stl
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/base_link.stl
Normal file
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/base_link.stl
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/camera_1.stl
Normal file
BIN
unilabos/device_mesh/devices/dummy2_robot/meshes/camera_1.stl
Normal file
Binary file not shown.
237
unilabos/device_mesh/devices/dummy2_robot/meshes/dummy2.xacro
Normal file
237
unilabos/device_mesh/devices/dummy2_robot/meshes/dummy2.xacro
Normal file
@@ -0,0 +1,237 @@
|
||||
<?xml version="1.0" ?>
|
||||
<robot name="dummy2" xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
|
||||
<xacro:include filename="$(find dummy2_description)/urdf/materials.xacro" />
|
||||
<xacro:include filename="$(find dummy2_description)/urdf/dummy2.trans" />
|
||||
<xacro:include filename="$(find dummy2_description)/urdf/dummy2.gazebo" />
|
||||
<link name="world" />
|
||||
<joint name="world_joint" type="fixed">
|
||||
<parent link="world" />
|
||||
<child link = "base_link" />
|
||||
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
|
||||
</joint>
|
||||
|
||||
<link name="base_link">
|
||||
<inertial>
|
||||
<origin xyz="0.00010022425916431473 -6.186605493937309e-05 0.05493640543484716" rpy="0 0 0"/>
|
||||
<mass value="1.2152141810431654"/>
|
||||
<inertia ixx="0.002105" iyy="0.002245" izz="0.002436" ixy="-0.0" iyz="-1.1e-05" ixz="0.0"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/base_link.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/base_link.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="J1_1">
|
||||
<inertial>
|
||||
<origin xyz="-0.00617659688932347 0.007029599744830012 0.012866826083045027" rpy="0 0 0"/>
|
||||
<mass value="0.1332774369186824"/>
|
||||
<inertia ixx="6e-05" iyy="5e-05" izz="8.8e-05" ixy="2.1e-05" iyz="-1.4e-05" ixz="8e-06"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="-0.0001 0.000289 -0.097579" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J1_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.0001 0.000289 -0.097579" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J1_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="J2_1">
|
||||
<inertial>
|
||||
<origin xyz="0.019335709221765855 0.0019392793940843159 0.07795928103332703" rpy="0 0 0"/>
|
||||
<mass value="1.9268013917303417"/>
|
||||
<inertia ixx="0.006165" iyy="0.006538" izz="0.00118" ixy="-3e-06" iyz="4.7e-05" ixz="0.0007"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="0.011539 -0.034188 -0.12478" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J2_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0.011539 -0.034188 -0.12478" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J2_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="J3_1">
|
||||
<inertial>
|
||||
<origin xyz="-0.010672101243726572 -0.02723871972304964 0.04876701375652198" rpy="0 0 0"/>
|
||||
<mass value="0.30531962155452225"/>
|
||||
<inertia ixx="0.00029" iyy="0.000238" izz="0.000191" ixy="-1.3e-05" iyz="4.1e-05" ixz="3e-05"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="-0.023811 -0.034188 -0.28278" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J3_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.023811 -0.034188 -0.28278" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J3_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="J4_1">
|
||||
<inertial>
|
||||
<origin xyz="-0.005237398377441591 0.06002028183461833 0.0005891767740203724" rpy="0 0 0"/>
|
||||
<mass value="0.14051172121899885"/>
|
||||
<inertia ixx="0.000245" iyy="7.9e-05" izz="0.00027" ixy="1.6e-05" iyz="-2e-06" ixz="1e-06"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="-0.010649 -0.038288 -0.345246" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J4_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.010649 -0.038288 -0.345246" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J4_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="J5_1">
|
||||
<inertial>
|
||||
<origin xyz="-0.014389813882964664 0.07305218143664277 -0.0009243405950149497" rpy="0 0 0"/>
|
||||
<mass value="0.7783315754227634"/>
|
||||
<inertia ixx="0.000879" iyy="0.000339" izz="0.000964" ixy="0.000146" iyz="1e-06" ixz="-5e-06"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="-0.031949 -0.148289 -0.345246" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J5_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.031949 -0.148289 -0.345246" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J5_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="J6_1">
|
||||
<inertial>
|
||||
<origin xyz="3.967160300787087e-07 0.0004995066702210837 1.4402781733924286e-07" rpy="0 0 0"/>
|
||||
<mass value="0.0020561527568204153"/>
|
||||
<inertia ixx="0.0" iyy="0.0" izz="0.0" ixy="0.0" iyz="0.0" ixz="0.0"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="-0.012127 -0.267789 -0.344021" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J6_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.012127 -0.267789 -0.344021" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/J6_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name="camera">
|
||||
<inertial>
|
||||
<origin xyz="-0.0006059984983273845 0.0005864706438700462 0.04601775357664567" rpy="0 0 0"/>
|
||||
<mass value="0.21961029019655884"/>
|
||||
<inertia ixx="2.9e-05" iyy="0.000206" izz="0.000198" ixy="-0.0" iyz="2e-06" ixz="-0.0"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin xyz="-0.012661 -0.239774 -0.37985" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/camera_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="-0.012661 -0.239774 -0.37985" rpy="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="file://$(find dummy2_description)/meshes/camera_1.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="Joint1" type="revolute">
|
||||
<origin xyz="0.0001 -0.000289 0.097579" rpy="0 0 0"/>
|
||||
<parent link="base_link"/>
|
||||
<child link="J1_1"/>
|
||||
<axis xyz="-0.0 -0.0 1.0"/>
|
||||
<limit upper="3.054326" lower="-3.054326" effort="100" velocity="100"/>
|
||||
</joint>
|
||||
|
||||
<joint name="Joint2" type="revolute">
|
||||
<origin xyz="-0.011639 0.034477 0.027201" rpy="0 0 0"/>
|
||||
<parent link="J1_1"/>
|
||||
<child link="J2_1"/>
|
||||
<axis xyz="1.0 0.0 -0.0"/>
|
||||
<limit upper="1.308997" lower="-2.007129" effort="100" velocity="100"/>
|
||||
</joint>
|
||||
|
||||
<joint name="Joint3" type="revolute">
|
||||
<origin xyz="0.03535 0.0 0.158" rpy="0 0 0"/>
|
||||
<parent link="J2_1"/>
|
||||
<child link="J3_1"/>
|
||||
<axis xyz="-1.0 0.0 -0.0"/>
|
||||
<limit upper="1.570796" lower="-1.047198" effort="100" velocity="100"/>
|
||||
</joint>
|
||||
|
||||
<joint name="Joint4" type="revolute">
|
||||
<origin xyz="-0.013162 0.0041 0.062466" rpy="0 0 0"/>
|
||||
<parent link="J3_1"/>
|
||||
<child link="J4_1"/>
|
||||
<axis xyz="0.0 1.0 -0.0"/>
|
||||
<limit upper="3.141593" lower="-3.141593" effort="100" velocity="100"/>
|
||||
</joint>
|
||||
|
||||
<joint name="Joint5" type="revolute">
|
||||
<origin xyz="0.0213 0.110001 0.0" rpy="0 0 0"/>
|
||||
<parent link="J4_1"/>
|
||||
<child link="J5_1"/>
|
||||
<axis xyz="-1.0 -0.0 -0.0"/>
|
||||
<limit upper="2.094395" lower="-1.919862" effort="100" velocity="100"/>
|
||||
</joint>
|
||||
|
||||
<joint name="Joint6" type="continuous">
|
||||
<origin xyz="-0.019822 0.1195 -0.001225" rpy="0 0 0"/>
|
||||
<parent link="J5_1"/>
|
||||
<child link="J6_1"/>
|
||||
<axis xyz="0.0 -1.0 0.0"/>
|
||||
</joint>
|
||||
|
||||
<joint name="camera" type="fixed">
|
||||
<origin xyz="-0.019988 0.091197 0.024883" rpy="0 0 0"/>
|
||||
<parent link="J5_1"/>
|
||||
<child link="camera"/>
|
||||
<axis xyz="1.0 -0.0 0.0"/>
|
||||
<limit upper="0.0" lower="0.0" effort="100" velocity="100"/>
|
||||
</joint>
|
||||
|
||||
</robot>
|
||||
0
unilabos/devices/liquid_handling/prcxi/__init__.py
Normal file
0
unilabos/devices/liquid_handling/prcxi/__init__.py
Normal file
@@ -3,6 +3,8 @@ import logging
|
||||
import time as time_module
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
from unilabos.compile.utils.vessel_parser import get_vessel
|
||||
|
||||
|
||||
class VirtualFilter:
|
||||
"""Virtual filter device - 完全按照 Filter.action 规范 🌊"""
|
||||
@@ -40,7 +42,6 @@ class VirtualFilter:
|
||||
"progress": 0.0, # Filter.action feedback
|
||||
"current_temp": 25.0, # Filter.action feedback
|
||||
"filtered_volume": 0.0, # Filter.action feedback
|
||||
"current_status": "Ready for filtration", # Filter.action feedback
|
||||
"message": "Ready for filtration"
|
||||
})
|
||||
|
||||
@@ -52,9 +53,7 @@ class VirtualFilter:
|
||||
self.logger.info(f"🧹 清理虚拟过滤器 {self.device_id} 🔚")
|
||||
|
||||
self.data.update({
|
||||
"status": "Offline",
|
||||
"current_status": "System offline",
|
||||
"message": "System offline"
|
||||
"status": "Offline"
|
||||
})
|
||||
|
||||
self.logger.info(f"✅ 过滤器 {self.device_id} 清理完成 💤")
|
||||
@@ -62,8 +61,8 @@ class VirtualFilter:
|
||||
|
||||
async def filter(
|
||||
self,
|
||||
vessel: str,
|
||||
filtrate_vessel: str = "",
|
||||
vessel: dict,
|
||||
filtrate_vessel: dict = {},
|
||||
stir: bool = False,
|
||||
stir_speed: float = 300.0,
|
||||
temp: float = 25.0,
|
||||
@@ -71,7 +70,9 @@ class VirtualFilter:
|
||||
volume: float = 0.0
|
||||
) -> bool:
|
||||
"""Execute filter action - 完全按照 Filter.action 参数 🌊"""
|
||||
|
||||
vessel_id, _ = get_vessel(vessel)
|
||||
filtrate_vessel_id, _ = get_vessel(filtrate_vessel) if filtrate_vessel else (f"{vessel_id}_filtrate", {})
|
||||
|
||||
# 🔧 新增:温度自动调整
|
||||
original_temp = temp
|
||||
if temp == 0.0:
|
||||
@@ -81,7 +82,7 @@ class VirtualFilter:
|
||||
temp = 4.0 # 小于4度自动设置为4度
|
||||
self.logger.info(f"🌡️ 温度自动调整: {original_temp}°C → {temp}°C (最低温度) ❄️")
|
||||
|
||||
self.logger.info(f"🌊 开始过滤操作: {vessel} → {filtrate_vessel} 🚰")
|
||||
self.logger.info(f"🌊 开始过滤操作: {vessel_id} → {filtrate_vessel_id} 🚰")
|
||||
self.logger.info(f" 🌪️ 搅拌: {stir} ({stir_speed} RPM)")
|
||||
self.logger.info(f" 🌡️ 温度: {temp}°C")
|
||||
self.logger.info(f" 💧 体积: {volume}mL")
|
||||
@@ -93,7 +94,6 @@ class VirtualFilter:
|
||||
self.logger.error(f"❌ {error_msg}")
|
||||
self.data.update({
|
||||
"status": f"Error: 温度超出范围 ⚠️",
|
||||
"current_status": f"Error: 温度超出范围 ⚠️",
|
||||
"message": error_msg
|
||||
})
|
||||
return False
|
||||
@@ -103,7 +103,6 @@ class VirtualFilter:
|
||||
self.logger.error(f"❌ {error_msg}")
|
||||
self.data.update({
|
||||
"status": f"Error: 搅拌速度超出范围 ⚠️",
|
||||
"current_status": f"Error: 搅拌速度超出范围 ⚠️",
|
||||
"message": error_msg
|
||||
})
|
||||
return False
|
||||
@@ -112,8 +111,7 @@ class VirtualFilter:
|
||||
error_msg = f"💧 过滤体积 {volume} mL 超出范围 (0-{self._max_volume} mL) ⚠️"
|
||||
self.logger.error(f"❌ {error_msg}")
|
||||
self.data.update({
|
||||
"status": f"Error: 体积超出范围 ⚠️",
|
||||
"current_status": f"Error: 体积超出范围 ⚠️",
|
||||
"status": f"Error",
|
||||
"message": error_msg
|
||||
})
|
||||
return False
|
||||
@@ -123,12 +121,11 @@ class VirtualFilter:
|
||||
self.logger.info(f"🚀 开始过滤 {filter_volume}mL 液体 💧")
|
||||
|
||||
self.data.update({
|
||||
"status": f"🌊 过滤中: {vessel}",
|
||||
"status": f"Running",
|
||||
"current_temp": temp,
|
||||
"filtered_volume": 0.0,
|
||||
"progress": 0.0,
|
||||
"current_status": f"🌊 Filtering {vessel} → {filtrate_vessel}",
|
||||
"message": f"🚀 Starting filtration: {vessel} → {filtrate_vessel}"
|
||||
"message": f"🚀 Starting filtration: {vessel_id} → {filtrate_vessel_id}"
|
||||
})
|
||||
|
||||
try:
|
||||
@@ -164,8 +161,7 @@ class VirtualFilter:
|
||||
"progress": progress, # Filter.action feedback
|
||||
"current_temp": temp, # Filter.action feedback
|
||||
"filtered_volume": current_filtered, # Filter.action feedback
|
||||
"current_status": f"🌊 Filtering: {progress:.1f}% complete", # Filter.action feedback
|
||||
"status": status_msg,
|
||||
"status": "Running",
|
||||
"message": f"🌊 Filtering: {progress:.1f}% complete, {current_filtered:.1f}mL filtered"
|
||||
})
|
||||
|
||||
@@ -190,11 +186,10 @@ class VirtualFilter:
|
||||
"progress": 100.0, # Filter.action feedback
|
||||
"current_temp": final_temp, # Filter.action feedback
|
||||
"filtered_volume": filter_volume, # Filter.action feedback
|
||||
"current_status": f"✅ Filtration completed: {filter_volume}mL", # Filter.action feedback
|
||||
"message": f"✅ Filtration completed: {filter_volume}mL filtered from {vessel}"
|
||||
"message": f"✅ Filtration completed: {filter_volume}mL filtered from {vessel_id}"
|
||||
})
|
||||
|
||||
self.logger.info(f"🎉 过滤完成! 💧 {filter_volume}mL 从 {vessel} 过滤到 {filtrate_vessel} ✨")
|
||||
self.logger.info(f"🎉 过滤完成! 💧 {filter_volume}mL 从 {vessel_id} 过滤到 {filtrate_vessel_id} ✨")
|
||||
self.logger.info(f"📊 最终状态: 温度 {final_temp}°C | 进度 100% | 体积 {filter_volume}mL 🏁")
|
||||
return True
|
||||
|
||||
@@ -202,8 +197,7 @@ class VirtualFilter:
|
||||
error_msg = f"过滤过程中发生错误: {str(e)} 💥"
|
||||
self.logger.error(f"❌ {error_msg}")
|
||||
self.data.update({
|
||||
"status": f"❌ 过滤错误: {str(e)}",
|
||||
"current_status": f"❌ Filtration failed: {str(e)}",
|
||||
"status": f"Error",
|
||||
"message": f"❌ Filtration failed: {str(e)}"
|
||||
})
|
||||
return False
|
||||
@@ -222,17 +216,17 @@ class VirtualFilter:
|
||||
def current_temp(self) -> float:
|
||||
"""Filter.action feedback 字段 🌡️"""
|
||||
return self.data.get("current_temp", 25.0)
|
||||
|
||||
@property
|
||||
def filtered_volume(self) -> float:
|
||||
"""Filter.action feedback 字段 💧"""
|
||||
return self.data.get("filtered_volume", 0.0)
|
||||
|
||||
|
||||
@property
|
||||
def current_status(self) -> str:
|
||||
"""Filter.action feedback 字段 📋"""
|
||||
return self.data.get("current_status", "")
|
||||
|
||||
|
||||
@property
|
||||
def filtered_volume(self) -> float:
|
||||
"""Filter.action feedback 字段 💧"""
|
||||
return self.data.get("filtered_volume", 0.0)
|
||||
|
||||
@property
|
||||
def message(self) -> str:
|
||||
return self.data.get("message", "")
|
||||
|
||||
@@ -21,19 +21,6 @@ class VirtualMultiwayValve:
|
||||
self._current_position = 0 # 默认在0号位(transfer pump位置)
|
||||
self._target_position = 0
|
||||
|
||||
# 位置映射说明
|
||||
self.position_map = {
|
||||
0: "transfer_pump", # 0号位连接转移泵
|
||||
1: "port_1", # 1号位
|
||||
2: "port_2", # 2号位
|
||||
3: "port_3", # 3号位
|
||||
4: "port_4", # 4号位
|
||||
5: "port_5", # 5号位
|
||||
6: "port_6", # 6号位
|
||||
7: "port_7", # 7号位
|
||||
8: "port_8" # 8号位
|
||||
}
|
||||
|
||||
print(f"🔄 === 虚拟多通阀门已创建 === ✨")
|
||||
print(f"🎯 端口: {port} | 📊 位置范围: 0-{self.max_positions} | 🏠 初始位置: 0 (transfer_pump)")
|
||||
self.logger.info(f"🔧 多通阀门初始化: 端口={port}, 最大位置={self.max_positions}")
|
||||
@@ -60,7 +47,7 @@ class VirtualMultiwayValve:
|
||||
|
||||
def get_current_port(self) -> str:
|
||||
"""获取当前连接的端口名称 🔌"""
|
||||
return self.position_map.get(self._current_position, "unknown")
|
||||
return self._current_position
|
||||
|
||||
def set_position(self, command: Union[int, str]):
|
||||
"""
|
||||
@@ -115,7 +102,7 @@ class VirtualMultiwayValve:
|
||||
old_position = self._current_position
|
||||
old_port = self.get_current_port()
|
||||
|
||||
self.logger.info(f"🔄 阀门切换: {old_position}({old_port}) → {pos}({self.position_map.get(pos, 'unknown')}) {pos_emoji}")
|
||||
self.logger.info(f"🔄 阀门切换: {old_position}({old_port}) → {pos} {pos_emoji}")
|
||||
|
||||
self._status = "Busy"
|
||||
self._valve_state = "Moving"
|
||||
@@ -190,6 +177,17 @@ class VirtualMultiwayValve:
|
||||
"""获取阀门位置 - 兼容性方法 📍"""
|
||||
return self._current_position
|
||||
|
||||
def set_valve_position(self, command: Union[int, str]):
|
||||
"""
|
||||
设置阀门位置 - 兼容pump_protocol调用 🎯
|
||||
这是set_position的别名方法,用于兼容pump_protocol.py
|
||||
|
||||
Args:
|
||||
command: 目标位置 (0-8) 或位置字符串
|
||||
"""
|
||||
# 删除debug日志:self.logger.debug(f"🎯 兼容性调用: set_valve_position({command})")
|
||||
return self.set_position(command)
|
||||
|
||||
def is_at_position(self, position: int) -> bool:
|
||||
"""检查是否在指定位置 🎯"""
|
||||
result = self._current_position == position
|
||||
@@ -210,17 +208,6 @@ class VirtualMultiwayValve:
|
||||
# 删除debug日志:self.logger.debug(f"🔌 端口{port_number}检查: {port_status} (当前位置: {self._current_position})")
|
||||
return result
|
||||
|
||||
def get_available_positions(self) -> list:
|
||||
"""获取可用位置列表 📋"""
|
||||
positions = list(range(0, self.max_positions + 1))
|
||||
# 删除debug日志:self.logger.debug(f"📋 可用位置: {positions}")
|
||||
return positions
|
||||
|
||||
def get_available_ports(self) -> Dict[int, str]:
|
||||
"""获取可用端口映射 🗺️"""
|
||||
# 删除debug日志:self.logger.debug(f"🗺️ 端口映射: {self.position_map}")
|
||||
return self.position_map.copy()
|
||||
|
||||
def reset(self):
|
||||
"""重置阀门到transfer pump位置(0号位)🔄"""
|
||||
self.logger.info(f"🔄 重置阀门到泵位置...")
|
||||
@@ -259,17 +246,6 @@ class VirtualMultiwayValve:
|
||||
|
||||
return f"🔄 VirtualMultiwayValve({status_emoji} 位置: {self._current_position}/{self.max_positions}, 端口: {current_port}, 状态: {self._status})"
|
||||
|
||||
def set_valve_position(self, command: Union[int, str]):
|
||||
"""
|
||||
设置阀门位置 - 兼容pump_protocol调用 🎯
|
||||
这是set_position的别名方法,用于兼容pump_protocol.py
|
||||
|
||||
Args:
|
||||
command: 目标位置 (0-8) 或位置字符串
|
||||
"""
|
||||
# 删除debug日志:self.logger.debug(f"🎯 兼容性调用: set_valve_position({command})")
|
||||
return self.set_position(command)
|
||||
|
||||
|
||||
# 使用示例
|
||||
if __name__ == "__main__":
|
||||
@@ -291,10 +267,6 @@ if __name__ == "__main__":
|
||||
print(f"\n🔌 切换到2号位: {valve.set_to_port(2)}")
|
||||
print(f"📍 当前状态: {valve}")
|
||||
|
||||
# 显示所有可用位置
|
||||
print(f"\n📋 可用位置: {valve.get_available_positions()}")
|
||||
print(f"🗺️ 端口映射: {valve.get_available_ports()}")
|
||||
|
||||
# 测试切换功能
|
||||
print(f"\n🔄 智能切换测试:")
|
||||
print(f"当前位置: {valve._current_position}")
|
||||
|
||||
@@ -99,8 +99,8 @@ class VirtualRotavap:
|
||||
self.logger.error(f"❌ 时间参数类型无效: {type(time)},使用默认值180.0秒")
|
||||
time = 180.0
|
||||
|
||||
# 确保time是float类型
|
||||
time = float(time)
|
||||
# 确保time是float类型; 并加速
|
||||
time = float(time) / 10.0
|
||||
|
||||
# 🔧 简化处理:如果vessel就是设备自己,直接操作
|
||||
if vessel == self.device_id:
|
||||
|
||||
@@ -48,20 +48,6 @@ class VirtualSolenoidValve:
|
||||
"""获取阀门位置状态"""
|
||||
return "OPEN" if self._is_open else "CLOSED"
|
||||
|
||||
@property
|
||||
def state(self) -> dict:
|
||||
"""获取阀门完整状态"""
|
||||
return {
|
||||
"device_id": self.device_id,
|
||||
"port": self.port,
|
||||
"voltage": self.voltage,
|
||||
"response_time": self.response_time,
|
||||
"is_open": self._is_open,
|
||||
"valve_state": self._valve_state,
|
||||
"status": self._status,
|
||||
"position": self.valve_position
|
||||
}
|
||||
|
||||
async def set_valve_position(self, command: str = None, **kwargs):
|
||||
"""
|
||||
设置阀门位置 - ROS动作接口
|
||||
|
||||
@@ -1,3 +1,318 @@
|
||||
robotic_arm.Dummy2:
|
||||
category:
|
||||
- robot_arm
|
||||
class:
|
||||
action_value_mappings:
|
||||
auto-check_tf_update_actions:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default: {}
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: check_tf_update_actions的参数schema
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties: {}
|
||||
required: []
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: check_tf_update_actions参数
|
||||
type: object
|
||||
type: UniLabJsonCommand
|
||||
auto-moveit_joint_task:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default:
|
||||
joint_names: null
|
||||
joint_positions: null
|
||||
move_group: null
|
||||
retry: 10
|
||||
speed: 1
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: moveit_joint_task的参数schema(关节空间规划)
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties:
|
||||
joint_names:
|
||||
type: string
|
||||
joint_positions:
|
||||
type: string
|
||||
move_group:
|
||||
type: string
|
||||
retry:
|
||||
default: 10
|
||||
type: string
|
||||
speed:
|
||||
default: 1
|
||||
type: string
|
||||
required:
|
||||
- move_group
|
||||
- joint_positions
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: moveit_joint_task参数
|
||||
type: object
|
||||
type: UniLabJsonCommand
|
||||
auto-moveit_task:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default:
|
||||
cartesian: false
|
||||
move_group: null
|
||||
offsets:
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
position: null
|
||||
quaternion: null
|
||||
retry: 10
|
||||
speed: 1
|
||||
target_link: null
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: moveit_task的参数schema(笛卡尔空间/位姿规划)
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties:
|
||||
cartesian:
|
||||
default: false
|
||||
type: string
|
||||
move_group:
|
||||
type: string
|
||||
offsets:
|
||||
default:
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
type: string
|
||||
position:
|
||||
type: string
|
||||
quaternion:
|
||||
type: string
|
||||
retry:
|
||||
default: 10
|
||||
type: string
|
||||
speed:
|
||||
default: 1
|
||||
type: string
|
||||
target_link:
|
||||
type: string
|
||||
required:
|
||||
- move_group
|
||||
- position
|
||||
- quaternion
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: moveit_task参数
|
||||
type: object
|
||||
type: UniLabJsonCommand
|
||||
auto-post_init:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default:
|
||||
ros_node: null
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: post_init的参数schema
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties:
|
||||
ros_node:
|
||||
type: string
|
||||
required:
|
||||
- ros_node
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: post_init参数
|
||||
type: object
|
||||
type: UniLabJsonCommand
|
||||
auto-resource_manager:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default:
|
||||
parent_link: null
|
||||
resource: null
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: resource_manager的参数schema
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties:
|
||||
parent_link:
|
||||
type: string
|
||||
resource:
|
||||
type: string
|
||||
required:
|
||||
- resource
|
||||
- parent_link
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: resource_manager参数
|
||||
type: object
|
||||
type: UniLabJsonCommand
|
||||
auto-set_position:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default:
|
||||
command: null
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: ''
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties:
|
||||
command:
|
||||
type: string
|
||||
required:
|
||||
- command
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: set_position参数
|
||||
type: object
|
||||
type: UniLabJsonCommand
|
||||
auto-set_status:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default:
|
||||
command: null
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: ''
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties:
|
||||
command:
|
||||
type: string
|
||||
required:
|
||||
- command
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: set_status参数
|
||||
type: object
|
||||
type: UniLabJsonCommand
|
||||
auto-wait_for_resource_action:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default: {}
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: wait_for_resource_action的参数schema
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties: {}
|
||||
required: []
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: wait_for_resource_action参数
|
||||
type: object
|
||||
type: UniLabJsonCommand
|
||||
pick_and_place:
|
||||
feedback: {}
|
||||
goal:
|
||||
command: command
|
||||
goal_default:
|
||||
command: ''
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: ''
|
||||
properties:
|
||||
feedback:
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
title: SendCmd_Feedback
|
||||
type: object
|
||||
goal:
|
||||
properties:
|
||||
command:
|
||||
type: string
|
||||
required:
|
||||
- command
|
||||
title: SendCmd_Goal
|
||||
type: object
|
||||
result:
|
||||
properties:
|
||||
return_info:
|
||||
type: string
|
||||
success:
|
||||
type: boolean
|
||||
required:
|
||||
- return_info
|
||||
- success
|
||||
title: SendCmd_Result
|
||||
type: object
|
||||
required:
|
||||
- goal
|
||||
title: SendCmd
|
||||
type: object
|
||||
type: SendCmd
|
||||
module: unilabos.devices.ros_dev.moveit_interface:MoveitInterface
|
||||
status_types: {}
|
||||
type: python
|
||||
config_info: []
|
||||
description: Dummy2 六自由度机械臂(与线性滑台可选配)使用 MoveIt2 进行运动规划与控制。 通过 ROS2 Action 与 MoveItInterface
|
||||
对接,支持关节空间与笛卡尔空间规划、 轨迹执行、碰撞检测与逆运动学。底层 CAN2ETH 通信由独立服务提供,本设备 不直接管理网络参数。
|
||||
handles: []
|
||||
icon: dummy2_robot
|
||||
init_param_schema:
|
||||
config:
|
||||
properties:
|
||||
device_config:
|
||||
type: string
|
||||
joint_poses:
|
||||
type: string
|
||||
moveit_type:
|
||||
type: string
|
||||
rotation:
|
||||
type: string
|
||||
required:
|
||||
- moveit_type
|
||||
- joint_poses
|
||||
type: object
|
||||
data:
|
||||
properties: {}
|
||||
required: []
|
||||
type: object
|
||||
model:
|
||||
mesh: dummy2_robot
|
||||
type: device
|
||||
version: 1.0.0
|
||||
robotic_arm.SCARA_with_slider.virtual:
|
||||
category:
|
||||
- robot_arm
|
||||
|
||||
@@ -2161,8 +2161,6 @@ virtual_multiway_valve:
|
||||
type: SendCmd
|
||||
module: unilabos.devices.virtual.virtual_multiway_valve:VirtualMultiwayValve
|
||||
status_types:
|
||||
available_ports: dict
|
||||
available_positions: list
|
||||
current_port: str
|
||||
current_position: int
|
||||
flow_path: str
|
||||
@@ -2268,10 +2266,6 @@ virtual_multiway_valve:
|
||||
type: object
|
||||
data:
|
||||
properties:
|
||||
available_ports:
|
||||
type: object
|
||||
available_positions:
|
||||
type: array
|
||||
current_port:
|
||||
type: string
|
||||
current_position:
|
||||
@@ -2293,8 +2287,6 @@ virtual_multiway_valve:
|
||||
- target_position
|
||||
- current_port
|
||||
- valve_position
|
||||
- available_positions
|
||||
- available_ports
|
||||
- flow_path
|
||||
type: object
|
||||
version: 1.0.0
|
||||
@@ -3775,7 +3767,6 @@ virtual_solenoid_valve:
|
||||
module: unilabos.devices.virtual.virtual_solenoid_valve:VirtualSolenoidValve
|
||||
status_types:
|
||||
is_open: bool
|
||||
state: dict
|
||||
status: str
|
||||
valve_position: str
|
||||
valve_state: str
|
||||
@@ -3813,8 +3804,6 @@ virtual_solenoid_valve:
|
||||
properties:
|
||||
is_open:
|
||||
type: boolean
|
||||
state:
|
||||
type: object
|
||||
status:
|
||||
type: string
|
||||
valve_position:
|
||||
@@ -3826,7 +3815,6 @@ virtual_solenoid_valve:
|
||||
- valve_state
|
||||
- is_open
|
||||
- valve_position
|
||||
- state
|
||||
type: object
|
||||
version: 1.0.0
|
||||
virtual_solid_dispenser:
|
||||
|
||||
@@ -340,14 +340,14 @@ def convert_resources_to_type(
|
||||
Returns:
|
||||
List of resources in the given type.
|
||||
"""
|
||||
if resource_type == dict:
|
||||
if resource_type == dict or resource_type == str:
|
||||
return list_to_nested_dict(resources_list)
|
||||
elif isinstance(resource_type, type) and issubclass(resource_type, ResourcePLR):
|
||||
if isinstance(resources_list, dict):
|
||||
return resource_ulab_to_plr(resources_list, plr_model)
|
||||
resources_tree = dict_to_tree({r["id"]: r for r in resources_list})
|
||||
return resource_ulab_to_plr(resources_tree[0], plr_model)
|
||||
elif isinstance(resource_type, list) :
|
||||
elif isinstance(resource_type, list):
|
||||
if all((get_origin(t) is Union) for t in resource_type):
|
||||
resources_tree = dict_to_tree({r["id"]: r for r in resources_list})
|
||||
return [resource_ulab_to_plr(r, plr_model) for r in resources_tree]
|
||||
|
||||
@@ -336,7 +336,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
res.response = ""
|
||||
return res
|
||||
|
||||
def append_resource(req: SerialCommand_Request, res: SerialCommand_Response):
|
||||
async def append_resource(req: SerialCommand_Request, res: SerialCommand_Response):
|
||||
# 物料传输到对应的node节点
|
||||
rclient = self.create_client(ResourceAdd, "/resources/add")
|
||||
rclient.wait_for_service()
|
||||
@@ -395,10 +395,11 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
if "data" not in resource:
|
||||
resource["data"] = {}
|
||||
resource["data"].update(json.loads(container_instance.data))
|
||||
request.resources[0].name = resource["name"]
|
||||
logger.info(f"更新物料{container_query_dict['name']}的数据{resource['data']} dict")
|
||||
else:
|
||||
logger.info(f"更新物料{container_query_dict['name']}出现不支持的数据类型{type(resource)} {resource}")
|
||||
response = rclient.call(request)
|
||||
response = await rclient.call_async(request)
|
||||
# 应该先add_resource了
|
||||
res.response = "OK"
|
||||
# 如果driver自己就有assign的方法,那就使用driver自己的assign方法
|
||||
@@ -655,6 +656,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
|
||||
action_kwargs = convert_from_ros_msg_with_mapping(goal, action_value_mapping["goal"])
|
||||
self.lab_logger().debug(f"任务 {ACTION.__name__} 接收到原始目标: {action_kwargs}")
|
||||
error_skip = False
|
||||
# 向Host查询物料当前状态,如果是host本身的增加物料的请求,则直接跳过
|
||||
if action_name not in ["create_resource_detailed", "create_resource"]:
|
||||
for k, v in goal.get_fields_and_field_types().items():
|
||||
@@ -664,7 +666,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
# TODO: resource后面需要分组
|
||||
only_one_resource = False
|
||||
try:
|
||||
if len(action_kwargs[k]) > 1:
|
||||
if isinstance(action_kwargs[k], list) and len(action_kwargs[k]) > 1:
|
||||
for i in action_kwargs[k]:
|
||||
r = ResourceGet.Request()
|
||||
r.id = i["id"] # splash optional
|
||||
@@ -694,17 +696,43 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
final_resource = convert_resources_to_type(resources_list, final_type)
|
||||
else:
|
||||
final_resource = [convert_resources_to_type([i], final_type)[0] for i in resources_list]
|
||||
action_kwargs[k] = self.resource_tracker.figure_resource(final_resource)
|
||||
try:
|
||||
action_kwargs[k] = self.resource_tracker.figure_resource(final_resource, try_mode=True)
|
||||
except Exception as e:
|
||||
self.lab_logger().error(f"物料实例获取失败: {e}\n{traceback.format_exc()}")
|
||||
error_skip = True
|
||||
execution_error = traceback.format_exc()
|
||||
break
|
||||
|
||||
##### self.lab_logger().info(f"准备执行: {action_kwargs}, 函数: {ACTION.__name__}")
|
||||
time_start = time.time()
|
||||
time_overall = 100
|
||||
future = None
|
||||
# 将阻塞操作放入线程池执行
|
||||
if asyncio.iscoroutinefunction(ACTION):
|
||||
try:
|
||||
##### self.lab_logger().info(f"异步执行动作 {ACTION}")
|
||||
future = ROS2DeviceNode.run_async_func(ACTION, trace_error=False, **action_kwargs)
|
||||
if not error_skip:
|
||||
# 将阻塞操作放入线程池执行
|
||||
if asyncio.iscoroutinefunction(ACTION):
|
||||
try:
|
||||
##### self.lab_logger().info(f"异步执行动作 {ACTION}")
|
||||
future = ROS2DeviceNode.run_async_func(ACTION, trace_error=False, **action_kwargs)
|
||||
|
||||
def _handle_future_exception(fut):
|
||||
nonlocal execution_error, execution_success, action_return_value
|
||||
try:
|
||||
action_return_value = fut.result()
|
||||
execution_success = True
|
||||
except Exception as e:
|
||||
execution_error = traceback.format_exc()
|
||||
error(f"异步任务 {ACTION.__name__} 报错了\n{traceback.format_exc()}\n原始输入:{action_kwargs}")
|
||||
error(traceback.format_exc())
|
||||
|
||||
future.add_done_callback(_handle_future_exception)
|
||||
except Exception as e:
|
||||
execution_error = traceback.format_exc()
|
||||
execution_success = False
|
||||
self.lab_logger().error(f"创建异步任务失败: {traceback.format_exc()}")
|
||||
else:
|
||||
##### self.lab_logger().info(f"同步执行动作 {ACTION}")
|
||||
future = self._executor.submit(ACTION, **action_kwargs)
|
||||
|
||||
def _handle_future_exception(fut):
|
||||
nonlocal execution_error, execution_success, action_return_value
|
||||
@@ -712,28 +740,9 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
action_return_value = fut.result()
|
||||
execution_success = True
|
||||
except Exception as e:
|
||||
execution_error = traceback.format_exc()
|
||||
error(f"异步任务 {ACTION.__name__} 报错了\n{traceback.format_exc()}\n原始输入:{action_kwargs}")
|
||||
error(traceback.format_exc())
|
||||
error(f"同步任务 {ACTION.__name__} 报错了\n{traceback.format_exc()}\n原始输入:{action_kwargs}")
|
||||
|
||||
future.add_done_callback(_handle_future_exception)
|
||||
except Exception as e:
|
||||
execution_error = traceback.format_exc()
|
||||
execution_success = False
|
||||
self.lab_logger().error(f"创建异步任务失败: {traceback.format_exc()}")
|
||||
else:
|
||||
##### self.lab_logger().info(f"同步执行动作 {ACTION}")
|
||||
future = self._executor.submit(ACTION, **action_kwargs)
|
||||
|
||||
def _handle_future_exception(fut):
|
||||
nonlocal execution_error, execution_success, action_return_value
|
||||
try:
|
||||
action_return_value = fut.result()
|
||||
execution_success = True
|
||||
except Exception as e:
|
||||
error(f"同步任务 {ACTION.__name__} 报错了\n{traceback.format_exc()}\n原始输入:{action_kwargs}")
|
||||
|
||||
future.add_done_callback(_handle_future_exception)
|
||||
|
||||
action_type = action_value_mapping["type"]
|
||||
feedback_msg_types = action_type.Feedback.get_fields_and_field_types()
|
||||
|
||||
@@ -351,7 +351,7 @@ class HostNode(BaseROS2DeviceNode):
|
||||
except Exception as e:
|
||||
self.lab_logger().error(f"[Host Node] Failed to create ActionClient for {action_id}: {str(e)}")
|
||||
|
||||
def create_resource_detailed(
|
||||
async def create_resource_detailed(
|
||||
self,
|
||||
resources: list[Union[list["Resource"], "Resource"]],
|
||||
device_ids: list[str],
|
||||
@@ -393,24 +393,25 @@ class HostNode(BaseROS2DeviceNode):
|
||||
},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
response = sclient.call(request)
|
||||
response = await sclient.call_async(request)
|
||||
responses.append(response)
|
||||
return responses
|
||||
|
||||
def create_resource(
|
||||
async def create_resource(
|
||||
self,
|
||||
device_id: str,
|
||||
res_id: str,
|
||||
class_name: str,
|
||||
parent: str,
|
||||
bind_locations: Point,
|
||||
liquid_input_slot: list[int],
|
||||
liquid_type: list[str],
|
||||
liquid_volume: list[int],
|
||||
slot_on_deck: str,
|
||||
liquid_input_slot: list[int] = [],
|
||||
liquid_type: list[str] = [],
|
||||
liquid_volume: list[int] = [],
|
||||
slot_on_deck: str = "",
|
||||
):
|
||||
# 暂不支持多对同名父子同时存在
|
||||
res_creation_input = {
|
||||
"id": res_id.split("/")[-1],
|
||||
"name": res_id.split("/")[-1],
|
||||
"class": class_name,
|
||||
"parent": parent.split("/")[-1],
|
||||
@@ -424,8 +425,10 @@ class HostNode(BaseROS2DeviceNode):
|
||||
res_creation_input.update(
|
||||
{
|
||||
"data": {
|
||||
"liquids": [{
|
||||
"liquid_type": liquid_type[0] if liquid_type else None,
|
||||
"liquid_volume": liquid_volume[0] if liquid_volume else None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -448,7 +451,9 @@ class HostNode(BaseROS2DeviceNode):
|
||||
)
|
||||
]
|
||||
|
||||
return self.create_resource_detailed(resources, device_ids, bind_parent_id, bind_location, other_calling_param)
|
||||
response = await self.create_resource_detailed(resources, device_ids, bind_parent_id, bind_location, other_calling_param)
|
||||
|
||||
return response
|
||||
|
||||
def initialize_device(self, device_id: str, device_config: Dict[str, Any]) -> None:
|
||||
"""
|
||||
@@ -840,6 +845,15 @@ class HostNode(BaseROS2DeviceNode):
|
||||
success = bool(r)
|
||||
|
||||
response.success = success
|
||||
|
||||
if success:
|
||||
from unilabos.resources.graphio import physical_setup_graph
|
||||
for resource in resources:
|
||||
if resource.get("id") not in physical_setup_graph.nodes:
|
||||
physical_setup_graph.add_node(resource["id"], **resource)
|
||||
else:
|
||||
physical_setup_graph.nodes[resource["id"]]["data"].update(resource["data"])
|
||||
|
||||
self.lab_logger().info(f"[Host Node-Resource] Add request completed, success: {success}")
|
||||
return response
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
||||
# 为子设备的每个动作创建动作客户端
|
||||
if d is not None and hasattr(d, "ros_node_instance"):
|
||||
node = d.ros_node_instance
|
||||
node.resource_tracker = self.resource_tracker # 站内应当共享资源跟踪器
|
||||
for action_name, action_mapping in node._action_value_mappings.items():
|
||||
if action_name.startswith("auto-") or str(action_mapping.get("type", "")).startswith("UniLabJsonCommand"):
|
||||
continue
|
||||
@@ -223,9 +224,14 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
||||
|
||||
self.lab_logger().info(f"Working on physical setup: {physical_setup_graph}")
|
||||
protocol_steps = protocol_steps_generator(G=physical_setup_graph, **protocol_kwargs)
|
||||
|
||||
logs = []
|
||||
for step in protocol_steps:
|
||||
if isinstance(step, dict) and "log_message" in step.get("action_kwargs", {}):
|
||||
logs.append(step)
|
||||
elif isinstance(step, list):
|
||||
logs.append(step)
|
||||
self.lab_logger().info(f"Goal received: {protocol_kwargs}, running steps: "
|
||||
f"{json.dumps([step for step in protocol_steps if 'log_message' not in step['action_kwargs']], indent=4)}")
|
||||
f"{json.dumps(logs, indent=4, ensure_ascii=False)}")
|
||||
|
||||
time_start = time.time()
|
||||
time_overall = 100
|
||||
|
||||
@@ -36,7 +36,9 @@ class DeviceNodeResourceTracker(object):
|
||||
|
||||
def figure_resource(self, query_resource, try_mode=False):
|
||||
if isinstance(query_resource, list):
|
||||
return [self.figure_resource(r) for r in query_resource]
|
||||
return [self.figure_resource(r, try_mode) for r in query_resource]
|
||||
elif isinstance(query_resource, dict) and "id" not in query_resource and "name" not in query_resource: # 临时处理,要删除的,driver有太多类型错误标注
|
||||
return [self.figure_resource(r, try_mode) for r in query_resource.values()]
|
||||
res_id = query_resource.id if hasattr(query_resource, "id") else (query_resource.get("id") if isinstance(query_resource, dict) else None)
|
||||
res_name = query_resource.name if hasattr(query_resource, "name") else (query_resource.get("name") if isinstance(query_resource, dict) else None)
|
||||
res_identifier = res_id if res_id else res_name
|
||||
|
||||
@@ -51,7 +51,7 @@ class DeviceClassCreator(Generic[T]):
|
||||
"""
|
||||
if self.device_instance is not None:
|
||||
for c in self.children.values():
|
||||
if c["type"] == "container":
|
||||
if c["type"] != "device":
|
||||
self.resource_tracker.add_resource(c)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user