拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ABB机器人RAPID Socket通讯实战:客户端服务端与排错指南

ABB机器人RAPID Socket通讯实战:客户端服务端与排错指南

做ABB机器人调试的,最躲不开的一件事就是跟外部系统“对话”。视觉系统告诉你工件坐标,MES要拿走产量数据,上位机要远程控制机器人启动,产线还要实时上报状态——这种场景太多了。信号量太粗糙,走总线又要配一堆硬件加扫描配置,这时候socket通讯几乎是最省事的方案:一根网线,两端各写几行代码,数据就通了。这篇文章把ABB机器人RAPID里的socket客户端和服务端写法、上位机怎么配合、以及我调了无数个项目后攒下来的排查经验一次讲清楚。不管你是刚接触机器人通讯的电气工程师,还是想先在RobotStudio里把流程验证一把的集成商,照着走基本都能跑起来。

1.1 现场通讯方案怎么选才不折腾

先说说为什么我偏爱socket。现场做设备互联,常见的有三条路。

第一条是硬接线IO,简单直接,但要传的东西稍微多一点就完蛋。几十个点位拉线拉到怀疑人生,而且只能传开关量,你说要把一个工件的X、Y、Z坐标传过去,用IO怎么传?要么拆成N位二进制慢慢抖,要么加模拟量模块,折腾一圈下来精度还不一定够。

第二条是走现场总线,Profinet、EtherNet/IP这些。这类方案非常稳,适合PLC和机器人之间做实时控制,但配置成本高。机器人要装对应选件、PLC要组态GSD文件、博途里面一顿操作,两边工程师凑到一起对半天才能把IO映射对上。如果是异种设备之间高速交互,比如机器人和工控机、机器人和MES,走总线反而痛苦。

第三条就是socket通讯。TCP/IP是通用协议,任何语言都有socket库,C#、Python、Java、C++随便挑。两端约定好报文格式,建立连接之后就是收发字符串或者字节流。它解决的最大问题是“跨平台、跨厂商的系统之间,用一套大家都认的协议去传数据”。不挑硬件,不挑操作系统,一条网线就完事。

从ABB机器人角度来看,RAPID语言本身就内置了完整的socket指令,不需要额外购买什么昂贵选件,标准系统就能用。这就是它在我这里成为首选的根本原因:零成本、开箱即用、灵活度高。

1.2 Socket适合干什么,不适合干什么

用socket之前要先清楚它的边界。它适合做非实时、响应时间在几十毫秒到秒级的数据交换,比如:

  • 视觉系统给机器人发工件坐标,机器人走位完成后回传OK
  • 机器人向上位机上报当前状态、报警、产量
  • 上位机下发配方、任务、切换程序的命令
  • MES采集设备数据,机器人周期性地把节拍、故障代码推上去

但如果你的需求是真正意义上的实时同步运动控制,比如多轴联动、插补、伺服同步,那socket不适合,那得走EtherCAT或者专用总线。TCP本身有延迟、有重传、有粘包,它保证的是“最终送达”,不保证“准时送达”。拿它来做实时控制,方向就错了。

还有一个容易忽略的点:socket通讯在项目里通常只担当数据管道,不在上面做复杂的业务逻辑。复杂逻辑放上位机或者后台任务里处理,机器人端越简单越稳。

2. 开工前的准备:网络规划和软件坑

2.1 网口、IP和网段怎么搭

很多人第一步就挂在网络配置上。ABB机器人控制柜上面有好几个网口,用途不一样。Service口一般是用来连RobotStudio做在线编程的,部分型号默认IP是192.168.125.1这个网段;LAN口或者WAN口才是用来做外部通讯的。不同型号、不同系统版本不太一样,最靠谱的做法是拿到控制柜之后,在示教器里进控制面板,找到IP设置,看清楚每个网口实际地址。

做socket通讯,我的习惯是给机器人单独规划一个固定的调试网段,不跟办公网混在一起。举个例子,机器人LAN口设成192.168.1.10,上位机设成192.168.1.20,掩码都是255.255.255.0。不要用DHCP,不要用自动获取。工业现场一旦上位机重启之后IP漂了,那你通讯就废了。

接线方面,点对点调试用一根网线直连最省事,机器人和PC网卡都是千兆口,直连完全没问题。如果现场设备多,就加一个工业交换机,把机器人、上位机、视觉相机、PLC全部拉进同一个网段。注意别跟车间大网冲突,尤其是那种整个厂房一个网段的,极易撞IP。真撞了,你ping网关都是通的,但就是连不上机器人,排查半天才发现是地址被人占用了。

