mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-08 07:55:12 +00:00
Compare commits
5 Commits
68871358c2
...
f2753fc69a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2753fc69a | ||
|
|
09ad905280 | ||
|
|
7714c71cd2 | ||
|
|
64832718be | ||
|
|
4139e079f4 |
45
test/experiments/camera.json
Normal file
45
test/experiments/camera.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "Camera",
|
||||
"name": "摄像头",
|
||||
"children": [
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "camera",
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
"camera_index": 0,
|
||||
"period": 0.05
|
||||
},
|
||||
"data": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "Gripper1",
|
||||
"name": "假夹爪",
|
||||
"children": [
|
||||
],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "gripper.mock",
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"config": {
|
||||
},
|
||||
"data": {
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
|
||||
]
|
||||
}
|
||||
@@ -680,7 +680,7 @@
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "5",
|
||||
"rotavap_1": "sample_in"
|
||||
"rotavap_1": "samplein"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -770,7 +770,7 @@
|
||||
"type": "transport",
|
||||
"port": {
|
||||
"multiway_valve_2": "4",
|
||||
"filter_1": "filter_in"
|
||||
"filter_1": "filterin"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -800,7 +800,7 @@
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_2": "6",
|
||||
"separator_1": "separator_in"
|
||||
"separator_1": "separatorin"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -809,7 +809,7 @@
|
||||
"target": "collection_bottle_3",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"separator_1": "bottom_phase_out",
|
||||
"separator_1": "bottomphaseout",
|
||||
"collection_bottle_3": "top"
|
||||
}
|
||||
},
|
||||
@@ -859,7 +859,7 @@
|
||||
"target": "waste_bottle_2",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"separator_1": "top_phase_out",
|
||||
"separator_1": "topphaseout",
|
||||
"waste_bottle_2": "top"
|
||||
}
|
||||
},
|
||||
@@ -879,7 +879,7 @@
|
||||
"target": "collection_bottle_1",
|
||||
"type": "transport",
|
||||
"port": {
|
||||
"filter_1": "filtrate_out",
|
||||
"filter_1": "filtrateout",
|
||||
"collection_bottle_1": "top"
|
||||
}
|
||||
},
|
||||
@@ -889,7 +889,7 @@
|
||||
"target": "waste_bottle_1",
|
||||
"type": "transport",
|
||||
"port": {
|
||||
"filter_1": "retentate_out",
|
||||
"filter_1": "retentateout",
|
||||
"waste_bottle_1": "top"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +48,9 @@ dependencies:
|
||||
- ros-humble-ros2-control
|
||||
- ros-humble-robot-state-publisher
|
||||
- ros-humble-joint-state-publisher
|
||||
# web
|
||||
# web and visualization
|
||||
- ros-humble-rosbridge-server
|
||||
- ros-humble-cv-bridge
|
||||
# geometry & motion planning
|
||||
- ros-humble-tf2
|
||||
- ros-humble-moveit
|
||||
|
||||
@@ -50,8 +50,9 @@ dependencies:
|
||||
- ros-humble-ros2-control
|
||||
- ros-humble-robot-state-publisher
|
||||
- ros-humble-joint-state-publisher
|
||||
# web
|
||||
# web and visualization
|
||||
- ros-humble-rosbridge-server
|
||||
- ros-humble-cv-bridge
|
||||
# geometry & motion planning
|
||||
- ros-humble-tf2
|
||||
- ros-humble-moveit
|
||||
|
||||
@@ -48,8 +48,9 @@ dependencies:
|
||||
- ros-humble-ros2-control
|
||||
- ros-humble-robot-state-publisher
|
||||
- ros-humble-joint-state-publisher
|
||||
# web
|
||||
# web and visualization
|
||||
- ros-humble-rosbridge-server
|
||||
- ros-humble-cv-bridge
|
||||
# geometry & motion planning
|
||||
- ros-humble-tf2
|
||||
- ros-humble-moveit
|
||||
|
||||
@@ -5,38 +5,138 @@ from .pump_protocol import generate_pump_protocol_with_rinsing, generate_pump_pr
|
||||
|
||||
|
||||
def find_gas_source(G: nx.DiGraph, gas: str) -> str:
|
||||
"""根据气体名称查找对应的气源"""
|
||||
# 按照命名规则查找气源
|
||||
"""
|
||||
根据气体名称查找对应的气源,支持多种匹配模式:
|
||||
1. 容器名称匹配
|
||||
2. 气体类型匹配(data.gas_type)
|
||||
3. 默认气源
|
||||
"""
|
||||
print(f"EVACUATE_REFILL: 正在查找气体 '{gas}' 的气源...")
|
||||
|
||||
# 第一步:通过容器名称匹配
|
||||
gas_source_patterns = [
|
||||
f"gas_source_{gas}",
|
||||
f"gas_{gas}",
|
||||
f"flask_{gas}",
|
||||
f"{gas}_source"
|
||||
f"{gas}_source",
|
||||
f"source_{gas}",
|
||||
f"reagent_bottle_{gas}",
|
||||
f"bottle_{gas}"
|
||||
]
|
||||
|
||||
for pattern in gas_source_patterns:
|
||||
if pattern in G.nodes():
|
||||
print(f"EVACUATE_REFILL: 通过名称匹配找到气源: {pattern}")
|
||||
return pattern
|
||||
|
||||
# 模糊匹配
|
||||
for node in G.nodes():
|
||||
node_class = G.nodes[node].get('class', '') or ''
|
||||
if 'gas_source' in node_class and gas.lower() in node.lower():
|
||||
return node
|
||||
if node.startswith('flask_') and gas.lower() in node.lower():
|
||||
return node
|
||||
# 第二步:通过气体类型匹配 (data.gas_type)
|
||||
for node_id in G.nodes():
|
||||
node_data = G.nodes[node_id]
|
||||
node_class = node_data.get('class', '') or ''
|
||||
|
||||
# 检查是否是气源设备
|
||||
if ('gas_source' in node_class or
|
||||
'gas' in node_id.lower() or
|
||||
node_id.startswith('flask_')):
|
||||
|
||||
# 检查 data.gas_type
|
||||
data = node_data.get('data', {})
|
||||
gas_type = data.get('gas_type', '')
|
||||
|
||||
if gas_type.lower() == gas.lower():
|
||||
print(f"EVACUATE_REFILL: 通过气体类型匹配找到气源: {node_id} (gas_type: {gas_type})")
|
||||
return node_id
|
||||
|
||||
# 检查 config.gas_type
|
||||
config = node_data.get('config', {})
|
||||
config_gas_type = config.get('gas_type', '')
|
||||
|
||||
if config_gas_type.lower() == gas.lower():
|
||||
print(f"EVACUATE_REFILL: 通过配置气体类型匹配找到气源: {node_id} (config.gas_type: {config_gas_type})")
|
||||
return node_id
|
||||
|
||||
# 查找所有可用的气源
|
||||
available_gas_sources = [
|
||||
# 第三步:查找所有可用的气源设备
|
||||
available_gas_sources = []
|
||||
for node_id in G.nodes():
|
||||
node_data = G.nodes[node_id]
|
||||
node_class = node_data.get('class', '') or ''
|
||||
|
||||
if ('gas_source' in node_class or
|
||||
'gas' in node_id.lower() or
|
||||
(node_id.startswith('flask_') and any(g in node_id.lower() for g in ['air', 'nitrogen', 'argon']))):
|
||||
|
||||
data = node_data.get('data', {})
|
||||
gas_type = data.get('gas_type', 'unknown')
|
||||
available_gas_sources.append(f"{node_id} (gas_type: {gas_type})")
|
||||
|
||||
print(f"EVACUATE_REFILL: 可用气源列表: {available_gas_sources}")
|
||||
|
||||
# 第四步:如果找不到特定气体,使用默认的第一个气源
|
||||
default_gas_sources = [
|
||||
node for node in G.nodes()
|
||||
if ((G.nodes[node].get('class') or '').startswith('virtual_gas_source')
|
||||
or ('gas' in node and 'source' in node)
|
||||
or (node.startswith('flask_') and any(g in node.lower() for g in ['air', 'nitrogen', 'argon', 'vacuum'])))
|
||||
or 'gas_source' in node)
|
||||
]
|
||||
|
||||
if default_gas_sources:
|
||||
default_source = default_gas_sources[0]
|
||||
print(f"EVACUATE_REFILL: ⚠️ 未找到特定气体 '{gas}',使用默认气源: {default_source}")
|
||||
return default_source
|
||||
|
||||
raise ValueError(f"找不到气体 '{gas}' 对应的气源。可用气源: {available_gas_sources}")
|
||||
|
||||
|
||||
def find_gas_source_by_any_match(G: nx.DiGraph, gas: str) -> str:
|
||||
"""
|
||||
增强版气源查找,支持各种匹配方式的别名函数
|
||||
"""
|
||||
return find_gas_source(G, gas)
|
||||
|
||||
|
||||
def get_gas_source_type(G: nx.DiGraph, gas_source: str) -> str:
|
||||
"""获取气源的气体类型"""
|
||||
if gas_source not in G.nodes():
|
||||
return "unknown"
|
||||
|
||||
node_data = G.nodes[gas_source]
|
||||
data = node_data.get('data', {})
|
||||
config = node_data.get('config', {})
|
||||
|
||||
# 检查多个可能的字段
|
||||
gas_type = (data.get('gas_type') or
|
||||
config.get('gas_type') or
|
||||
data.get('gas') or
|
||||
config.get('gas') or
|
||||
"air") # 默认为空气
|
||||
|
||||
return gas_type
|
||||
|
||||
|
||||
def find_vessels_by_gas_type(G: nx.DiGraph, gas: str) -> List[str]:
|
||||
"""
|
||||
根据气体类型查找所有匹配的容器/气源
|
||||
"""
|
||||
matching_vessels = []
|
||||
|
||||
for node_id in G.nodes():
|
||||
node_data = G.nodes[node_id]
|
||||
|
||||
# 检查容器名称匹配
|
||||
if gas.lower() in node_id.lower():
|
||||
matching_vessels.append(f"{node_id} (名称匹配)")
|
||||
continue
|
||||
|
||||
# 检查气体类型匹配
|
||||
data = node_data.get('data', {})
|
||||
config = node_data.get('config', {})
|
||||
|
||||
gas_type = data.get('gas_type', '') or config.get('gas_type', '')
|
||||
if gas_type.lower() == gas.lower():
|
||||
matching_vessels.append(f"{node_id} (gas_type: {gas_type})")
|
||||
|
||||
return matching_vessels
|
||||
|
||||
|
||||
def find_vacuum_pump(G: nx.DiGraph) -> str:
|
||||
"""查找真空泵设备"""
|
||||
vacuum_pumps = [
|
||||
|
||||
10
unilabos/registry/devices/camera.yaml
Normal file
10
unilabos/registry/devices/camera.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
camera:
|
||||
class:
|
||||
action_value_mappings: {}
|
||||
module: unilabos.ros.nodes.presets.camera:VideoPublisher
|
||||
status_types: {}
|
||||
type: ros2
|
||||
description: ''
|
||||
handles: []
|
||||
icon: ''
|
||||
init_param_schema: {}
|
||||
@@ -631,7 +631,7 @@ virtual_filter:
|
||||
data_source: handle
|
||||
data_type: transport
|
||||
description: 需要过滤的样品容器
|
||||
handler_key: filter_in
|
||||
handler_key: filterin
|
||||
io_type: target
|
||||
label: filter_in
|
||||
side: NORTH
|
||||
@@ -639,7 +639,7 @@ virtual_filter:
|
||||
data_source: handle
|
||||
data_type: transport
|
||||
description: 滤液出口
|
||||
handler_key: filtrate_out
|
||||
handler_key: filtrateout
|
||||
io_type: source
|
||||
label: filtrate_out
|
||||
side: SOUTH
|
||||
@@ -647,7 +647,7 @@ virtual_filter:
|
||||
data_source: handle
|
||||
data_type: transport
|
||||
description: 滤渣/固体出口
|
||||
handler_key: retentate_out
|
||||
handler_key: retentateout
|
||||
io_type: source
|
||||
label: retentate_out
|
||||
side: EAST
|
||||
@@ -2433,7 +2433,7 @@ virtual_rotavap:
|
||||
data_source: handle
|
||||
data_type: fluid
|
||||
description: 样品连接口
|
||||
handler_key: sample_in
|
||||
handler_key: samplein
|
||||
io_type: target
|
||||
label: sample_in
|
||||
side: NORTH
|
||||
@@ -2441,7 +2441,7 @@ virtual_rotavap:
|
||||
data_source: handle
|
||||
data_type: fluid
|
||||
description: 浓缩产物出口
|
||||
handler_key: product_out
|
||||
handler_key: productout
|
||||
io_type: source
|
||||
label: product_out
|
||||
side: SOUTH
|
||||
@@ -2449,7 +2449,7 @@ virtual_rotavap:
|
||||
data_source: handle
|
||||
data_type: fluid
|
||||
description: 冷凝溶剂出口
|
||||
handler_key: solvent_out
|
||||
handler_key: solventout
|
||||
io_type: source
|
||||
label: solvent_out
|
||||
side: EAST
|
||||
@@ -2774,7 +2774,7 @@ virtual_separator:
|
||||
data_source: handle
|
||||
data_type: fluid
|
||||
description: 需要分离的混合液体输入口
|
||||
handler_key: separator_in
|
||||
handler_key: separatorin
|
||||
io_type: target
|
||||
label: separator_in
|
||||
side: NORTH
|
||||
@@ -2782,7 +2782,7 @@ virtual_separator:
|
||||
data_source: executor
|
||||
data_type: fluid
|
||||
description: 下相(重相)液体输出口
|
||||
handler_key: bottom_phase_out
|
||||
handler_key: bottomphaseout
|
||||
io_type: source
|
||||
label: bottom_phase_out
|
||||
side: SOUTH
|
||||
@@ -2790,7 +2790,7 @@ virtual_separator:
|
||||
data_source: executor
|
||||
data_type: fluid
|
||||
description: 上相(轻相)液体输出口
|
||||
handler_key: top_phase_out
|
||||
handler_key: topphaseout
|
||||
io_type: source
|
||||
label: top_phase_out
|
||||
side: EAST
|
||||
|
||||
@@ -271,7 +271,7 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
- data_key: reagent
|
||||
data_source: handle
|
||||
@@ -282,7 +282,7 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -373,13 +373,13 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
output:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -460,7 +460,7 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
- data_key: solvent
|
||||
data_source: handle
|
||||
@@ -471,7 +471,7 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -580,7 +580,7 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
- data_key: solvent
|
||||
data_source: handle
|
||||
@@ -591,7 +591,7 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -675,7 +675,7 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
- data_key: solvent
|
||||
data_source: handle
|
||||
@@ -686,7 +686,7 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -766,13 +766,13 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
output:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -875,13 +875,13 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
output:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -992,7 +992,7 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
@@ -1003,7 +1003,7 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
@@ -1102,12 +1102,12 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: from_vessel
|
||||
handler_key: FromVessel
|
||||
label: From Vessel
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: to_vessel
|
||||
handler_key: ToVessel
|
||||
label: To Vessel
|
||||
- data_key: solvent
|
||||
data_source: handle
|
||||
@@ -1118,12 +1118,12 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: from_vessel_out
|
||||
handler_key: FromVesselOut
|
||||
label: From Vessel
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: to_vessel_out
|
||||
handler_key: ToVesselOut
|
||||
label: To Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -1211,13 +1211,13 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
output:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -1288,13 +1288,13 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
output:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -1352,13 +1352,13 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
output:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -1428,12 +1428,12 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: from_vessel
|
||||
handler_key: FromVessel
|
||||
label: From Vessel
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: to_vessel
|
||||
handler_key: ToVessel
|
||||
label: To Vessel
|
||||
- data_key: solvent
|
||||
data_source: handle
|
||||
@@ -1444,12 +1444,12 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: from_vessel_out
|
||||
handler_key: FromVesselOut
|
||||
label: From Vessel
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: to_vessel_out
|
||||
handler_key: ToVesselOut
|
||||
label: To Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -1569,23 +1569,23 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: from_vessel
|
||||
handler_key: FromVessel
|
||||
label: From Vessel
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: to_vessel
|
||||
handler_key: ToVessel
|
||||
label: To Vessel
|
||||
output:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: from_vessel_out
|
||||
handler_key: FromVesselOut
|
||||
label: From Vessel
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: to_vessel_out
|
||||
handler_key: ToVesselOut
|
||||
label: To Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -1673,12 +1673,12 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: from_vessel
|
||||
handler_key: FromVessel
|
||||
label: From Vessel
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: to_vessel
|
||||
handler_key: ToVessel
|
||||
label: To Vessel
|
||||
- data_key: solvent
|
||||
data_source: handle
|
||||
@@ -1689,12 +1689,12 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: from_vessel_out
|
||||
handler_key: FromVesselOut
|
||||
label: From Vessel
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: to_vessel_out
|
||||
handler_key: ToVesselOut
|
||||
label: To Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -1823,13 +1823,13 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
output:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -1900,13 +1900,13 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
output:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -1964,13 +1964,13 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
output:
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -2046,12 +2046,12 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: from_vessel
|
||||
handler_key: FromVessel
|
||||
label: From Vessel
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: to_vessel
|
||||
handler_key: ToVessel
|
||||
label: To Vessel
|
||||
- data_key: solvent
|
||||
data_source: handle
|
||||
@@ -2062,12 +2062,12 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: from_vessel_out
|
||||
handler_key: FromVesselOut
|
||||
label: From Vessel
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
handler_key: to_vessel_out
|
||||
handler_key: ToVesselOut
|
||||
label: To Vessel
|
||||
result: {}
|
||||
schema:
|
||||
@@ -2173,7 +2173,7 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel
|
||||
handler_key: Vessel
|
||||
label: Vessel
|
||||
- data_key: solvent
|
||||
data_source: handle
|
||||
@@ -2189,7 +2189,7 @@ workstation:
|
||||
- data_key: vessel
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
handler_key: vessel_out
|
||||
handler_key: VesselOut
|
||||
label: Vessel Out
|
||||
- data_key: vessel
|
||||
data_source: executor
|
||||
|
||||
61
unilabos/ros/nodes/presets/camera.py
Normal file
61
unilabos/ros/nodes/presets/camera.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
import cv2
|
||||
from sensor_msgs.msg import Image
|
||||
from cv_bridge import CvBridge
|
||||
from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode, DeviceNodeResourceTracker
|
||||
|
||||
class VideoPublisher(BaseROS2DeviceNode):
|
||||
def __init__(self, device_id='video_publisher', camera_index=0, period: float = 0.1, resource_tracker: DeviceNodeResourceTracker = None):
|
||||
# 初始化BaseROS2DeviceNode,使用自身作为driver_instance
|
||||
BaseROS2DeviceNode.__init__(
|
||||
self,
|
||||
driver_instance=self,
|
||||
device_id=device_id,
|
||||
status_types={},
|
||||
action_value_mappings={},
|
||||
hardware_interface="camera",
|
||||
print_publish=False,
|
||||
resource_tracker=resource_tracker,
|
||||
)
|
||||
# 创建一个发布者,发布到 /video 话题,消息类型为 sensor_msgs/Image,队列长度设为 10
|
||||
self.publisher_ = self.create_publisher(Image, f'/{device_id}/video', 10)
|
||||
# 初始化摄像头(默认设备索引为 0)
|
||||
self.cap = cv2.VideoCapture(camera_index)
|
||||
if not self.cap.isOpened():
|
||||
self.get_logger().error("无法打开摄像头")
|
||||
# 用于将 OpenCV 的图像转换为 ROS 图像消息
|
||||
self.bridge = CvBridge()
|
||||
# 设置定时器,10 Hz 发布一次
|
||||
timer_period = period # 单位:秒
|
||||
self.timer = self.create_timer(timer_period, self.timer_callback)
|
||||
|
||||
def timer_callback(self):
|
||||
ret, frame = self.cap.read()
|
||||
if not ret:
|
||||
self.get_logger().error("读取视频帧失败")
|
||||
return
|
||||
# 将 OpenCV 图像转换为 ROS Image 消息,注意图像编码需与摄像头数据匹配,这里使用 bgr8
|
||||
img_msg = self.bridge.cv2_to_imgmsg(frame, encoding="bgr8")
|
||||
self.publisher_.publish(img_msg)
|
||||
# self.get_logger().info("已发布视频帧")
|
||||
|
||||
def destroy_node(self):
|
||||
# 释放摄像头资源
|
||||
if self.cap.isOpened():
|
||||
self.cap.release()
|
||||
super().destroy_node()
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
node = VideoPublisher()
|
||||
try:
|
||||
rclpy.spin(node)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
node.destroy_node()
|
||||
rclpy.shutdown()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -17,7 +17,7 @@ from unilabos.ros.msgs.message_converter import (
|
||||
get_action_type,
|
||||
convert_to_ros_msg,
|
||||
convert_from_ros_msg,
|
||||
convert_from_ros_msg_with_mapping,
|
||||
convert_from_ros_msg_with_mapping, String,
|
||||
)
|
||||
from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode, DeviceNodeResourceTracker, ROS2DeviceNode
|
||||
from unilabos.utils.log import error
|
||||
@@ -75,7 +75,7 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
||||
try:
|
||||
d = self.initialize_device(device_id, device_config)
|
||||
except Exception as ex:
|
||||
self.lab_logger().error(f"[Protocol Node] Failed to initialize device {device_id}: {ex}")
|
||||
self.lab_logger().error(f"[Protocol Node] Failed to initialize device {device_id}: {ex}\n{traceback.format_exc()}")
|
||||
d = None
|
||||
if d is None:
|
||||
continue
|
||||
@@ -134,11 +134,17 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
||||
if d is not None and hasattr(d, "ros_node_instance"):
|
||||
node = d.ros_node_instance
|
||||
for action_name, action_mapping in node._action_value_mappings.items():
|
||||
if action_name.startswith("auto-"):
|
||||
continue
|
||||
action_id = f"/devices/{device_id_abs}/{action_name}"
|
||||
if action_id not in self._action_clients:
|
||||
self._action_clients[action_id] = ActionClient(
|
||||
self, action_mapping["type"], action_id, callback_group=self.callback_group
|
||||
)
|
||||
try:
|
||||
self._action_clients[action_id] = ActionClient(
|
||||
self, action_mapping["type"], action_id, callback_group=self.callback_group
|
||||
)
|
||||
except Exception as ex:
|
||||
self.lab_logger().error(f"创建动作客户端失败: {action_id}, 错误: {ex}")
|
||||
continue
|
||||
self.lab_logger().debug(f"为子设备 {device_id} 创建动作客户端: {action_name}")
|
||||
return d
|
||||
|
||||
|
||||
Reference in New Issue
Block a user