☰
ABB机器人与PLC通过PROFINET总线交换浮点数的原理与RAPID实现
2026/10/5 5:01:28 网站建设 项目流程

做ABB机器人集成项目的时候,我几乎每次都会被问到同一个问题:机器人要和PLC通过PROFINET(PN总线)交换数据,而且传的不是简单的0/1信号,是带小数点的浮点数,比如视觉系统算出来的坐标偏移、焊接电流反馈、伺服压力值。刚开始做这类通讯时,我也踩过不少坑,尤其是浮点数在总线上怎么拆、怎么拼、字节序怎么处理,搞不明白的话,数据传过去全是乱码。

这篇文章就把我自己在几个项目里总结下来的经验整理一遍,重点讲清楚ABB机器人通过PN总线收发浮点数的原理、配置步骤和RAPID代码实现。无论你是刚接触机器人通讯的新手,还是已经在现场调过几个项目的工程师,只要是做PLC和ABB机器人数据交互的,这篇文章应该能帮你少走不少弯路。

1. 浮点数上PN总线的需求从哪来:先想清楚要传什么

1.1 现场最常见的三种浮点数据交换场景

先说场景。很多朋友一上来就找代码、找配置方法,但我觉得第一步应该是想清楚项目里到底要传哪些数据。我做过的大多数项目里,浮点数交互无非是三类。

第一类是视觉引导类。相机检测到工件的位置偏差,PLC拿到结果后要把X、Y、Rz这几个浮点数值下发给机器人,机器人根据偏移量做抓取或装配。这类数据的特点是更新频率高、实时性强,而且数值带有正负号,传错了整个动作都会偏掉。

第二类是工艺参数类。比如点焊项目中,PLC需要实时给机器人下发焊接压力设定值,机器人则要把实际压力反馈上传给PLC。这类数据的特点是范围大、精度要求高,可能从零点几到几十甚至上百,普通整数根本没法满足需求。

第三类是运行状态监控类。机器人把TCP末端速度、当前位置坐标、六个轴的实时角度等浮点数据周期性传给PLC,用于产线监控和数据分析。这类数据通常量比较大,可能一帧就是好几十个字节。

这三种场景如果靠传统IO点一个一个bit去传,不仅接线量大,而且传输精度完全没法保证。PN总线正好解决了这个问题,一根网线搞定,周期刷新,实时性也够。

1.2 为什么不建议用DI/DO拼数或模拟量通讯

有的老工程师习惯用DI/DO拼二进制数来传数据,或者用模拟量模块。这两种方式也不是完全不能用,但都有明显的短板。

用DI/DO拼数,比如16个bit拼一个无符号整数,接线要16根,占了16个IO点,传一个值都费劲,要传三四个浮点数,IO资源直接爆炸。而且全是0/1的组合,现场查错非常痛苦。用模拟量的话,常见的是4到20mA或者0到10V,首先要选匹配的模拟量模块,其次模拟量本身精度一般只有12位左右,换算成数值大约万分之几,对于位置补偿这种要求较高的场景远远不够,还容易受现场干扰。

我在一个老产线改造项目里就吃过这个亏。原来设备用模拟量传速度给定值,现场变频器一启动,模拟量信号就波动,机器人速度也跟着抖。后来改成PN总线直接传浮点速度值,问题马上消失。所以如果你所在的项目条件允许,能用现场总线传浮点数,就不要折腾拼bit和模拟量了。

2. 难的不是总线,是浮点数本身:字节、字序与IEEE 754

2.1 一个浮点数在内存里到底是什么样子

很多PLC程序写得溜的同行,一提到浮点数的二进制表示就有点发怵。其实道理不复杂。工业总线上的浮点数遵循IEEE 754标准,单精度浮点数用32位(4个字节)表示,双精度用64位(8个字节)。我们最常用的是单精度,在西门子PLC里叫REAL,在C语言里叫float,在ABB机器人端就对应一个32位的浮点数。

单精度浮点数的32位是这样分配的:最高1位是符号位,接下来8位是指数位,剩下23位是尾数位。符号位为0表示正数,为1表示负数。指数位用偏移方式编码,尾数位存储的是规格化后的小数部分。

给你几个具体例子,最好背下来,调试时很有用:

  • 十进制的1.0,十六进制表示是0x3F800000
  • 十进制的-1.0,十六进制表示是0xBF800000
  • 十进制的2.0,十六进制表示是0x40000000
  • 十进制的3.14,十六进制表示是0x4048F5C3

