本文档将展示如何为RoboDK添加OPC-UA通信功能。通过OPC UA连接,你可以与支持该协议的PLC及其他设备进行交互。你需要启用RoboDK中的OPC-UA插件,以便为项目添加OPC UA服务器和客户端功能。
RoboDK内置的OPC-UA插件可为项目添加OPC UA兼容性。
默认情况下,RoboDK禁用OPC-UA插件。启用后,该插件将在每次启动RoboDK时显示。
启用步骤:
1.在菜单栏选择:工具➔插件。
2.在列表里找到:OPC-UA,鼠标双击红色圆圈。
此时应会出现包含OPC-UA功能的附加工具栏。
Note:你可以在 RoboDK 插件市场找到更多有关 OPC UA插件的信息:https://robodk.com/addin/com.robodk.plugin.opc-ua。


然后,我们就可以在工具栏看到OPC-UA的图标,菜单栏也会出现OPC-UA选项。

在本示例中,你将学习如何启用OPC UA插件,并将 RoboDK转换为OPC UA服务器。
我们将使用UaExpert软件和Beckhoff TwinCAT3 TF6100设置一些功能。

我们需要在OPC UA插件中配置一些数据,如服务器端口。你还可以选择激活服务器、停用服务器或使用 RoboDK 自动启动服务器。
启用OPC UA插件后,在菜单栏选择OPC UA➔OPC-UA Settings。
OPC UA设置窗口会显示在界面左边,如下图所示:

Tip:你可以更改 OPC UA 服务器的端口,按"Start"按钮更改OPC UA服务器的端口。

如果你看到类似 "RoboDK’s OPC UA server running on port 4840"的消息,则表示RoboDK中的OPC UA服务器已启动。
Note:请确保上一节中配置的 OPC UA 端口已在 Windows 防火墙、反病毒软件或系统要求的任何其他安全设置中启用。
你可以创建一些工作站来测试OPC UA连接情况,工作站的机器人可以一个或多个。

你可以使用UaExpert软件测试与RoboDK OPC UA服务器的连接。
我们可以在该网站下载免费的UaExpert软件:https://www.unified-automation.com/downloads/opc-ua-clients.html 。

启用UaExpert,点击按钮"+",即可添加RoboDK插件OPC UA服务器。

在Add Server窗口,展开Custom Discovery,选择<Double Click to Add Server...> 选项,即可添加RoboDK OPC UA 服务器。

输入上一步配置的OPC UA 服务器URL:opc.tcp://127.0.0.1:48440。

以"None"安全性连接OPC UA服务器。

服务器就配置好了。

现在,你可以在UaExpert连接到RoboDK OPC UA服务器。

创建连接后,你可以看到节点和方法。

RoboDK OPC UA服务器内有一些节点,可传输工作站的一些信息。
RoboDK节点:提供RoboDK软件实际版本的节点。

本案例使用的版本:RoboDK 64 Bit v5.5.3.23031。

SimulationSpeed节点:显示实际仿真速度,并允许用户覆盖当前仿真速度。

节点值参考模拟速度的滑动条。
可以从该节点读取当前的模拟结果,并可以覆盖模拟速度。

Station节点:用户获取RoboDK中当前站名称。

如下所示,Station节点在 RoboDK 中引用了你的工作站名称:New Station(1)。

站参数(StationParameter)和站值(StationValue)是一对设置节点,允许用户获取或设置站内的任何参数。RoboDK OPC UA 服务器将持续监控 "StationParameter "的实际值,并从 "Station Value "节点返回该 "StationParameter "的值。
Note:如果站参数不存在,则返回空字符串。

在RoboDK中右键单击工作站,点击:工作站参数。

在固定参数模块,我们可以看到工作站的默认参数和值。

工作站参数指的是"Parameter"字段,台站值指的是"Value"字段。

在工作站参数窗口,点击按钮:添加,我们就可以创建参数。

这样就添加了一个新的工作站参数。

输入参数名称和值,然后点击应用即可保存。

你还可以获得自己的站点的参数。

time节点:可以获取RoboDK站当前的时间。

返回值是DataTime格式。

会自动更新数据。

RoboDK OPC UA服务器还提供了一些方法,允许用户动态访问RoboDK工作站数据。
只需要右键点击你需要的方法,选择Call…,即可执行该方法。

