借助PLC-Recorder,倍福(Beckhoff)TC3 PLC高速采集的方法(2ms,带时间戳采集)
一、案例介绍
高速数据采集要保证速度,也要保证时刻的准确性。在Windows系统里,时间稳定性是个很难的问题。如果PLC发送的数据里带有时间信息,则可以由PLC来保证采样周期的稳定性。
从V2.12版本开始,PLC-Recorder软件可以处理发送电文里的时间戳(并有时钟校准等高级功能),有网友用0.24ms的速度外发,软件也能够稳定接收并精确确定数据的时刻。
本文向大家展示一下倍福TC3 PLC通过UDP快速通信的实现方法。
二、准备工作
1.设置
1)通信协议:UDP
2)编程软件和系统:TcXaeShell
3)PLC的IP地址:192.168.20.17
4)PLC-Recorder所在电脑的IP地址:192.168.20.20。
2.说明
PLC的主任务设置成1ms的循环(最小可以设置为0.25ms,实际周期与平台有关,本文是用软PLC测试,实际周期是1ms左右,所以此处将周期设置为1ms)。

三、测试说明
![image]本次测试采用了连续循环,每两个周期发送一次(上升沿触发)。理论发送周期:2ms。
四、PLC侧配置
1.报文数据结构
使用联合体而不是普通结构,是为了解决字节对齐的问题,详见文章:《以汇川中型PLC(AM系列)为例,CODESYS平台变量与字节数组互转的多种方法》
TYPE union_real :
UNION
Value:REAL:=-3.1;
Bytes:ARRAY[0..3] OF BYTE;
END_UNION
END_TYPE
TYPE union_udint :
UNION
Value:UDINT;
Bytes:ARRAY[0..3] OF BYTE;
END_UNION
END_TYPE
TYPE union_uint :
UNION
Value:UINT;
Bytes:ARRAY[0..1] OF BYTE;
END_UNION
END_TYPE
TYPE union_int :
UNION
Value:INT:=50;
Bytes:ARRAY[0..1] OF BYTE;
END_UNION
END_TYPE
TYPE DUT_MSG :
STRUCT
stamp:union_udint;//时间戳,单位是微秒 偏移=0
seq:union_uint;//电文序号 偏移=4
sysWord:union_int;// 偏移=6
D_A:union_int;// 偏移=8
D_B:union_udint;// 偏移=10
D_C:union_real;// 偏移=14
D_D:union_int;// 偏移=18, length=20byte
END_STRUCT
END_TYPE
2.主程序的局部变量
VAR
fbSocketUdpCreate : FB_SocketUdpCreate;(*UDP建立连接的功能块*)
fbSocketUdpSendTo : FB_SocketUdpSendTo;(* UDP发送功能块*)
bExecute : BOOL;(* UDP建立连接的执行位*)
bSocketConnectBusy : BOOL;
hSocket : T_HSOCKET;(*UDP的句柄*)
bSocketSendbBusy : BOOL;
localtime: INT;
cyclecounter: INT;
sendPluse: BOOL;
bSend1: BOOL:=TRUE;//启动发送
arrSendData: DUT_MSG;
sendBuffer:ARRAY[0..199] OF BYTE;
pArray:UINT;
i: UINT;
fbCpuCounter : FW_GetCpuCounter;
lowTime : UDINT;
highTime : UDINT;
bFirstScanDone : BOOL := FALSE;
bIsFirstScan : BOOL;
startStamp:UDINT;
timeNowULIntStart:UDINT;
timeNowULIntDif:UDINT;
timeNowULIntDifOld: UDINT;
END_VAR
3.PLC程序
cyclecounter:=cyclecounter+1;
IF
cyclecounter>1 AND bSend1 //每2个周期发送一次报文。bSend1是启动变量,如果需要上电自动启动通讯,则初始值设置为TRUE
THEN
sendPluse:=TRUE;
cyclecounter:=0;
bExecute:=TRUE;//启动通讯
ELSE
sendPluse:=FALSE;
END_IF
arrSendData.D_A.Value:=arrSendData.D_A.Value+1; //数据循环累加
bIsFirstScan := NOT bFirstScanDone; // 首次扫描时为 TRUE
bFirstScanDone := TRUE; // 第一次执行后永久置为 TRUE
IF bIsFirstScan THEN
STARTSTAMP:=Lowtime;
// 第一个扫描周期执行的代码
END_IF
fbCpuCounter(
dwCpuCntLo => lowTime, // 低 32 位
dwCpuCntHi => highTime // 高 32 位
);
IF sendPluse THEN
arrSendData.seq.Value:=arrSendData.seq.Value+1;//电文序号+1
timeNowULIntDif:=(lowTime-STARTSTAMP)/10;//时间戳单位转换成微秒,并计算出时间偏差
arrSendData.stamp.Value:=timeNowULIntDif;
arrSendData.D_C.Value:=UDINT_TO_REAL(timeNowULIntDif-timeNowULIntDifOld)/1000;
timeNowULIntDifOld:=timeNowULIntDif;
END_IF
arrSendData.D_D.Value:=-50;
//发送缓冲区生成
pArray:=0;
FOR i:=0 TO SIZEOF(arrSendData.stamp.Bytes)-1 BY 1 DO
sendBuffer[pArray]:=arrSendData.stamp.Bytes[i];
pArray:=pArray+1;
END_FOR
FOR i:=0 TO SIZEOF(arrSendData.seq.Bytes)-1 BY 1 DO
sendBuffer[pArray]:=arrSendData.seq.Bytes[i];
pArray:=pArray+1;
END_FOR
FOR i:=0 TO SIZEOF(arrSendData.sysWord.Bytes)-1 BY 1 DO
sendBuffer[pArray]:=arrSendData.sysWord.Bytes[i];
pArray:=pArray+1;
END_FOR
FOR i:=0 TO SIZEOF(arrSendData.D_A.Bytes)-1 BY 1 DO
sendBuffer[pArray]:=arrSendData.D_A.Bytes[i];
pArray:=pArray+1;
END_FOR
FOR i:=0 TO SIZEOF(arrSendData.D_B.Bytes)-1 BY 1 DO
sendBuffer[pArray]:=arrSendData.D_B.Bytes[i];
pArray:=pArray+1;
END_FOR
FOR i:=0 TO SIZEOF(arrSendData.D_C.Bytes)-1 BY 1 DO
sendBuffer[pArray]:=arrSendData.D_C.Bytes[i];
pArray:=pArray+1;
END_FOR
FOR i:=0 TO SIZEOF(arrSendData.D_D.Bytes)-1 BY 1 DO
sendBuffer[pArray]:=arrSendData.D_D.Bytes[i];
pArray:=pArray+1;
END_FOR
fbSocketUdpCreate(
sSrvNetId:= ,
sLocalHost:='192.168.0.99' ,
nLocalPort:=5011,
bExecute:= bExecute,
tTimeout:= ,
bBusy=> bSocketConnectBusy,
bError=> ,
nErrId=> ,
hSocket=>hSocket );
fbSocketUdpSendTo(
sSrvNetId:= ,
hSocket:=hSocket ,
sRemoteHost:='192.168.0.230' , //PLC-Recorder所在的电脑IP地址。
nRemotePort:=5011 ,
cbLen:=pArray, //发送的长度
pSrc:=ADR (sendBuffer) ,
bExecute:=sendpluse,
tTimeout:= ,
bBusy=>bSocketSendbBusy ,
bError=> ,
nErrId=> );
4.时间戳计算
读取PLC的CPU脉冲计数,单位是0.1微秒,转换成微秒。
定义:
fbCpuCounter : FW_GetCpuCounter;
计算:
fbCpuCounter(
dwCpuCntLo => lowTime, // 低 32 位
dwCpuCntHi => highTime // 高 32 位
);
IF sendPluse THEN
arrSendData.seq.Value:=arrSendData.seq.Value+1;//电文序号+1
timeNowULIntDif:=(lowTime-STARTSTAMP)/10;//时间戳单位转换成微秒,并计算出时间偏差
arrSendData.stamp.Value:=timeNowULIntDif;
arrSendData.D_C.Value:=UDINT_TO_REAL(timeNowULIntDif-timeNowULIntDifOld)/1000;
timeNowULIntDifOld:=timeNowULIntDif;
END_IF
四、PLC-Recorder侧配置
1.添加通道及配置
1)添加收听模式通道
2)配置来源的IP地址及本机收听的端口号。
3)时间戳单位为us。
4)修正系数默认参数。