最后补一句,调试前先在PC上ping一下机器人IP,通了再接下一步。ping不通的情况下搞socket,纯属浪费时间。

2.2 RobotStudio仿真与安装常见问题

没有真实机器人的时候,先用RobotStudio仿真验证socket逻辑,这个习惯非常推荐。虚拟控制器和真实控制器的RAPID行为基本一致,上位机程序也几乎不用改,省了现场大量调试时间。

如果是PC和RobotStudio在同一台电脑上,机器人客户端可以直接连127.0.0.1访问本机的上位机服务端,实测是通的。但要注意,RobotStudio本身也有网络设置,稳妥一点的做法是给虚拟控制器配上固定IP,然后上位机和机器人互ping验证一下,确认通了再跑程序。

顺带说一个很多人问的问题:RobotStudio 6.08安装刚开始就结束怎么办。我遇到过好几次,共性原因基本是这几个:安装包或者电脑缺.NET运行库、杀毒软件把安装进程拦了、之前装过旧版本残留注册表导致冲突、还有用户权限不够。通用解法是:右键管理员身份运行安装程序,安装前退出杀毒软件和防火墙,用控制面板卸载干净旧版本,再用清理工具把注册表残留清一遍,最后重新装。装的时候全程别切窗口,安装路径别带中文。这套流程下来,99%都能解决。

2.3 RAPID Socket指令族一览

写代码之前,先熟悉几条核心指令。RAPID的socket通讯指令不算多,我整理了一张表,对照着看思路会很清楚。

功能指令客户端服务器端
创建socketSocketCreate用用
绑定端口SocketBind不用用
监听端口SocketListen不用用
等待并接受连接SocketAccept不用用
连接远端SocketConnect用不用
发送数据SocketSend用用
接收数据SocketReceive用用
关闭连接SocketClose用用

客户端和服务器端的角色差异很好理解。客户端主动去connect别人的IP和端口;服务器端先在本地绑定一个端口,然后listen监听,再调用accept等别人连进来。ABB机器人两个角色都能当,具体用哪种取决于你的应用架构。

比如视觉引导,通常是上位机或者视觉软件做服务器,机器人做客户端主动上报坐标请求;反过来,如果上位机要随时查询机器人状态,那更自然的是机器人做服务器,上位机往这个端口发查询命令。两种我都做过,代码结构上差别不大,核心就是上面这8个指令来回组合。

3. 机器人做客户端,主动上报给上位机

3.1 上位机先开一个TCP服务器

我举一个最典型的场景:机器人每次完成抓取,把当前坐标发给上位机,上位机收到后回一个“OK”。

先写上位机。这里给两个版本,一个是C#,一个是Python,看你自己的环境选。

C#用TcpListener,代码非常清晰:

using System; using System.Net; using System.Net.Sockets; using System.Text; class Program { static void Main() { TcpListener server = new TcpListener(IPAddress.Any, 5000); server.Start(); Console.WriteLine("服务器监听中,端口5000 ..."); while (true) { TcpClient client = server.AcceptTcpClient(); Console.WriteLine("机器人已连接"); NetworkStream stream = client.GetStream(); byte[] buffer = new byte[1024]; string msg = ""; while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) { msg += Encoding.ASCII.GetString(buffer, 0, bytesRead); if (msg.EndsWith("\n")) break; // 遇到换行符认为一条完整指令 } Console.WriteLine("收到: " + msg); byte[] reply = Encoding.ASCII.GetBytes("OK\n"); stream.Write(reply, 0, reply.Length); client.Close(); } } }

Python版更短:

import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(("0.0.0.0", 5000)) server.listen(1) print("等待ABB机器人连接...") conn, addr = server.accept() print("连接来自:", addr) while True: data = conn.recv(1024) if not data: break if data.endswith(b"\n"): print("收到:", data) conn.sendall(b"OK\n") conn.close() server.close()

两个版本的共同点是:收到一条以换行符结尾的数据后才认为是一条完整报文。这个习惯非常关键,具体原因看3.3节,ABB侧对换行符有硬性依赖。

3.2 RAPID端连接、发送、接收

机器人这一侧,RAPID代码结构大概是这样的:

VAR socketdev client_socket; VAR string received_data; PROC Sock_Client() ! 1. 创建套接字 SocketCreate client_socket; ! 2. 连接上位机 SocketConnect client_socket, "192.168.1.20", 5000; ! 3. 发送请求,注意字符串结尾加了换行符\n SocketSend client_socket \Str:="REQ_POSE\n"; ! 4. 接收上位机回包,加2秒超时 SocketReceive client_socket \Str:=received_data \Time:=2000; TPWrite "收到回复: " + received_data; ! 5. 关闭连接 SocketClose client_socket; ERROR IF ERRNO = ERR_SOCK_TIMEOUT THEN TPWrite "通讯超时"; ELSEIF ERRNO = ERR_SOCK_CONN_CLOSED THEN TPWrite "连接被对端关闭"; ELSE TPWrite "Socket错误,错误号: " + ValToStr(ERRNO); ENDIF SocketClose client_socket; END

这段代码的逻辑很直白:创建socket,连接,发送,接收,关闭。关键词都在,不需要额外解释。

需要注意几点。

第一,SocketConnect是阻塞的。如果IP不通或者端口不对,它不会像上位机那样立刻抛异常,而是会卡在那里一段时间。所以调试时一定先确认网络通,否则你会看到机器人程序一直卡在连接语句那一行。

第二,SocketSend \Str:=发送的是普通的ASCII字符串。上位机收到的就是这一串字节。RAPID里字符串拼接用加号,"REQ_POSE" + "\n"这样也行,但我习惯直接写在一个字符串里。

第三,SocketReceive的\Time是超时时间,单位是毫秒。我强烈建议给所有接收指令都加上超时,否则一旦对端不回复,机器人就会一直傻等,现场生产就卡住了。

3.3 换行符、超时、STRING长度:三个绕不开的坑

这一节是重点,全是实战踩出来的。

第一个坑:SocketReceive \Str:=这个形式,收到换行符才算接收完成。ABB官方文档里的讲法是,当收到\n字符或者超时到达时,接收指令才会返回。也就是说,如果你上位机发送的数据末尾没有加换行符,机器人那边就会一直等,等到你设置的超时时间到,然后报超时错误。

我见过太多新人在这一步卡一整天。上位机明明把数据发出去了,机器人就是收不到,最后发现是没加\n。所以无论哪端发给机器人,只要是字符串报文,末尾一定要带上换行符。

第二个坑:RAPID的STRING类型有长度限制。常规情况下STRING最大长度80字符,超了直接报错。所以做通讯协议时,不要设计那种动辄几百个字节的文本报文。要么把长数据拆成多条短消息,要么干脆用rawbytes字节流来收发二进制数据。对于现场大多数应用,先按短报文设计,一条指令控制在80字符以内,能省掉大量麻烦。

第三个坑:TCP是流式协议,天然存在粘包和半包。上位机一次send,机器人不一定一次receive就能拿到全部数据;上位机两次send的数据,也可能被合并成一次到达。所以通讯协议里必须要有边界标识。我用得最多的就是换行符做边界,简单可靠。如果传二进制,一般用固定长度或者“长度头+数据体”的结构,后面第4.3节会展开讲。

4. 机器人做服务器,让上位机来读取

4.1 Bind、Listen、Accept三步走

机器人做服务器,最大的好处是上位机可以随时主动来查询,不需要机器人一直主动上报。比如产线有个上位机,每个工位都可能来查机器人当前状态,这时候机器人作为服务端更自然。

RAPID端的服务器初始化代码:

VAR socketdev server_socket; VAR socketdev client_socket; VAR string recv_cmd; PROC Sock_Server_Init() ! 创建服务器socket SocketCreate server_socket; ! 绑定到所有网卡,端口5000 SocketBind server_socket, "0.0.0.0", 5000; ! 开始监听 SocketListen server_socket; TPWrite "服务器启动,等待上位机连接..."; END

这里有个细节:SocketBind的地址,我通常写"0.0.0.0",表示监听机器人所有的网卡。如果机器人有多个网口,这样最省事,不会出现上位机连了A网口、数据却从B网口进来的问题。

紧接着是等待上位机连接。SocketAccept也是一个阻塞指令,会一直等待,直到有客户端连进来,或者超时时间到。

PROC Sock_Server_Loop() WHILE TRUE DO SocketAccept server_socket, client_socket \Time:=60000; SocketReceive client_socket \Str:=recv_cmd \Time:=3000; TPWrite "收到命令: " + recv_cmd; IF recv_cmd = "GET_POSE\n" THEN SocketSend client_socket \Str:="100,200,300\n"; ELSE SocketSend client_socket \Str:="UNKNOWN_CMD\n"; ENDIF SocketClose client_socket; ENDWHILE ERROR IF ERRNO = ERR_SOCK_TIMEOUT THEN SocketClose client_socket; RETRY; ENDIF END

这段逻辑是服务器端最常见的形态:一直接收连接,处理完一条命令就关闭连接,然后回到循环继续等下一个。每条命令都是一个独立的TCP连接,虽然效率不是最高的,但胜在逻辑简单、不容易出状态问题。

4.2 一个连接处理完再接下一个

有一个新手很容易踩的坑:RAPID的socketdev变量一次只能维护一个连接。你创建了client_socket接收了上位机连接,在没有关闭之前,SocketAccept不能再次调用,或者说即使调用了也没法同时处理两个客户端。

所以服务器端的编程模型必须是一个连接处理完、关闭、再accept下一个。如果上位机那边频繁重连,也要确保旧的client_socket被关闭,否则连接资源会泄漏,最终导致机器人无法再接受新连接。

处理上位机异常断开的情况也要小心。对端直接拔网线或者程序崩溃,机器人这边的SocketReceive会报连接被关闭的错误。所以每次收发都要有ERROR分支兜底。我上面示例里,只要出现超时或者错误,就把client_socket关掉,然后RETRY回到accept等待状态。这样即使上位机抽风,机器人的服务端也不会死。

还有一个实际经验:给SocketAccept也加超时。如果长时间没有客户端连接,accept会一直阻塞,后台任务就卡在那里。加上超时之后,哪怕没有连接,循环也能定期醒来,做点状态刷新之类的事情,程序整体更健康。

4.3 设计一个稳一点的报文协议

通讯写多了你会发现,代码本身不是难点,协议设计才是。一个烂协议会在调试现场反复折磨你。

我总结的报文协议三件套:边界、校验、超时。

边界就是告诉对端“一条完整消息到哪里结束”。文本协议用换行符最简单,但只适合短小的指令。二进制协议我推荐固定长度,比如每条消息固定16字节,不够补零,简单粗暴好解析。再进阶一点用4字节长度头加数据体,上位机先读长度再读数据体,这样能兼容任意长度。

校验是防止数据在传输过程中被干扰。工业现场电磁环境复杂,偶尔会出现一个字节被改掉的情况。简单的做法是加一个累加和校验(Checksum),复杂一点上CRC16。对大多数非安全级应用来说,累加和已经够用。

超时前面反复强调了,接收时一定要设超时。协议层面最好还能约定,如果对端在N秒内没有回应,发起方就认为这次通讯失败,进入错误处理。

举个例子,我常用的文本指令协议大概是这样的:

GET_POSE\n SET_SPEED,100\n MOVE,100,200,300,10\n

每条命令都是“命令字+参数+逗号分隔+换行符”。机器人收到后按逗号拆字段,命令字做逻辑分支,参数转成数值,非常容易解析。注意RAPID里字符串拆分需要自己写循环,没有现成的Split函数,所以字符串指令的字段别搞太多,简化解析逻辑。

5. 实战中高频问题的排查清单

5.1 连不上先按这个顺序查

通讯出了问题,千万不要瞎改代码。按顺序排查,绝大多数问题几分钟就能定位。

第一,先ping。PC上ping机器人IP,机器人侧的服务器地址也ping一下上位机。ping不通,说明物理链路、IP配置有问题,后面全白搭。检查网线是否插对口、IP是否同网段、交换机哪个灯亮了。

第二,确认端口通不通。用Windows自带的telnet,或者用Test-NetConnection -Port 5000来测试。如果端口不通,可能服务端没启动、端口被防火墙拦住、或者机器人程序根本没跑到listen那一行。

第三,确认Windows防火墙是否放行了端口。这个问题在开发机上尤其常见,服务端程序明明启动了,但本机防火墙默认禁止外部连接,导致机器人连不过来。解决方法是进防火墙高级设置,添加入站规则,放行TCP 5000端口。开发调试时图省事可以先关防火墙,但现场部署一定按规则放行,别把安全关了。

第四,看机器人示教器上的程序走到哪一行了。如果在SocketConnect上停着,说明连接阶段就失败了;如果在SocketReceive上停着,多半是数据发了但没带换行符,或者对端没回。示教器上的光标位置和TPWrite日志,比任何工具都直观。

5.2 数据乱、粘包、半包怎么处理

数据收到了但是内容不对,一般就三类。

第一种是编码问题。RAPID的字符串本质上是字节,上位机如果按UTF-8解析,机器人按ASCII发送的中文就会变成乱码。我的建议是现场通讯报文统一用ASCII字符集,能不用中文就不用中文。像“OK”“ERROR”“100,200,300”这种纯ASCII内容,永远不会出编码问题。如果一定要传中文,就用字节数组配合UTF-8编码收发,上位机那端用Encoding.UTF8,机器人端用rawbytes,两边约定好编码,费点事但能通。

第二种是粘包。上位机连续发两条指令,比如“SET_SPEED,100\nMOVE,1,2,3\n”,机器人一次接收可能拿到的是一整串“SET_SPEED,100\nMOVE,1,2,3\n”。这时按行解析,把\n作为分隔符拆开,逐条处理。注意处理完一条命令后,剩余的数据要保留到下一次接收拼接,不能丢掉。

第三种是半包。一条完整指令被拆成了两截到达。解决办法是上位机或者机器人收到数据后先缓存,直到缓冲区里出现了换行符,才认为是一条完整的消息。这就是为什么我前面反复强调边界符——没有边界符,粘包半包问题几乎无解。

5.3 机器人卡死、超时怎么兜底

机器人程序卡在socket指令上,是现场最闹心的问题。生产节拍停在那里,设备一动不动,后面工位全堵住。所以要提前做防护。

我的习惯是,所有阻塞型的socket操作全部加超时。SocketReceive加\Time,SocketAccept加\Time,这是底线。SocketConnect本身没有直接的超时参数,但连接失败会报错误,同时RAPID的ERROR块可以捕获并处理,不会无限阻塞在系统层。

另外,程序里要防“死循环重试”。有些人图省事,出错就RETRY,结果网络一直不通,机器人就在错误处理和重试之间反复横跳,程序还是卡着。更好的做法是设置一个重试次数上限,比如连续重试3次还失败,就退出socket流程,把故障状态抛给主程序,让产线逻辑决定是停机报警还是继续尝试。

真正重要的经验是:不要在机器人主程序里做长时间的socket阻塞等待。如果通讯数据量比较大,或者对端响应慢,把socket通讯放到后台任务里,主任务只负责读取共享变量。后面专门讲这个。

5.4 后台任务与主任务协同

ABB机器人支持多任务,只要系统选项里启用了Multitasking,就可以新建一个后台任务专门跑socket通讯。后台任务和主任务通过PERS变量共享数据,互不阻塞。

我的常用结构是这样:后台任务里跑一个socket服务器循环,一直等上位机连接、接收命令、解析命令,把结果写入一个PERS变量。主任务在运动程序里周期性读取这个PERS变量,发现有新命令就响应。

PERS string recv_buffer; PERS bool new_cmd_flag; PROC Sock_Background() WHILE TRUE DO ! 接收数据,写入recv_buffer SocketReceive client_socket \Str:=recv_buffer \Time:=100; IF recv_buffer <> "" THEN new_cmd_flag := TRUE; ENDIF ENDWHILE END

主任务那边:

IF new_cmd_flag THEN new_cmd_flag := FALSE; ! 解析recv_buffer并执行 ENDIF

这里要特别注意数据一致性问题。主任务和后台任务同时访问同一个PERS变量,理论上会有读写竞争。我的处理方式是用一个简单的布尔标志做互斥,写数据前先清标志,写完再置位,读数据前等标志位稳定。对于这个应用场景已经完全够用。

这个架构带来的最大好处是,通讯不会拖累机器人运动。就算上位机长时间不回复,后台任务卡住了,主任务照样可以走轨迹、做逻辑,生产不会因为通讯问题停下来。这个设计在真实产线上价值极大。

最后说点个人习惯。我每写一个socket通讯程序,不管多简单,都会做三件事:第一,把报文格式先写在程序注释里,包含命令字、分隔符、结束符、超时时间,这样换人维护也看得懂;第二,所有接收指令都加超时,绝不让机器人无限等;第三,通讯程序的错误分支要单独测试一次,把网线拔了、把上位机关了、把端口占了,看程序会不会报错僵住。这三次破坏性测试,帮我少熬了很多夜。

如果你现在正卡在“上位机收不到数据”或者“机器人一直等”这两个问题上,先去检查发送方有没有在报文末尾加换行符。这是ABB socket通讯里出现频率最高、也最容易忽视的坑。

返回列表