mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-06 23:15:10 +00:00
初始提交,只保留工作区当前状态
This commit is contained in:
98
opcua_config.json
Normal file
98
opcua_config.json
Normal 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": [
|
||||
"完整控制流程"
|
||||
]
|
||||
}
|
||||
41
recipes/ros-humble-unilabos-msgs/bld_ament_cmake.bat
Normal file
41
recipes/ros-humble-unilabos-msgs/bld_ament_cmake.bat
Normal 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
|
||||
71
recipes/ros-humble-unilabos-msgs/build_ament_cmake.sh
Normal file
71
recipes/ros-humble-unilabos-msgs/build_ament_cmake.sh
Normal 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
|
||||
61
recipes/ros-humble-unilabos-msgs/recipe.yaml
Normal file
61
recipes/ros-humble-unilabos-msgs/recipe.yaml
Normal 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') }}
|
||||
19
test/experiments/opcua_example.json
Normal file
19
test/experiments/opcua_example.json
Normal 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": {
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
19
unilabos/device_comms/opcua_client/README.md
Normal file
19
unilabos/device_comms/opcua_client/README.md
Normal 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定义工作流文件
|
||||
|
||||
12
unilabos/device_comms/opcua_client/__init__.py
Normal file
12
unilabos/device_comms/opcua_client/__init__.py
Normal 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',
|
||||
]
|
||||
1380
unilabos/device_comms/opcua_client/client.py
Normal file
1380
unilabos/device_comms/opcua_client/client.py
Normal file
File diff suppressed because it is too large
Load Diff
10
unilabos/device_comms/opcua_client/node/__init__.py
Normal file
10
unilabos/device_comms/opcua_client/node/__init__.py
Normal 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',
|
||||
]
|
||||
180
unilabos/device_comms/opcua_client/node/uniopcua.py
Normal file
180
unilabos/device_comms/opcua_client/node/uniopcua.py
Normal 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
|
||||
@@ -0,0 +1,2 @@
|
||||
Name,EnglishName,NodeType,DataType,NodeLanguage
|
||||
中文名,EnglishName,VARIABLE,INT32,Chinese
|
||||
|
@@ -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}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
311
unilabos/device_comms/opcua_client/server.py
Normal file
311
unilabos/device_comms/opcua_client/server.py
Normal 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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
arm:
|
||||
kinematics_solver: lma_kinematics_plugin/LMAKinematicsPlugin
|
||||
kinematics_solver_search_resolution: 0.0050000000000000001
|
||||
kinematics_solver_timeout: 0.0050000000000000001
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
planner_configs:
|
||||
- ompl_interface/OMPLPlanner
|
||||
@@ -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,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
|
||||
44
unilabos/device_mesh/devices/benyao_arm/joint_limit.yaml
Normal file
44
unilabos/device_mesh/devices/benyao_arm/joint_limit.yaml
Normal 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
|
||||
293
unilabos/device_mesh/devices/benyao_arm/macro_device.xacro
Normal file
293
unilabos/device_mesh/devices/benyao_arm/macro_device.xacro
Normal 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>
|
||||
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/arm_base.STL
Normal file
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/arm_base.STL
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/arm_link_1.STL
Normal file
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/arm_link_1.STL
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/arm_link_2.STL
Normal file
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/arm_link_2.STL
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/arm_link_3.STL
Normal file
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/arm_link_3.STL
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/arm_slideway.STL
Normal file
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/arm_slideway.STL
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/gripper_base.STL
Normal file
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/gripper_base.STL
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/gripper_left.STL
Normal file
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/gripper_left.STL
Normal file
Binary file not shown.
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/gripper_right.STL
Normal file
BIN
unilabos/device_mesh/devices/benyao_arm/meshes/gripper_right.STL
Normal file
Binary file not shown.
12
unilabos/registry/devices/opcua_example.yaml
Normal file
12
unilabos/registry/devices/opcua_example.yaml
Normal 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: {}
|
||||
Reference in New Issue
Block a user