刚才这几个值我用在线浮点转十六进制工具验证过很多遍。实际调试时这些值就是“锚点”,一旦通讯数据不对,先发一个1.0过去,看机器人端收到的字节是不是0x3F800000,马上能定位问题出在哪个环节。这种排查方法比对着乱码猜要高效得多。

之所以要说清楚这些,是因为浮点数在PN总线上的传输,本质上就是把4个字节按顺序从一个设备搬到另一个设备。只要搞懂了这4个字节的排列顺序,后面所有代码都是水到渠成的事。

2.2 PN总线的周期IO机制与字(Word)映射

PROFINET(PN总线)在传输实时数据时,用的是周期IO机制。PLC作为IO控制器,机器人侧或第三方模块作为IO设备,双方按设定好的周期(比如4ms、8ms、16ms)交换数据。每个周期里,控制器下发输出数据,设备上传输入数据,数据以字节或字为基本单位。

ABB机器人的I/O系统里,信号类型分为数字量信号DI/DO、组信号GI/GO,还有模拟量信号AI/AO。组信号GI/GO可以自定义位长,最大支持到32位,这一点非常关键。一个32位的组信号正好对应一个单精度浮点数。所以从理论上讲,你可以直接定义一个32位的Group输入信号,把PLC下发的4个字节当作一个整体读进来,然后通过RAPID里的字节转换函数把它变成浮点数。

不过实际项目中我更推荐用两个16位的组信号来承接一个浮点数,原因后面仔细说,这跟字节序和模块的地址映射方式有很大关系。很多PROFINET从站模块在映射时是以字为单位的,一个REAL正好占用两个字,用两个16位信号来对应,逻辑更清晰,排查地址也方便。

2.3 字节序问题:高低字顺序搞反是最大的坑

我在标题里就说了,浮点数收发真正难的不是代码,是字节序。字节序这个问题,现场十个调试人员有八个都栽在上面。

工业网络里普遍采用大端字节序,即高位字节在前。比如浮点数1.0的十六进制是0x3F800000,按大端顺序发送,4个字节依次是3F 80 00 00。PLC内部通常也是这么存储的。但问题出在接收端怎么把这些字节重新拼起来。

ABB机器人的组信号是按字读取的。如果两个16位字分别存储,你读取到的第一个字可能是0x3F80,第二个字是0x0000,你把它们按“低字在前”的方式拼接,就变成了0x00003F80,这显然不是0x3F800000,转换出来的浮点数完全是错的。反过来,如果第一个字读出来是0x0000,第二个是0x3F80,那就要按“高字在前”来拼。

还有更复杂的情况,就是模块内部做了字节交换,读出来的字不是0x3F80而是0x803F。这种时候光交换高低字还不够,还要交换字内部的字节。

所以调试时第一件事不是急着写业务逻辑,而是先确认字节序。我的固定做法是:PLC给机器人发一个1.0,机器人在RAPID里把收到的两个16位字转成十六进制打印出来,看看到底是3F 80 00 00、00 00 3F 80、80 3F 00 00还是00 00 80 3F。确认了规则,再写转换逻辑,一次就能过。

3. 从零开始配置:RobotStudio里的PN从站和信号映射

3.1 第1步:确认硬件选项和PROFINET功能是否激活

ABB机器人的控制器要支持PROFINET通讯,首先得在硬件选项中包含PROFINET相关配置。我们在RobotStudio里连接控制器后,可以在控制器的System Configuration里查看已安装的选项。如果发现没有PROFINET相关选项,需要在启动界面里选择对应的RobotWare版本并添加选项,然后重启控制器。

这个操作必须在控制器断电或者热启动前完成,选项添加后一般会要求重启系统。有次我在现场忘了重启就直接在I/O System里找PN网络,找半天没找到,白白浪费了半个小时。后来学乖了,每次加完选项先重启控制器,再继续后续配置。

3.2 第2步:添加PROFINET网络和设备,分配设备名

重启完成后,在RobotStudio左侧导航栏找到“配置”里的“I/O System”,右键“Network”新建网络,类型选择PROFINET。新建之后,需要在网络下面添加具体的IO设备。

如果ABB机器人作为IO设备(也就是Device)接入PLC控制的PROFINET网络,那你需要在PLC侧导入ABB机器人提供的GSDML文件,然后在PLC组态软件里添加这个设备。这个时候,机器人侧的PROFINET网络上不需要再挂别的设备,只需要配置好自身的设备标识。

