初始提交,只保留工作区当前状态

This commit is contained in:
tt11142023
2025-08-01 16:48:48 +08:00
parent 5cd0f72fbd
commit ac198b1f79
35 changed files with 2828 additions and 0 deletions

98
opcua_config.json Normal file
View File

@@ -0,0 +1,98 @@
{
"register_node_list_from_csv_path": {
"path": "simple_opcua_nodes.csv"
},
"create_flow": [
{
"name": "温度控制流程",
"action": [
{
"name": "温度控制动作",
"node_function_to_create": [
{
"func_name": "read_temperature",
"node_name": "Temperature",
"mode": "read"
},
{
"func_name": "read_heating_status",
"node_name": "HeatingStatus",
"mode": "read"
},
{
"func_name": "set_heating",
"node_name": "HeatingEnabled",
"mode": "write",
"value": true
}
],
"create_init_function": {
"func_name": "init_setpoint",
"node_name": "Setpoint",
"mode": "write",
"value": 25.0
},
"create_start_function": {
"func_name": "start_heating_control",
"node_name": "HeatingEnabled",
"mode": "write",
"write_functions": [
"set_heating"
],
"condition_functions": [
"read_temperature",
"read_heating_status"
],
"stop_condition_expression": "read_temperature >= 25.0 and read_heating_status"
},
"create_stop_function": {
"func_name": "stop_heating",
"node_name": "HeatingEnabled",
"mode": "write",
"value": false
},
"create_cleanup_function": null
}
]
},
{
"name": "报警重置流程",
"action": [
{
"name": "报警重置动作",
"node_function_to_create": [
{
"func_name": "reset_alarm",
"node_name": "ResetAlarm",
"mode": "call",
"value": []
}
],
"create_init_function": null,
"create_start_function": {
"func_name": "start_reset_alarm",
"node_name": "ResetAlarm",
"mode": "call",
"write_functions": [],
"condition_functions": [
"reset_alarm"
],
"stop_condition_expression": "True"
},
"create_stop_function": null,
"create_cleanup_function": null
}
]
},
{
"name": "完整控制流程",
"action": [
"温度控制流程",
"报警重置流程"
]
}
],
"execute_flow": [
"完整控制流程"
]
}

0
python Normal file
View File

View File

@@ -0,0 +1,41 @@
:: Generated by vinca http://github.com/RoboStack/vinca.
:: DO NOT EDIT!
setlocal EnableDelayedExpansion
set "PYTHONPATH=%LIBRARY_PREFIX%\lib\site-packages;%SP_DIR%"
:: MSVC is preferred.
set CC=cl.exe
set CXX=cl.exe
rd /s /q build
mkdir build
pushd build
:: set "CMAKE_GENERATOR=Ninja"
:: try to fix long paths issues by using default generator
set "CMAKE_GENERATOR=Visual Studio %VS_MAJOR% %VS_YEAR%"
set "SP_DIR_FORWARDSLASHES=%SP_DIR:\=/%"
set PYTHON="%PREFIX%\python.exe"
cmake ^
-G "%CMAKE_GENERATOR%" ^
-DCMAKE_INSTALL_PREFIX=%LIBRARY_PREFIX% ^
-DCMAKE_BUILD_TYPE=Release ^
-DCMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP=True ^
-DPYTHON_EXECUTABLE=%PYTHON% ^
-DPython_EXECUTABLE=%PYTHON% ^
-DPython3_EXECUTABLE=%PYTHON% ^
-DSETUPTOOLS_DEB_LAYOUT=OFF ^
-DBUILD_SHARED_LIBS=ON ^
-DBUILD_TESTING=OFF ^
-DCMAKE_OBJECT_PATH_MAX=255 ^
-DPYTHON_INSTALL_DIR=%SP_DIR_FORWARDSLASHES% ^
--compile-no-warning-as-error ^
%SRC_DIR%\%PKG_NAME%\src\work
if errorlevel 1 exit 1
cmake --build . --config Release --target install
if errorlevel 1 exit 1

View File