2.连接参数
此处虽然配置了采集周期,但在高速模式下不再使用该周期,以收到信息的时刻为准。

3、变量配置
UDP报文结构简单,只需要定义自己需要提取的变量即可。起始地址为0的变量就是PLC里定义的时间戳。第二个变量是电文序号。(这是个示例,与后续的分析文件稍有不同)。

五、使用Ana分析采集数据
1.启用时间戳的采集
发现在启用时间戳功能时,实际采集周期是2.9ms,电文序号有波动。

2.未启用时间戳的采集
而不启用时间戳功能时,实际采集周期是2.1ms,此处的stamp变量是两个扫描周期之间的时间戳差值(专门用于分析的,不是上面给出的程序),可以看出时间戳波动比较大,最小0.04ms,最大1.744ms,平均是1.01ms,因此,这个软PLC里时钟是不稳定的。

3.声明
(此处仅为测试系统的表现,不代表倍福实际产品的状态)。
六、小结
采集的速度越快,需要的技术越复杂,代价也越大。PLC-Recorder的主动采集模式,基本只需要在PLC-Recorder里配置变量,不需要在PLC做什么复杂工作,这种方式很便捷,但是速度也有限(最快8ms)。高速模式,就需要在PLC里配置通信,组织数据,然后调用通信语句,才能够实现,因此,对于PLC工程师也有了一点要求。
高速模式采用标准以太网通信协议,这也是大部分PLC都具备的能力,因此,可以被广泛用于快速数据采集。



浙公网安备 33010602011771号