如果ABB机器人作为IO控制器(Controller),主动去连接第三方IO模块或远程站,这个场景也很常见。比如机器人要读一个带PN接口的传感器模块,那就在RobotStudio的网络里添加设备,导入对应的GSDML文件。导入后需要分配Device Name,这个名字必须和实际硬件的设备名完全一致,包括大小写。

提一句痛点:PROFINET识别设备靠的不是IP地址,而是设备名。很多工程师习惯了IP寻址,把IP配好了就以为完事了,结果设备一直报故障,最后发现是Device Name少了个字母。我刚接触时也犯过这毛病,从那时起养成了习惯,每次配置完设备名都用PROFINET的DCP工具扫描一遍,确认和组态一致再往下走。

3.3 第3步:定义Group Input/Output信号和地址映射

设备添加好后,接下来要在“Signal”里定义机器人侧用到的信号。我通常的做法是给每个浮点数分配两个16位的组信号,并备注好它们对应的是同一个浮点数的高字和低字。

比如PLC要下发一个速度设定值给机器人,我会在RobotStudio中定义两个Group Input信号,一个叫GI_Speed_High,一个叫GI_Speed_Low。地址映射就看模块的输入起始地址,假设起始地址是0,那GI_Speed_Low放在地址0、位宽16,GI_Speed_High放在地址16、位宽16。

这里有个细节需要注意:不同厂家模块的地址排列顺序不一样。有的模块从0开始是低字,有的模块从0开始是高字,这直接决定了后面代码里谁是高字谁是低字。所以定义信号的时候不要想当然,要从模块手册或者PLC侧组态软件里确认。

输出方向同理。机器人要上传一个实际压力值给PLC,就定义两个Group Output信号,分别对应输出地址中的高字和低字,名字叫GO_Pressure_High和GO_Pressure_Low。

信号定义好之后,在RobotStudio里可以先把“仿真”或“I/O监控”打开,手动给信号赋值,看信号能否正常在设备间传递。这一步能过滤掉一大半配置问题。

3.4 第4步:PLC侧的组态与地址确认

虽然这篇文章主要讲ABB机器人侧,但PLC侧配置也得交代几句。以西门子S7-1200/1500为例,在TIA Portal里添加ABB机器人或第三方PN从站的GSDML文件后,需要给设备分配一个设备名称,这个名称要和RobotStudio里配置的完全一致。然后查看模块的IO起始地址,比如I地址从68开始,Q地址从68开始,再回到RobotStudio里核对映射地址。

PLC侧发浮点数就简单了,直接在程序里往对应地址写一个REAL变量就行。但要注意:如果PN从站模块带有字节交换功能,有些模块默认是把数据按小端排列的,这时在PLC侧和机器人侧都要做相应调整。我的建议是尽量不用模块自带的交换功能,而是在机器人RAPID代码里统一处理,这样逻辑更清晰,换一个现场也能快速适配。

4. RAPID代码实现:四个字节到浮点数的互相转换

4.1 发送方向:把浮点数拆成两个16位字写到输出信号

当机器人要把一个计算结果上传给PLC时,需要把浮点数拆成4个字节,再组合成两个16位字,分别写到两个Group Output信号上。ABB的RAPID语言里提供了PackRawBytes和UnpackRawBytes这两个非常有用的函数,专门用来做字节流的打包和解包。

上传方向的代码可以这样写:

PROC SendRealToPLC() VAR num value_to_send; VAR rawbytes packet{4}; VAR num low_word; VAR num high_word; value_to_send := 3.14; ! 把浮点数打包成4个字节 PackRawBytes value_to_send, packet, 1 \Float4; ! 取出前两个字节合成低16位字 low_word := packet{1} + packet{2} * 256; ! 取出后两个字节合成高16位字 high_word := packet{3} + packet{4} * 256; SetGO GO_Pressure_Low, low_word; SetGO GO_Pressure_High, high_word; ENDPROC

这段代码的关键在PackRawBytes。第一个参数是待打包的浮点数,第二个参数是长度为4的rawbytes数组,第三个参数1表示从数组第1个字节开始写,\Float4告诉系统我要打包的是32位单精度浮点。

为什么用packet{1} + packet{2} * 256来合成字?因为rawbytes里每个字节都是0到255之间的数值,第2个字节的值乘以256再和第1个字节相加,就得到了低16位对应的无符号整数值。比如第1个字节是0x3F(十进制63),第2个字节是0x80(十进制128),那么低字就是63 + 128 * 256 = 32831,十六进制正好是0x803F。