@@ -0,0 +1,71 @@
# Generated by vinca http://github.com/RoboStack/vinca.
# DO NOT EDIT!
rm -rf build
mkdir build
cd build
# necessary for correctly linking SIP files (from python_qt_bindings)
export LINK=$CXX
if [[ "$CONDA_BUILD_CROSS_COMPILATION" != "1" ]]; then
PYTHON_EXECUTABLE=$PREFIX/bin/python
PKG_CONFIG_EXECUTABLE=$PREFIX/bin/pkg-config
OSX_DEPLOYMENT_TARGET="10.15"
else
PYTHON_EXECUTABLE=$BUILD_PREFIX/bin/python
PKG_CONFIG_EXECUTABLE=$BUILD_PREFIX/bin/pkg-config
OSX_DEPLOYMENT_TARGET="11.0"
fi
echo "USING PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}"
echo "USING PKG_CONFIG_EXECUTABLE=${PKG_CONFIG_EXECUTABLE}"
export ROS_PYTHON_VERSION=`$PYTHON_EXECUTABLE -c "import sys; print('%i.%i' % (sys.version_info[0:2]))"`
echo "Using Python ${ROS_PYTHON_VERSION}"
# Fix up SP_DIR which for some reason might contain a path to a wrong Python version
FIXED_SP_DIR=$(echo $SP_DIR | sed -E "s/python[0-9]+\.[0-9]+/python$ROS_PYTHON_VERSION/")
echo "Using site-package dir ${FIXED_SP_DIR}"
# see https://github.com/conda-forge/cross-python-feedstock/issues/24
if [[ "$CONDA_BUILD_CROSS_COMPILATION" == "1" ]]; then
find $PREFIX/lib/cmake -type f -exec sed -i "s~\${_IMPORT_PREFIX}/lib/python${ROS_PYTHON_VERSION}/site-packages~${BUILD_PREFIX}/lib/python${ROS_PYTHON_VERSION}/site-packages~g" {} + || true
find $PREFIX/share/rosidl* -type f -exec sed -i "s~$PREFIX/lib/python${ROS_PYTHON_VERSION}/site-packages~${BUILD_PREFIX}/lib/python${ROS_PYTHON_VERSION}/site-packages~g" {} + || true
find $PREFIX/share/rosidl* -type f -exec sed -i "s~\${_IMPORT_PREFIX}/lib/python${ROS_PYTHON_VERSION}/site-packages~${BUILD_PREFIX}/lib/python${ROS_PYTHON_VERSION}/site-packages~g" {} + || true
find $PREFIX/lib/cmake -type f -exec sed -i "s~message(FATAL_ERROR \"The imported target~message(WARNING \"The imported target~g" {} + || true
fi
if [[ $target_platform =~ linux.* ]]; then
export CFLAGS="${CFLAGS} -D__STDC_FORMAT_MACROS=1"
export CXXFLAGS="${CXXFLAGS} -D__STDC_FORMAT_MACROS=1"
fi;
# Needed for qt-gui-cpp ..
if [[ $target_platform =~ linux.* ]]; then
ln -s $GCC ${BUILD_PREFIX}/bin/gcc
ln -s $GXX ${BUILD_PREFIX}/bin/g++
fi;
cmake \
-G "Ninja" \
-DCMAKE_INSTALL_PREFIX=$PREFIX \
-DCMAKE_PREFIX_PATH=$PREFIX \
-DAMENT_PREFIX_PATH=$PREFIX \
-DCMAKE_INSTALL_LIBDIR=lib \
-DCMAKE_BUILD_TYPE=Release \
-DPYTHON_EXECUTABLE=$PYTHON_EXECUTABLE \
-DPython_EXECUTABLE=$PYTHON_EXECUTABLE \
-DPython3_EXECUTABLE=$PYTHON_EXECUTABLE \
-DPython3_FIND_STRATEGY=LOCATION \
-DPKG_CONFIG_EXECUTABLE=$PKG_CONFIG_EXECUTABLE \
-DPYTHON_INSTALL_DIR=$FIXED_SP_DIR \
-DSETUPTOOLS_DEB_LAYOUT=OFF \
-DCATKIN_SKIP_TESTING=$SKIP_TESTING \
-DCMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP=True \
-DBUILD_SHARED_LIBS=ON \
-DBUILD_TESTING=OFF \
-DCMAKE_OSX_DEPLOYMENT_TARGET=$OSX_DEPLOYMENT_TARGET \
--compile-no-warning-as-error \
$SRC_DIR/$PKG_NAME/src/work
cmake --build . --config Release --target install

View File

@@ -0,0 +1,61 @@
package:
name: ros-humble-unilabos-msgs
version: 0.9.7
source:
path: ../../unilabos_msgs
folder: ros-humble-unilabos-msgs/src/work
build:
script:
sel(win): bld_ament_cmake.bat
sel(unix): build_ament_cmake.sh
number: 5
about:
home: https://www.ros.org/
license: BSD-3-Clause
summary: |
Robot Operating System
extra:
recipe-maintainers:
- ros-forge
requirements:
build:
- "{{ compiler('cxx') }}"
- "{{ compiler('c') }}"
- sel(linux64): sysroot_linux-64 2.17
- ninja
- setuptools
- sel(unix): make
- sel(unix): coreutils
- sel(osx): tapi
- sel(build_platform != target_platform): pkg-config
- cmake
- cython
- sel(win): vs2022_win-64
- sel(build_platform != target_platform): python
- sel(build_platform != target_platform): cross-python_{{ target_platform }}
- sel(build_platform != target_platform): numpy
host:
- numpy
- pip
- sel(build_platform == target_platform): pkg-config
- robostack-staging::ros-humble-action-msgs
- robostack-staging::ros-humble-ament-cmake
- robostack-staging::ros-humble-ament-lint-auto
- robostack-staging::ros-humble-ament-lint-common
- robostack-staging::ros-humble-ros-environment
- robostack-staging::ros-humble-ros-workspace
- robostack-staging::ros-humble-rosidl-default-generators
- robostack-staging::ros-humble-std-msgs
- robostack-staging::ros-humble-geometry-msgs
- robostack-staging::ros2-distro-mutex=0.5.*
run:
- robostack-staging::ros-humble-action-msgs
- robostack-staging::ros-humble-ros-workspace
- robostack-staging::ros-humble-rosidl-default-runtime
- robostack-staging::ros-humble-std-msgs
- robostack-staging::ros-humble-geometry-msgs
# - robostack-staging::ros2-distro-mutex=0.6.*
- sel(osx and x86_64): __osx >={{ MACOSX_DEPLOYMENT_TARGET|default('10.14') }}

View File

@@ -0,0 +1,19 @@
{
"nodes": [
{
"id": "id",
"name": "name",
"children": [
],
"parent": null,
"type": "device",
"class": "opcua_example",
"config": {
"url": "url",
"config_path": "unilabos/device_comms/opcua_client/opcua_workflow_example.json"
},
"data": {
}
}
]
}

View File