getItem方法:getItem是一个通过设备名称获取对象指针(内存地址引用)的关键方法,主要用于跨系统交互时精确定位工作站内的设备。

对于InputArguments(输入参数),设备名称是必需的,你可以将设备名称想象为你的站名、机器人名称等。项目 ID 是输出参数,用于返回该设备的指针。

在本例中,我们可以看到,接收到的ABB机器人的项目Item Name:ABB_RB1。

如果项目名称无效或在站内不存在,则返回 0。

getJonits是一种允许用户根据项目 ID 从站台获取机器人关节值的方法。

Item ID 是 Item 的指针值,可以通过 getItem() 方法获取。

我们将通过 "ABB_RB1 "项目名称获取项目 ID,并返回一个 UInt64 值。

在上一个方法中传递项目 ID 时,会返回关节值。

getJointsStr 是一种允许用户根据字符串值获取关节值的方法。

我们可以在此方法中传递机器人名称(字符串)。

在示例工作站中,ABB_RB1 是我的机器人名称。

我们只需在机器人名称参数中输入 "ABB_RB1",然后调用该方法--以字符串格式返回关节值。

setJointsStr 是一个允许用户根据字符串值设置机器人关节值的方法。

在机器人名称中,传递的是 ABB_RB1,我们只需在关节参数中传递一个包含关节值的字符串即可。
举例来说:-0.000000,0.000000,-0.000000,-0.000000,-0.0,-0.000000

你可以使用Beckhoff TwinCAT 3 软件,用来测试与RoboDK OPC UA服务器的连接性。
首先,你需要选择I/O➔Devices➔Add New Item,即可创建OPC UA客户端。

随后OPC➔OK,添加虚拟OPC UA设备。

插入OPC UA客户端模块。

我们需要添加一个 OPC UA 客户端来访问 RoboDK OPC UA 服务器。
选择右键点击Device 1➔Add New Item。

接下来:OPC UA Client(Module)➔OK

完成OPC UA客户端插入。

打开 OPC UA 客户端 > 进入设置选项卡 > 点击 "选择端点 "配置要访问的 OPC UA 服务器端点。
打开OPC UA Client, Settings选项卡➔Select Endpoint。

输入RoboDK OPC UA 服务器的URL,然后点击Update。

点击Add Nodes,可以查看到OPC UA Server内部的节点。

如果已建立TwinCAT与OPC UA服务器之间的连接,则可以看到OPC UA服务器的详细信息。

选择所有方法(紫色标志),然后Ok。

然后方法会插入到你的配置中。

在此字段中配置你的名称前缀。

点击:Create Plc Code,在TwinCAT创建PLC代码。

在项目中创建文件夹:OpcUaClient,并以IEC61131-3功能块格式创建所有RoboDK方法。