这里也再次强调:如果你确认收到的数据是高字在前,那发送方向也要对应调换,把packet{1}和packet{2}放到高字,packet{3}和packet{4}放到低字。一切以现场实测的字节序为准。

4.2 接收方向:把两个16位组信号拼成一个浮点数

接收方向是反过来的。PLC把REAL拆成两个16位字发送,机器人从组信号里读取这两个字,合成4个字节,再用UnpackRawBytes还原成浮点数。

FUNC num RecvRealFromPLC() VAR rawbytes packet{4}; VAR num low_word; VAR num high_word; VAR num recv_value; low_word := GetGO(GI_Speed_Low); high_word := GetGO(GI_Speed_High); ! 低16位字拆成两个字节 packet{1} := low_word MOD 256; packet{2} := (low_word - packet{1}) / 256; ! 高16位字拆成两个字节 packet{3} := high_word MOD 256; packet{4} := (high_word - packet{3}) / 256; ! 4个字节解包成浮点数 UnpackRawBytes packet, 1, recv_value \Float4; RETURN recv_value; ENDFUNC

这段代码里,GetGO返回的是组信号当前的值,范围是0到65535。用MOD 256取余数得到低字节,减去低字节后除以256得到高字节,这是字节拆分的通用写法。如果你不想用数学运算,RAPID里也有LowByte和HighByte函数可以直接提取字节,效果一样,看个人习惯。

无论用哪种方式,只要PLC下发的是1.0,并且机器人侧收到的两个字恰好是0x0000和0x3F80,那么UnpackRawBytes还原后 recv_value 就应该等于1.0。如果还原出来是个天文数字或者完全不对,十有八九是高低字顺序搞反了。

4.3 高低字和字节序反了怎么办:两种常见变体写法

现场调试时最典型的两个问题就是高字低字相反,以及字内字节顺序相反。针对这两种情况,代码只需要做小调整。

如果是高字低字相反,也就是你实际应该先把high_word拆成前两个字节,再把low_word拆成后两个字节,那接收代码只需要调整packet的填充顺序:

packet{1} := high_word MOD 256; packet{2} := (high_word - packet{1}) / 256; packet{3} := low_word MOD 256; packet{4} := (low_word - packet{3}) / 256;

如果是字内字节序也反了,比如第一个字读出来是0x803F而不是0x3F80,那就需要把每个字内部的字节再做交换。这种时候我建议直接调整原始字节数组的读取顺序,不要绕来绕去:

! 假设 word1 实际是 0x803F,低字节是0x80,高字节是0x3F packet{1} := (word1 - word1 MOD 256) / 256; packet{2} := word1 MOD 256;

这类问题没有标准答案,完全取决于现场设备的字节序规则。所以我才反复强调,不要猜,先用1.0做验证实验。把验证串口打印写成一个例行程序,现场直接调用,几秒钟就能确认所有排列规则。

4.4 直接用32位组信号的做法与潜在隐患

前面提到过可以定义一个32位的组信号来容纳一个浮点数,这样在RAPID代码里会更紧凑:

VAR num raw_value; raw_value := GetGO(GI_Speed_32bit);

然后通过位运算或者UnpackRawBytes把raw_value当作4字节数据解析。理论上这样也能实现,但我在实际项目中用得不多,原因有两个。

第一,不同厂家PROFINET模块对32位组信号的支持并不一致,有的模块在地址映射时无法把连续两个字直接合并成32位信号,需要额外配置。第二,32位组信号读出来的num在RAPID内部实际上是浮点数,如果你要把这个值当作无符号32位整数去做位运算,数值超过2的31次方后行为可能不符合预期。相比之下,两个16位信号加手动拼接,每一步都可控、可打印、可调试,看起来多写几行代码,但排错的时候省时间省到飞起。

5. 现场调试实录与常见故障排查

5.1 用1.0这个“魔数”做字节序验证的标准流程

每次新接一个项目,无论之前做过多少次,我都会在联调前先做一次字节序验证。流程固定,大概是这么几步。

第一步,PLC侧给机器人发送一个1.0,发送地址和机器人侧定义的输入信号对应。第二步,机器人侧写一个临时测试程序,把GetGO读到的两个16位字打印到示教器上,同时打印把这两个字按不同顺序拼接后解包出来的浮点数值。第三步,根据打印结果确定字节序规则。