@@ -0,0 +1,19 @@
# OPC UA 通用客户端
本模块提供了一个通用的 OPC UA 客户端实现可以通过外部配置CSV文件来定义节点并通过JSON配置来执行工作流。
## 特点
- 支持通过 CSV 文件配置 OPC UA 节点(只需提供名称、类型和数据类型,支持节点为中文名需指定NodeLanguage
- 自动查找服务器中的节点无需知道确切的节点ID
- 提供工作流机制
- 支持通过 JSON 配置创建工作流
## 使用方法
step1: 准备opcua_nodes.csv文件
step2: 编写opcua_workflow_example.json,以定义工作流。指定opcua_nodes.csv
step3: 编写工作流对应action
step4: 编写opcua_example.yaml注册表
step5: 编写opcua_example.json组态图。指定opcua_workflow_example.json定义工作流文件

View File

@@ -0,0 +1,12 @@
from unilabos.device_comms.opcua_client.client_o import OpcUaClient, BaseClient
from unilabos.device_comms.opcua_client.node.uniopcua import Variable, Method, Object, NodeType, DataType
__all__ = [
'OpcUaClient',
'BaseClient',
'Variable',
'Method',
'Object',
'NodeType',
'DataType',
]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
from unilabos.device_comms.opcua_client.node.uniopcua import Variable, Method, Object, NodeType, DataType, Base
__all__ = [
'Variable',
'Method',
'Object',
'NodeType',
'DataType',
'Base',
]

View File

@@ -0,0 +1,180 @@
# coding=utf-8
from enum import Enum
from abc import ABC, abstractmethod
from typing import Tuple, Union, Optional, Any, List
from opcua import Client, Node
from opcua.ua import NodeId, NodeClass, VariantType
class DataType(Enum):
BOOLEAN = VariantType.Boolean
SBYTE = VariantType.SByte
BYTE = VariantType.Byte
INT16 = VariantType.Int16
UINT16 = VariantType.UInt16
INT32 = VariantType.Int32
UINT32 = VariantType.UInt32
INT64 = VariantType.Int64
UINT64 = VariantType.UInt64
FLOAT = VariantType.Float
DOUBLE = VariantType.Double
STRING = VariantType.String
DATETIME = VariantType.DateTime
BYTESTRING = VariantType.ByteString
class NodeType(Enum):
VARIABLE = NodeClass.Variable
OBJECT = NodeClass.Object
METHOD = NodeClass.Method
OBJECTTYPE = NodeClass.ObjectType
VARIABLETYPE = NodeClass.VariableType
REFERENCETYPE = NodeClass.ReferenceType
DATATYPE = NodeClass.DataType
VIEW = NodeClass.View
class Base(ABC):
def __init__(self, client: Client, name: str, node_id: str, typ: NodeType, data_type: DataType):
self._node_id: str = node_id
self._client = client
self._name = name
self._type = typ
self._data_type = data_type
self._node: Optional[Node] = None
def _get_node(self) -> Node:
if self._node is None:
try:
# 检查是否是NumericNodeId(ns=X;i=Y)格式
if "NumericNodeId" in self._node_id:
# 从字符串中提取命名空间和标识符
import re
match = re.search(r'ns=(\d+);i=(\d+)', self._node_id)
if match:
ns = int(match.group(1))
identifier = int(match.group(2))
node_id = NodeId(identifier, ns)
self._node = self._client.get_node(node_id)
else:
raise ValueError(f"无法解析节点ID: {self._node_id}")
else:
# 直接使用节点ID字符串
self._node = self._client.get_node(self._node_id)
except Exception as e:
print(f"获取节点失败: {self._node_id}, 错误: {e}")
raise
return self._node
@abstractmethod
def read(self) -> Tuple[Any, bool]:
"""读取节点值,返回(值, 是否出错)"""
pass
@abstractmethod
def write(self, value: Any) -> bool:
"""写入节点值,返回是否出错"""
pass
@property
def type(self) -> NodeType:
return self._type
@property
def node_id(self) -> str:
return self._node_id
@property
def name(self) -> str:
return self._name
class Variable(Base):
def __init__(self, client: Client, name: str, node_id: str, data_type: DataType):
super().__init__(client, name, node_id, NodeType.VARIABLE, data_type)
def read(self) -> Tuple[Any, bool]:
try:
value = self._get_node().get_value()
return value, False
except Exception as e:
print(f"读取变量 {self._name} 失败: {e}")
return None, True
def write(self, value: Any) -> bool:
try:
self._get_node().set_value(value)
return False
except Exception as e:
print(f"写入变量 {self._name} 失败: {e}")
return True
class Method(Base):
def __init__(self, client: Client, name: str, node_id: str, parent_node_id: str, data_type: DataType):
super().__init__(client, name, node_id, NodeType.METHOD, data_type)
self._parent_node_id = parent_node_id
self._parent_node = None
def _get_parent_node(self) -> Node:
if self._parent_node is None:
try:
# 检查是否是NumericNodeId(ns=X;i=Y)格式
if "NumericNodeId" in self._parent_node_id:
# 从字符串中提取命名空间和标识符
import re
match = re.search(r'ns=(\d+);i=(\d+)', self._parent_node_id)
if match:
ns = int(match.group(1))
identifier = int(match.group(2))
node_id = NodeId(identifier, ns)
self._parent_node = self._client.get_node(node_id)
else:
raise ValueError(f"无法解析父节点ID: {self._parent_node_id}")
else:
# 直接使用节点ID字符串
self._parent_node = self._client.get_node(self._parent_node_id)
except Exception as e:
print(f"获取父节点失败: {self._parent_node_id}, 错误: {e}")
raise
return self._parent_node
def read(self) -> Tuple[Any, bool]:
"""方法节点不支持读取操作"""
return None, True
def write(self, value: Any) -> bool:
"""方法节点不支持写入操作"""
return True
def call(self, *args) -> Tuple[Any, bool]:
"""调用方法,返回(返回值, 是否出错)"""
try:
result = self._get_parent_node().call_method(self._get_node(), *args)
return result, False
except Exception as e:
print(f"调用方法 {self._name} 失败: {e}")
return None, True
class Object(Base):
def __init__(self, client: Client, name: str, node_id: str):
super().__init__(client, name, node_id, NodeType.OBJECT, None)
def read(self) -> Tuple[Any, bool]:
"""对象节点不支持直接读取操作"""
return None, True
def write(self, value: Any) -> bool:
"""对象节点不支持直接写入操作"""
return True
def get_children(self) -> Tuple[List[Node], bool]:
"""获取子节点列表,返回(子节点列表, 是否出错)"""
try:
children = self._get_node().get_children()
return children, False
except Exception as e:
print(f"获取对象 {self._name} 的子节点失败: {e}")
return [], True

View File

@@ -0,0 +1,2 @@
Name,EnglishName,NodeType,DataType,NodeLanguage
中文名,EnglishName,VARIABLE,INT32,Chinese
1 Name EnglishName NodeType DataType NodeLanguage
2 中文名 EnglishName VARIABLE INT32 Chinese

View File

@@ -0,0 +1,30 @@
{
"register_node_list_from_csv_path": {
"path": "opcua_nodes_example.csv"
},
"create_flow": [
{
"name": "name",
"description": "description",
"parameters": ["parameter1", "parameter2"],
"action": [
{
"init_function": {
"func_name": "init_grab_params",
"write_nodes": ["parameter1", "parameter2"]
},
"start_function": {
"func_name": "start_grab",
"write_nodes": {"parameter_start": true},
"condition_nodes": ["parameter_condition"],
"stop_condition_expression": "parameter_condition == True"
},
"stop_function": {
"func_name": "stop_grab",
"write_nodes": {"parameter_start": false}
}
}
]
}
]
}

View File

@@ -0,0 +1,311 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
OPC UA测试服务器
用于测试OPC UA客户端功能特别是temperature_control和valve_control工作流
"""
import sys
import time
import logging
from opcua import Server, ua
import threading
# 设置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class OpcUaTestServer:
"""OPC UA测试服务器类"""
def __init__(self, endpoint="opc.tcp://localhost:4840/freeopcua/server/"):
"""
初始化OPC UA服务器
Args:
endpoint: 服务器端点URL
"""
self.server = Server()
self.server.set_endpoint(endpoint)
# 设置服务器名称
self.server.set_server_name("UniLabOS OPC UA Test Server")
# 设置服务器命名空间
self.idx = self.server.register_namespace("http://unilabos.com/opcua/test")
# 获取Objects节点
self.objects = self.server.get_objects_node()
# 创建设备对象
self.device = self.objects.add_object(self.idx, "TestDevice")
# 存储所有节点的字典
self.nodes = {}
# 初始化标志
self.running = False
# 控制标志
self.simulation_active = True
def add_variable(self, name, value, data_type=None):
"""
添加变量节点
Args:
name: 变量名称
value: 初始值
data_type: 数据类型 (可选)
"""
if data_type is None:
var = self.device.add_variable(self.idx, name, value)
else:
var = self.device.add_variable(self.idx, name, value, data_type)
# 设置变量可写
var.set_writable()
# 存储节点
self.nodes[name] = var
logger.info(f"添加变量节点: {name}, 初始值: {value}")
return var
def add_method(self, name, callback, inputs=None, outputs=None):
"""
添加方法节点
Args:
name: 方法名称
callback: 回调函数
inputs: 输入参数列表 [(name, type), ...]
outputs: 输出参数列表 [(name, type), ...]
"""
if inputs is None:
inputs = []
if outputs is None:
outputs = []
# 创建输入参数
input_args = []
for arg_name, arg_type in inputs:
input_args.append(ua.Argument())
input_args[-1].Name = arg_name
input_args[-1].DataType = arg_type
input_args[-1].ValueRank = -1
# 创建输出参数
output_args = []
for arg_name, arg_type in outputs:
output_args.append(ua.Argument())
output_args[-1].Name = arg_name
output_args[-1].DataType = arg_type
output_args[-1].ValueRank = -1
# 添加方法
method = self.device.add_method(
self.idx,
name,
callback,
input_args,
output_args
)
# 存储节点
self.nodes[name] = method
logger.info(f"添加方法节点: {name}")
return method
def start(self):
"""启动服务器"""
if not self.running:
self.server.start()
self.running = True
logger.info("OPC UA服务器已启动")
# 启动模拟线程
self.simulation_thread = threading.Thread(target=self.run_simulation)
self.simulation_thread.daemon = True
self.simulation_thread.start()
def stop(self):
"""停止服务器"""
if self.running:
self.simulation_active = False
if hasattr(self, 'simulation_thread'):
self.simulation_thread.join(timeout=2)
self.server.stop()
self.running = False
logger.info("OPC UA服务器已停止")
def get_node(self, name):
"""获取节点"""
if name in self.nodes:
return self.nodes[name]
return None
def update_variable(self, name, value):
"""更新变量值"""
if name in self.nodes:
self.nodes[name].set_value(value)
logger.debug(f"更新变量 {name} = {value}")
return True
logger.warning(f"变量 {name} 不存在")
return False
def run_simulation(self):
"""运行模拟线程"""
logger.info("启动模拟线程")
temp = 20.0
valve_position = 0.0
flow_rate = 0.0
while self.simulation_active and self.running:
try:
# 温度控制模拟
heating_enabled = self.get_node("HeatingEnabled").get_value()
setpoint = self.get_node("Setpoint").get_value()
if heating_enabled:
self.update_variable("HeatingStatus", True)
if temp < setpoint:
temp += 0.5 # 加快温度上升速度
else:
temp -= 0.1
else:
self.update_variable("HeatingStatus", False)
if temp > 20.0:
temp -= 0.2
# 更新温度
self.update_variable("Temperature", round(temp, 2))
# 阀门控制模拟
valve_control = self.get_node("ValveControl").get_value()
valve_setpoint = self.get_node("ValveSetpoint").get_value()
if valve_control:
if valve_position < valve_setpoint:
valve_position += 5.0 # 加快阀门开启速度
if valve_position > valve_setpoint:
valve_position = valve_setpoint
else:
valve_position -= 1.0
if valve_position < 0:
valve_position = 0
else:
if valve_position > 0:
valve_position -= 5.0
if valve_position < 0:
valve_position = 0
# 更新阀门位置
self.update_variable("ValvePosition", round(valve_position, 2))
# 流量模拟 - 与阀门位置成正比
flow_rate = valve_position * 0.2 # 简单线性关系
self.update_variable("FlowRate", round(flow_rate, 2))
# 更新系统状态
status = []
if heating_enabled:
status.append("Heating")
if valve_control:
status.append("Valve_Open")
if status:
self.update_variable("SystemStatus", "_".join(status))
else:
self.update_variable("SystemStatus", "Idle")
# 每200毫秒更新一次
time.sleep(0.2)
except Exception as e:
logger.error(f"模拟线程错误: {e}")
time.sleep(1) # 出错时稍等一会再继续
logger.info("模拟线程已停止")
def reset_alarm_callback(parent, *args):
"""重置报警的回调函数"""
logger.info("调用了重置报警方法")
return True
def start_process_callback(parent, *args):
"""启动流程的回调函数"""
process_id = args[0] if args else 0
logger.info(f"启动流程 ID: {process_id}")
return process_id
def stop_process_callback(parent, *args):
"""停止流程的回调函数"""
process_id = args[0] if args else 0
logger.info(f"停止流程 ID: {process_id}")
return True
def main():
"""主函数"""
try:
# 创建服务器
server = OpcUaTestServer()
# 添加变量节点 - 温度控制相关
server.add_variable("Temperature", 20.0, ua.VariantType.Float)
server.add_variable("Setpoint", 22.0, ua.VariantType.Float)
server.add_variable("HeatingEnabled", False, ua.VariantType.Boolean)
server.add_variable("HeatingStatus", False, ua.VariantType.Boolean)
# 添加变量节点 - 阀门控制相关
server.add_variable("ValvePosition", 0.0, ua.VariantType.Float)
server.add_variable("ValveSetpoint", 0.0, ua.VariantType.Float)
server.add_variable("ValveControl", False, ua.VariantType.Boolean)
server.add_variable("FlowRate", 0.0, ua.VariantType.Float)
# 其他状态变量
server.add_variable("SystemStatus", "Idle", ua.VariantType.String)
# 添加方法节点
server.add_method(
"ResetAlarm",
reset_alarm_callback,
[],
[("Result", ua.VariantType.Boolean)]
)
server.add_method(
"StartProcess",
start_process_callback,
[("ProcessId", ua.VariantType.Int32)],
[("Result", ua.VariantType.Int32)]
)
server.add_method(
"StopProcess",
stop_process_callback,
[("ProcessId", ua.VariantType.Int32)],
[("Result", ua.VariantType.Boolean)]
)
# 启动服务器
server.start()
logger.info("服务器已启动按Ctrl+C停止")
# 保持服务器运行
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
logger.info("收到键盘中断,正在停止服务器...")
# 停止服务器
server.stop()
except Exception as e:
logger.error(f"服务器错误: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,9 @@
# Default initial positions for full_dev's ros2_control fake system
initial_positions:
arm_base_joint: 0
arm_link_1_joint: 0
arm_link_2_joint: 0
arm_link_3_joint: 0
gripper_base_joint: 0
gripper_right_joint: 0.03

View File

@@ -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:
arm_base_joint:
has_velocity_limits: true
max_velocity: 0
has_acceleration_limits: false
max_acceleration: 0
arm_link_1_joint:
has_velocity_limits: true
max_velocity: 0
has_acceleration_limits: false
max_acceleration: 0
arm_link_2_joint:
has_velocity_limits: true
max_velocity: 0
has_acceleration_limits: false
max_acceleration: 0
arm_link_3_joint:
has_velocity_limits: true
max_velocity: 0
has_acceleration_limits: false
max_acceleration: 0
gripper_base_joint:
has_velocity_limits: true
max_velocity: 0
has_acceleration_limits: false
max_acceleration: 0
gripper_right_joint:
has_velocity_limits: true
max_velocity: 0
has_acceleration_limits: false
max_acceleration: 0

View File

@@ -0,0 +1,4 @@
arm:
kinematics_solver: lma_kinematics_plugin/LMAKinematicsPlugin
kinematics_solver_search_resolution: 0.0050000000000000001
kinematics_solver_timeout: 0.0050000000000000001

View File

@@ -0,0 +1,56 @@
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="benyao_arm_ros2_control" params="device_name mesh_path">
<xacro:property name="initial_positions" value="${load_yaml(mesh_path + '/devices/benyao_arm/config/initial_positions.yaml')['initial_positions']}"/>
<ros2_control name="${device_name}benyao_arm" type="system">
<hardware>
<!-- By default, set up controllers for simulation. This won't work on real hardware -->
<plugin>mock_components/GenericSystem</plugin>
</hardware>
<joint name="${device_name}arm_base_joint">
<command_interface name="position"/>
<state_interface name="position">
<param name="initial_value">${initial_positions['arm_base_joint']}</param>
</state_interface>
<state_interface name="velocity"/>
</joint>
<joint name="${device_name}arm_link_1_joint">
<command_interface name="position"/>
<state_interface name="position">
<param name="initial_value">${initial_positions['arm_link_1_joint']}</param>
</state_interface>
<state_interface name="velocity"/>
</joint>
<joint name="${device_name}arm_link_2_joint">
<command_interface name="position"/>
<state_interface name="position">
<param name="initial_value">${initial_positions['arm_link_2_joint']}</param>
</state_interface>
<state_interface name="velocity"/>
</joint>
<joint name="${device_name}arm_link_3_joint">
<command_interface name="position"/>
<state_interface name="position">
<param name="initial_value">${initial_positions['arm_link_3_joint']}</param>
</state_interface>
<state_interface name="velocity"/>
</joint>
<joint name="${device_name}gripper_base_joint">
<command_interface name="position"/>
<state_interface name="position">
<param name="initial_value">${initial_positions['gripper_base_joint']}</param>
</state_interface>
<state_interface name="velocity"/>
</joint>
<joint name="${device_name}gripper_right_joint">
<command_interface name="position"/>
<state_interface name="position">
<param name="initial_value">${initial_positions['gripper_right_joint']}</param>
</state_interface>
<state_interface name="velocity"/>
</joint>
</ros2_control>
</xacro:macro>
</robot>

View File

@@ -0,0 +1,46 @@
<?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 xmlns:xacro="http://ros.org/wiki/xacro">
<xacro:macro name="benyao_arm_srdf" params="device_name">
<!--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="${device_name}arm">
<chain base_link="${device_name}arm_slideway" tip_link="${device_name}gripper_base"/>
</group>
<group name="${device_name}arm_gripper">
<joint name="${device_name}gripper_right_joint"/>
</group>
<!--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="${device_name}arm_base" link2="${device_name}arm_link_2" reason="Adjacent"/>
<disable_collisions link1="${device_name}arm_base" link2="${device_name}arm_link_1" reason="Adjacent"/>
<disable_collisions link1="${device_name}arm_base" link2="${device_name}arm_link_3" reason="Never"/>
<disable_collisions link1="${device_name}arm_base" link2="${device_name}arm_slideway" reason="Adjacent"/>
<disable_collisions link1="${device_name}arm_link_1" link2="${device_name}arm_link_2" reason="Adjacent"/>
<disable_collisions link1="${device_name}arm_link_1" link2="${device_name}arm_link_3" reason="Never"/>
<disable_collisions link1="${device_name}arm_link_1" link2="${device_name}arm_slideway" reason="Never"/>
<disable_collisions link1="${device_name}arm_link_1" link2="${device_name}gripper_base" reason="Never"/>
<disable_collisions link1="${device_name}arm_link_1" link2="${device_name}gripper_left" reason="Never"/>
<disable_collisions link1="${device_name}arm_link_1" link2="${device_name}gripper_right" reason="Never"/>
<disable_collisions link1="${device_name}arm_link_2" link2="${device_name}arm_link_3" reason="Adjacent"/>
<disable_collisions link1="${device_name}arm_link_2" link2="${device_name}arm_slideway" reason="Never"/>
<disable_collisions link1="${device_name}arm_link_2" link2="${device_name}gripper_base" reason="Never"/>
<disable_collisions link1="${device_name}arm_link_2" link2="${device_name}gripper_left" reason="Never"/>
<disable_collisions link1="${device_name}arm_link_2" link2="${device_name}gripper_right" reason="Never"/>
<disable_collisions link1="${device_name}arm_link_3" link2="${device_name}arm_slideway" reason="Never"/>
<disable_collisions link1="${device_name}arm_link_3" link2="${device_name}gripper_base" reason="Adjacent"/>
<disable_collisions link1="${device_name}arm_link_3" link2="${device_name}gripper_left" reason="Never"/>
<disable_collisions link1="${device_name}arm_link_3" link2="${device_name}gripper_right" reason="Never"/>
<disable_collisions link1="${device_name}arm_slideway" link2="${device_name}gripper_base" reason="Never"/>
<disable_collisions link1="${device_name}arm_slideway" link2="${device_name}gripper_left" reason="Never"/>
<disable_collisions link1="${device_name}arm_slideway" link2="${device_name}gripper_right" reason="Never"/>
<disable_collisions link1="${device_name}gripper_base" link2="${device_name}gripper_left" reason="Adjacent"/>
<disable_collisions link1="${device_name}gripper_base" link2="${device_name}gripper_right" reason="Adjacent"/>
<disable_collisions link1="${device_name}gripper_left" link2="${device_name}gripper_right" reason="Never"/>
</xacro:macro>
</robot>

View File

@@ -0,0 +1,14 @@
{
"arm":
{
"joint_names": [
"arm_base_joint",
"arm_link_1_joint",
"arm_link_2_joint",
"arm_link_3_joint",
"gripper_base_joint"
],
"base_link_name": "device_link",
"end_effector_name": "gripper_base"
}
}

View File

@@ -0,0 +1,29 @@
# MoveIt uses this configuration for controller management
moveit_controller_manager: moveit_simple_controller_manager/MoveItSimpleControllerManager
moveit_simple_controller_manager:
controller_names:
- arm_controller
- gripper_controller
arm_controller:
type: FollowJointTrajectory
action_ns: follow_joint_trajectory
default: true
joints:
- arm_base_joint
- arm_link_1_joint
- arm_link_2_joint
- arm_link_3_joint
- gripper_base_joint
action_ns: follow_joint_trajectory
default: true
gripper_controller:
type: FollowJointTrajectory
action_ns: follow_joint_trajectory
default: true
joints:
- gripper_right_joint
action_ns: follow_joint_trajectory
default: true

View File

@@ -0,0 +1,2 @@
planner_configs:
- ompl_interface/OMPLPlanner

View File

@@ -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

View File

@@ -0,0 +1,39 @@
# This config file is used by ros2_control
controller_manager:
ros__parameters:
update_rate: 100 # Hz
arm_controller:
type: joint_trajectory_controller/JointTrajectoryController
gripper_controller:
type: joint_trajectory_controller/JointTrajectoryController
joint_state_broadcaster:
type: joint_state_broadcaster/JointStateBroadcaster
arm_controller:
ros__parameters:
joints:
- arm_base_joint
- arm_link_1_joint
- arm_link_2_joint
- arm_link_3_joint
- gripper_base_joint
command_interfaces:
- position
state_interfaces:
- position
- velocity
gripper_controller:
ros__parameters:
joints:
- gripper_right_joint
command_interfaces:
- position
state_interfaces:
- position
- velocity

View File

@@ -0,0 +1,44 @@
joint_limits:
arm_base_joint:
effort: 50
velocity: 1.0
lower: 0
upper: 1.5
arm_link_1_joint:
effort: 50
velocity: 1.0
lower: 0
upper: 0.6
arm_link_2_joint:
effort: 50
velocity: 1.0
lower: !degrees -95
upper: !degrees 95
arm_link_3_joint:
effort: 50
velocity: 1.0
lower: !degrees -195
upper: !degrees 195
gripper_base_joint:
effort: 50
velocity: 1.0
lower: !degrees -95
upper: !degrees 95
gripper_right_joint:
effort: 50
velocity: 1.0
lower: 0
upper: 0.03
gripper_left_joint:
effort: 50
velocity: 1.0
lower: 0
upper: 0.03

View File

@@ -0,0 +1,293 @@
<?xml version="1.0" ?>
<robot xmlns:xacro="http://ros.org/wiki/xacro" name="benyao_arm">
<xacro:macro name="benyao_arm" params="mesh_path:='' parent_link:='' station_name:='' device_name:='' 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/benyao_arm/joint_limit.yaml')}"/>
<!-- Extract subsections from yaml dictionaries -->
<xacro:property name= "sec_limits" value="${joint_limit_parameters['joint_limits']}"/>
<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}device_link"/>
<axis xyz="0 0 0"/>
</joint>
<link name="${station_name}${device_name}device_link"/>
<joint name="${station_name}${device_name}device_link_joint" type="fixed">
<origin xyz="0 0 0" rpy="0 0 0" />
<parent link="${station_name}${device_name}device_link"/>
<child link="${station_name}${device_name}arm_slideway"/>
<axis xyz="0 0 0"/>
</joint>
<!-- JOINTS LIMIT PARAMETERS -->
<xacro:property name="limit_arm_base_joint" value="${sec_limits['arm_base_joint']}" />
<xacro:property name="limit_arm_link_1_joint" value="${sec_limits['arm_link_1_joint']}" />
<xacro:property name="limit_arm_link_2_joint" value="${sec_limits['arm_link_2_joint']}" />
<xacro:property name="limit_arm_link_3_joint" value="${sec_limits['arm_link_3_joint']}" />
<xacro:property name="limit_gripper_base_joint" value="${sec_limits['gripper_base_joint']}" />
<xacro:property name="limit_gripper_right_joint" value="${sec_limits['gripper_right_joint']}"/>
<xacro:property name="limit_gripper_left_joint" value="${sec_limits['gripper_left_joint']}" />
<link name="${station_name}${device_name}arm_slideway">
<inertial>
<origin rpy="0 0 0" xyz="-0.913122246354019 -0.00141851388483838 0.0416079172839272"/>
<mass value="13.6578107753627"/>
<inertia ixx="0.0507627640890578" ixy="0.0245166532634714" ixz="-0.0112656803168519" iyy="5.2550852314372" iyz="0.000302974193920367" izz="5.26892263696439"/>
</inertial>
<visual>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/arm_slideway.STL"/>
</geometry>
<material name="">
<color rgba="0.752941176470588 0.752941176470588 0.752941176470588 1"/>
</material>
</visual>
<collision>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/arm_slideway.STL"/>
</geometry>
</collision>
</link>
<joint name="${station_name}${device_name}arm_base_joint" type="prismatic">
<origin rpy="0 0 0" xyz="0.307 0 0.1225"/>
<parent link="${station_name}${device_name}arm_slideway"/>
<child link="${station_name}${device_name}arm_base"/>
<axis xyz="1 0 0"/>
<limit
effort="${limit_arm_base_joint['effort']}"
lower="${limit_arm_base_joint['lower']}"
upper="${limit_arm_base_joint['upper']}"
velocity="${limit_arm_base_joint['velocity']}"/>
</joint>
<link name="${station_name}${device_name}arm_base">
<inertial>
<origin rpy="0 0 0" xyz="1.48458338655733E-06 -0.00831873687136486 0.351728466012153"/>
<mass value="16.1341586205194"/>
<inertia ixx="0.54871651759045" ixy="7.65476367433116E-07" ixz="2.0515139488158E-07" iyy="0.55113098995396" iyz="-5.13261457726806E-07" izz="0.0619081867727048"/>
</inertial>
<visual>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/arm_base.STL"/>
</geometry>
<material name="">
<color rgba="1 1 1 1"/>
</material>
</visual>
<collision>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/arm_base.STL"/>
</geometry>
</collision>
</link>
<link name="${station_name}${device_name}arm_link_1">
<inertial>
<origin rpy="0 0 0" xyz="0 -0.0102223856758559 0.0348505130779933"/>
<mass value="0.828629227096429"/>
<inertia ixx="0.00119703598787112" ixy="-2.46083048832131E-19" ixz="1.43864352731199E-19" iyy="0.00108355785790042" iyz="1.88092240278693E-06" izz="0.00160914803816438"/>
</inertial>
<visual>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/arm_link_1.STL"/>
</geometry>
<material name="">
<color rgba="1 1 1 1"/>
</material>
</visual>
<collision>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/arm_link_1.STL"/>
</geometry>
</collision>
</link>
<joint name="${station_name}${device_name}arm_link_1_joint" type="prismatic">
<origin rpy="0 0 0" xyz="0 0.1249 0.15"/>
<parent link="${station_name}${device_name}arm_base"/>
<child link="${station_name}${device_name}arm_link_1"/>
<axis xyz="0 0 1"/>
<limit
effort="${limit_arm_link_1_joint['effort']}"
lower="${limit_arm_link_1_joint['lower']}"
upper="${limit_arm_link_1_joint['upper']}"
velocity="${limit_arm_link_1_joint['velocity']}"/>
</joint>
<link name="${station_name}${device_name}arm_link_2">
<inertial>
<origin rpy="0 0 0" xyz="-3.33066907387547E-16 0.100000000000003 -0.0325000000000004"/>
<mass value="2.04764861029349"/>
<inertia ixx="0.0150150059448827" ixy="-1.28113733272213E-17" ixz="6.7561418872754E-19" iyy="0.00262980501315445" iyz="7.44451536320152E-18" izz="0.0162030186138787"/>
</inertial>
<visual>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/arm_link_2.STL"/>
</geometry>
<material name="">
<color rgba="1 1 1 1"/>
</material>
</visual>
<collision>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/arm_link_2.STL"/>
</geometry>
</collision>
</link>
<joint name="${station_name}${device_name}arm_link_2_joint" type="revolute">
<origin rpy="0 0 0" xyz="0 0 0"/>
<parent link="${station_name}${device_name}arm_link_1"/>
<child link="${station_name}${device_name}arm_link_2"/>
<axis xyz="0 0 1"/>
<limit
effort="${limit_arm_link_2_joint['effort']}"
lower="${limit_arm_link_2_joint['lower']}"
upper="${limit_arm_link_2_joint['upper']}"
velocity="${limit_arm_link_2_joint['velocity']}"/>
</joint>
<link name="${station_name}${device_name}arm_link_3">
<inertial>
<origin rpy="0 0 0" xyz="4.77395900588817E-15 0.0861257730831348 -0.0227999999999999"/>
<mass value="1.19870202871083"/>
<inertia ixx="0.00780783223764428" ixy="7.26567379579506E-18" ixz="1.02766851352053E-18" iyy="0.00109642607170081" iyz="-9.73775385060067E-18" izz="0.0084997384510058"/>
</inertial>
<visual>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/arm_link_3.STL"/>
</geometry>
<material name="">
<color rgba="1 1 1 1"/>
</material>
</visual>
<collision>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/arm_link_3.STL"/>
</geometry>
</collision>
</link>
<joint name="${station_name}${device_name}arm_link_3_joint" type="revolute">
<origin rpy="0 0 0" xyz="0 0.2 -0.0647"/>
<parent link="${station_name}${device_name}arm_link_2"/>
<child link="${station_name}${device_name}arm_link_3"/>
<axis xyz="0 0 1"/>
<limit
effort="${limit_arm_link_3_joint['effort']}"
lower="${limit_arm_link_3_joint['lower']}"
upper="${limit_arm_link_3_joint['upper']}"
velocity="${limit_arm_link_3_joint['velocity']}"/>
</joint>
<link name="${station_name}${device_name}gripper_base">
<inertial>
<origin rpy="0 0 0" xyz="-6.05365748571618E-05 0.0373027483464434 -0.0264392017534612"/>
<mass value="0.511925198394943"/>
<inertia ixx="0.000640463815051467" ixy="1.08132229596356E-06" ixz="7.165124649009E-07" iyy="0.000552164156414554" iyz="9.80000237347941E-06" izz="0.00103553457812823"/>
</inertial>
<visual>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/gripper_base.STL"/>
</geometry>
<material name="">
<color rgba="1 1 1 1"/>
</material>
</visual>
<collision>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/gripper_base.STL"/>
</geometry>
</collision>
</link>
<joint name="${station_name}${device_name}gripper_base_joint" type="revolute">
<origin rpy="0 0 0" xyz="0 0.2 -0.045"/>
<parent link="${station_name}${device_name}arm_link_3"/>
<child link="${station_name}${device_name}gripper_base"/>
<axis xyz="0 0 1"/>
<limit
effort="${limit_gripper_base_joint['effort']}"
lower="${limit_gripper_base_joint['lower']}"
upper="${limit_gripper_base_joint['upper']}"
velocity="${limit_gripper_base_joint['velocity']}"/>
</joint>
<link name="${station_name}${device_name}gripper_right">
<inertial>
<origin rpy="0 0 0" xyz="0.0340005471193899 0.0339655085140826 -0.0325252119823062"/>
<mass value="0.013337481136229"/>
<inertia ixx="2.02427962974094E-05" ixy="1.78442722292145E-06" ixz="-4.36485961300289E-07" iyy="1.4816483393622E-06" iyz="2.60539468115799E-06" izz="1.96629693098755E-05"/>
</inertial>
<visual>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/gripper_right.STL"/>
</geometry>
<material name="">
<color rgba="1 1 1 1"/>
</material>
</visual>
<collision>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/gripper_right.STL"/>
</geometry>
</collision>
</link>
<joint name="${station_name}${device_name}gripper_right_joint" type="prismatic">
<origin rpy="0 0 0" xyz="0 0.0942 -0.022277"/>
<parent link="${station_name}${device_name}gripper_base"/>
<child link="${station_name}${device_name}gripper_right"/>
<axis xyz="1 0 0"/>
<limit
effort="${limit_gripper_right_joint['effort']}"
lower="${limit_gripper_right_joint['lower']}"
upper="${limit_gripper_right_joint['upper']}"
velocity="${limit_gripper_right_joint['velocity']}"/>
</joint>
<link name="${station_name}${device_name}gripper_left">
<inertial>
<origin rpy="0 3.1416 0" xyz="-0.0340005471193521 0.0339655081029604 -0.0325252119827364"/>
<mass value="0.0133374811362292"/>
<inertia ixx="2.02427962974094E-05" ixy="-1.78442720812615E-06" ixz="4.36485961300305E-07" iyy="1.48164833936224E-06" iyz="2.6053946859901E-06" izz="1.96629693098755E-05"/>
</inertial>
<visual>
<origin rpy="0 3.1416 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/gripper_left.STL"/>
</geometry>
<material name="">
<color rgba="1 1 1 1"/>
</material>
</visual>
<collision>
<origin rpy="0 3.1416 0" xyz="0 0 0"/>
<geometry>
<mesh filename="file://${mesh_path}/devices/benyao_arm/meshes/gripper_left.STL"/>
</geometry>
</collision>
</link>
<joint name="${station_name}${device_name}gripper_left_joint" type="prismatic">
<origin rpy="0 3.1416 0" xyz="0 0.0942 -0.022277"/>
<parent link="${station_name}${device_name}gripper_base"/>
<child link="${station_name}${device_name}gripper_left"/>
<axis xyz="1 0 0"/>
<limit
effort="${limit_gripper_left_joint['effort']}"
lower="${limit_gripper_left_joint['lower']}"
upper="${limit_gripper_left_joint['upper']}"
velocity="${limit_gripper_left_joint['velocity']}"/>
<mimic joint="${station_name}${device_name}gripper_right_joint" multiplier="1" />
</joint>
</xacro:macro>
</robot>

View File

@@ -0,0 +1,12 @@
opcua_example:
class:
action_value_mappings:
module: unilabos.device_comms.opcua_client.client:OpcUaClient
status_types:
type: python
description:
handles: []
icon: ''
init_param_schema: {}