本节展示了Beckhoff TwinCAT PLC与RoboDK OPC UA服务器通信的示例程序。
PROGRAM MAIN
VAR
bConnected :BOOL;
StationPointer :DINT;
iStep :INT;
bStart :BOOL;;
i :INT;
TON :TON;
bReset :BOOL;
bWrite :BOOL;
TON2 :TON;
bShow :BOOL:=TRUE;
bVis :BOOL:=True;
END_VAR
VAR
Robot_name :STRING(80):='ABB_RB1';
Item_ID :ULINT;
arrJoints :ARRAY[0..11]OF LREAL;
strJoints :STRING(80):='';
arrJointsFromStr:ARRAY[1..11]OF LREAL;
sSeparator :STRING(1) := ',';
arrJointsCommand:ARRAY[1..11]OF LREAL;
strJointsCommand:STRING(80);
END_VAR
VAR CONSTANT
cStepWaitCmd :INT:=0;
cStepInit :INT:=5;
cStepGetItem :INT:=10;
cStepGetItemReset :INT:=20;
cStepGetItemError :INT:=990;
cStepGetJoints :INT:=30;
cStepGetJointsReset :INT:=40;
cStepGetJointsError :INT:=991;
cStepGetJointsStr :INT:=50;
cStepGetJointsStrReset:INT:=60;
cStepGetJointsStrError:INT:=992;
cStepSetJointStrDelay :INT:=69;
cStepSetJointsStr :INT:=70;
cStepSetJointsStrReset:INT:=80;
cStepSetJointsStrError:INT:=993;
cStepEnd :INT:=300;
cStepWaitReset :INT:=999;
END_VAR
VAR
aSplit :ARRAY[1..11] OF STRING(80);
bResultSplit :BOOL;
debug :BOOL;
URL :STRING:='http://192.168.3.42:8091';
END_VAR
bConnected:=OPCUA_VirtualClient_RoboDK_Station.bConnected;
CASE iStep OF
cStepWaitCmd:
IF bStart THEN
iStep:=cStepInit;
bStart:=FALSE;
END_IF
cStepInit:
StationPointer:=0;
FOR i :=1 TO 11 DO
arrJoints[i]:=0.0;
arrJointsFromStr[i]:=0.0;
aSplit[i]:='';
END_FOR
IF NOT OPCUA_VirtualClient_RoboDK_Station.getItem.bBusy
AND NOT OPCUA_VirtualClient_RoboDK_Station.getItem.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJoints.bBusy
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJoints.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bBusy
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.setJoints.bBusy
AND NOT OPCUA_VirtualClient_RoboDK_Station.setJoints.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bBusy
AND NOT OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bError
THEN
iStep:=cStepGetItem;
END_IF
iStep:=cStepGetItem;
cStepGetItem:
IF OPCUA_VirtualClient_RoboDK_Station.getItem.bDone THEN
iStep:=cStepGetItemReset;
Item_ID:=OPCUA_VirtualClient_RoboDK_Station.getItem.Item_ID;
ELSIF OPCUA_VirtualClient_RoboDK_Station.getItem.bError THEN
iStep:=cStepGetItemError;
END_IF
cStepGetItemReset:
IF NOT OPCUA_VirtualClient_RoboDK_Station.getItem.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.getItem.bBusy
THEN
iStep:=cStepGetJoints;
END_IF
cStepGetJoints:
IF OPCUA_VirtualClient_RoboDK_Station.getJoints.bDone
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJoints.bBusy
THEN
iStep:=cStepGetJointsReset;
ELSIF OPCUA_VirtualClient_RoboDK_Station.getJoints.bError THEN
iStep:=991;
END_IF
cStepGetJointsReset:
IF NOT OPCUA_VirtualClient_RoboDK_Station.getItem.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.getItem.bBusy
THEN
iStep:=cStepGetJointsStr;
END_IF;
cStepGetJointsStr:
IF OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bDone
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bBusy
THEN
iStep:=cStepGetJointsStrReset;
ELSIF OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bError THEN
iStep:=cStepGetJointsStrError;
END_IF
cStepGetJointsStrReset:
IF NOT OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bBusy
THEN
iStep:=cStepSetJointStrDelay;
END_IF;
cStepSetJointStrDelay:
strJointsCommand:=''; strJointsCommand:=CONCAT(LREAL_TO_STRING(arrJointsCommand[1]),strJointsCommand);
strJointsCommand:=CONCAT(strJointsCommand,',');
strJointsCommand:=CONCAT(strJointsCommand,LREAL_TO_STRING(arrJointsCommand[2]));
strJointsCommand:=CONCAT(strJointsCommand,',');
strJointsCommand:=CONCAT(strJointsCommand,LREAL_TO_STRING(arrJointsCommand[3]));
strJointsCommand:=CONCAT(strJointsCommand,',');
strJointsCommand:=CONCAT(strJointsCommand,LREAL_TO_STRING(arrJointsCommand[4]));
strJointsCommand:=CONCAT(strJointsCommand,',');
strJointsCommand:=CONCAT(strJointsCommand,LREAL_TO_STRING(arrJointsCommand[5]));
strJointsCommand:=CONCAT(strJointsCommand,',');
strJointsCommand:=CONCAT(strJointsCommand,LREAL_TO_STRING(arrJointsCommand[6]));
TON2(IN:=TRUE,PT:=T#0.2S);
IF TON2.Q THEN
TON2(IN:=FALSE);
iStep:=cStepSetJointsStr;
END_IF
cStepSetJointsStr:
IF (
OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bDone
AND NOT
OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bBusy
)
OR NOT bWrite
THEN
iStep:=cStepSetJointsStrReset;
ELSIF OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bError
THEN
iStep:=cStepSetJointsStrError;
END_IF
cStepSetJointsStrReset:
bWrite:=FALSE;
OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bExecute:=FALSE;
IF NOT OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bBusy
THEN
iStep:=cStepEnd;
END_IF;
cStepEnd:
TON(IN:=TRUE,PT:=T#0.1S);
IF TON.Q THEN
TON(IN:=FALSE);
IF NOT debug THEN
iStep:=10;
ELSE
iStep:=cStepSetJointStrDelay;
END_IF;
END_IF
cStepGetItemError:
Item_ID:=0;
iStep:=cStepWaitReset;
cStepGetJointsError:
FOR i :=0 TO 11 DO
arrJoints[i]:=-99999.99;
END_FOR
iStep:=cStepWaitReset;
cStepGetJointsStrError:
strJoints:='';
iStep:=cStepWaitReset;
cStepWaitReset:
IF bReset THEN
iStep:=cStepInit;
bReset:=FALSE;
END_IF;
END_CASE
aSplit[1] := strJoints;
FOR i:=1 TO 7 DO
bResultSplit := FindAndSplit(
pSeparator := ADR(sSeparator)
,pSrcString := ADR(aSplit[i])
,pLeftString:= ADR(aSplit[i])
,nLeftSize := SIZEOF(aSplit[i])
,pRightString:= ADR(aSplit[i+1])
,nRightSize := SIZEOF(aSplit[i+1])
,bSearchFromRight := FALSE );
IF NOT bResultSplit THEN
EXIT;
END_IF
END_FOR
FOR i :=1 TO 6 DO
arrJointsFromStr[i]:=STRING_TO_LREAL(aSplit[i]);
END_FOR;
//
OPCUA_VirtualClient_RoboDK_Station.getItem(
bExecute:=iStep=cStepGetItem
,Item_Name:=Robot_name
);
OPCUA_VirtualClient_RoboDK_Station.getJoints(
bExecute:=iStep=cStepGetJoints
,Item_ID:=Item_ID,Joints=>arrJoints
);
OPCUA_VirtualClient_RoboDK_Station.getJointsStr(
bExecute:=iStep=cStepGetJointsStr
,Robot_name:=Robot_name,Joints=>strJoints
);
IF bWrite THEN
OPCUA_VirtualClient_RoboDK_Station.setJointsStr(
bExecute:=TRUE
,Robot_name:=Robot_name,Joints:=strJointsCommand);
END_IF;
本例展示如何在RoboDK中添加OPC-UA客户端链接。RoboDK有一个OPC-UA插件,你可以在RoboDK项目中添加OPC UA。
在本示例中,你将学习如何在RoboDK从外部OPC UA服务器(如PLC、SCADA等)主动获取数据的功能。
Note:你可以右键单击工作站,点击工作站参数并删除它。

在工作站参数的窗口,点击:清除所有。

我们现在创建一个RoboDK项目,配置并启动OPC UA服务器。

本节介绍如何添加OPC UA客户端。
需要你输入端点URL,例如:opc.tcp://127.0.0.1:48441。
你需要将IP地址和端口配置与目标OPC UA服务器相匹配。

点击"连接 ",建立连接。
如果出现"Server variables retrieved. Right Click the station item and select ‘Station parameters’ to see the variables.",则表示已建立连接。

右键单击管理平台,选择:工作站参数。

你可以本文档的节点章节查看关于节点的介绍。

通过RoboDK中的OPC UA 客户端,从 OPC UA服务器获取节点数据后,还可以使用 RoboDK-Python-API 获取这些数据。
你可以点击此链接安装 RoboDK PythonAPI。
https://robodk.com/doc/cn/PythonAPI/intro.html#how-to-install
或者在Python中下载RoboDK包,在终端输入:
pip install robodk
本示例脚本展示了如何通过RoboDK 的 Python API 获取站参数。
from robodk import robolink # RoboDK API
RDK = robolink.Robolink()
from robodk import * # RoboDK API
from robolink import * # Robot toolbox
itemlist = RDK.ItemList()
if itemlist:
# Get all Station Parameters
print('Vaild Paramaters are configurated in your Station..')
StationParameters=RDK.getParams()
for StationParameter in StationParameters:
print("Station Parameters %s : %s"%(StationParameter[0],str((StationParameter[1]))))
else:
print('No Parameter list..')
下面是示例脚本的结果:
Vaild Paramaters are configurated in your Station..
Station Parameters RoboDK : RoboDK 64 bit v5.5.3.23031
Station Parameters time : 02/14/2023 03:58:29.191.000.000
Station Parameters SimulationSpeed : 13.8551
Station Parameters Station : MyTestStation