比如PLC给1.0,机器人读到两个字分别是0x0000和0x3F80。如果代码先填low_word再填high_word,解包出来就是1.0,那说明模块的地址0出口是低字,地址16出口是高字,当前代码不用改。如果解包出来不是1.0,就把高字低字的填充顺序调换再试一次。

这个流程快的话10分钟就能完成,但能避免后面一整天在错误的数据里挣扎。我见过有同事不验证,直接拿视觉坐标去现场调,发现机器人抓偏了才开始怀疑数据不对,最后排查了整整一天,原来是高字低字反了。

5.2 通信中断、数据跳变和默认值处理

PN总线通讯在正常工作时非常稳定,但一旦出现从站掉线、网线松动或者PLC程序停机,情况就变得很棘手。关键问题在于,设备掉线时机器人端的输入信号会保持在掉线前一瞬间的值,而不是自动归零。这意味着机器人可能会继续按照过时的数据执行动作,非常危险。

所以我的代码里凡是涉及外部浮点数输入,都会配合一个心跳信号来用。心跳可以是PLC里的一个定时翻转的DO点,或者是一个递增计数值。机器人端在一个后台任务里监控这个心跳,如果在规定时间(比如500ms)内没有变化,就认为通讯异常,立即把目标值替换成安全值或者触发急停逻辑。

另外还要处理浮点数本身的异常值。PLC在程序启动初始阶段,REAL变量可能是未定义的状态,有时会读到NaN或Infinity。如果机器人在收到这些值时直接参与运动计算,轻则数值异常,重则触发系统报警。RAPID里可以用系统函数检查数值是否有限,或者简单点,在业务逻辑里加一个范围判断:

IF recv_value > 1000 OR recv_value < -1000 THEN ! 数据越界,按异常处理 recv_value := 0; ENDIF

5.3 浮点数比较、精度和扩展应用

在RAPID里做浮点数比较,和C语言里的老问题一样:永远不要直接用等于号判断两个浮点数相等。浮点运算有精度损失,比如0.1加0.2,理论上等于0.3,但计算机里存储的值可能是0.30000000000000004。所以在机器人程序里判断浮点数是否达到目标,要写成范围判断:

IF ABS(current_value - target_value) < 0.001 THEN ! 认为达到目标 ENDIF

这个阈值要根据具体工艺精度去设置,位置控制可能要到0.01毫米,而速度监控0.1就足够。

再延伸一下,这种方法完全可以承载更复杂的数据交互。机器人要传姿态数据时,通常是6个浮点数,比如X、Y、Z和Rx、Ry、Rz,也就是6轴旋转角度的组合。一帧就是24个字节,用同样的方式定义12个组信号,批量打包发送就行。这些浮点数组成了一个结构化的“数据帧”,在PLC侧集中解析,应用起来非常灵活。

5.4 故障排查速查表

我把这几年遇到的高频问题整理成了一张表,现场排查时可以对照着看。

现象可能原因排查与解决
通讯一直不建立,设备显示故障设备名不一致或IP地址冲突用DCP工具扫描实际设备名,核对组态名称
数据能通,但数值完全不对高低字顺序或字节顺序不对发1.0做验证,打印两个字的十六进制值确认
数据偶尔跳变通讯周期不匹配或线缆干扰检查PN线缆屏蔽和接地,统一IO更新周期
数值为NaN或极大值PLC侧REAL未初始化或模块异常在机器人侧增加数值范围判断,配合心跳安全处理
机器人重启后信号丢失配置未保存或组信号名称冲突确认配置已同步到控制器,检查信号命名
输出信号有值但PLC读不到输出地址映射反了在机器人侧强制输出已知值,PLC侧监控对应地址

这张表看起来简单,但每一条背后都是真金白银的调试时间。尤其是第一条,设备名不一致,新手上线几乎必踩。

6. 一点经验

做这类ABB机器人和PLC之间的PN总线浮点数通讯,我现在已经形成了一套固定套路:先确认需求要传什么数据,再配置信号映射,然后做字节序验证,最后才写业务逻辑代码。这个顺序一旦颠倒,比如上来就噼里啪啦写一堆RAPID程序,再回头去查信号地址问题,效率会低很多。

最后分享一个小技巧,我在办公室常备一个在线浮点数十六进制转换器,调试时随手就能查。遇到莫名其妙的数值,先把收到的字节打出来,再和理论值比对,30秒就能判断出是不是字节序的问题。这个习惯帮我少加了不知多少班。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询