在泰国工厂做FANUC机器人TCP通信调试时,我遇到过一个非常折磨人的现象:机器人每隔一段时间就报告通信断开,可现场交换机指示灯明明正常,上位机软件也没有任何崩溃记录。“瞬时中断”这个词,就是在这种一线环境里被逼着清晰起来的。
当时我们负责的任务,是让一台FANUC机器人与产线上的MES上位机保持可靠的TCP通信:机器人把加工完成的产品序列号、良品信息上传,上位机再下发下一步生产参数。生产节拍很紧,每次掉线都意味着停线和人工确认。真正难的不是“重连”本身,而是先搞清楚“为什么会瞬时中断”,再设计一套能在断开后自动恢复、不影响节拍的机制。
这篇文章就是这次实战的完整复盘:从网络排查抓到真正的根因,到机器人与上位机两端重连逻辑的实现,再到两周运行验证的效果。如果你也在搞机器人Socket通信、无人化产线、或任何高可用TCP链路,这篇值得读完再走。
1. 故障现象与现场环境:为什么会发生“看起来一切正常”的断线
1.1 故障现象:生产中间歇性丢包
故障最开始的表现很典型:一台FANUC机器人在执行取料动作时,等待上位机反馈超时,然后TP示教器上弹出“Communication Error”,程序停在中间,现场需要人工复位。
刚开始我们以为是程序逻辑问题,毕竟这类上位机交互最常出的就是“握手协议没对齐”。但检查了一个班次后发现,报错没有固定规律:有时半小时一次,有时两个小时一次,甚至一天都不出一次。更诡异的是,每次到现场看,上位机画面的连接状态都是“已连接”,没有EXE崩溃,没有防火墙拦截记录。
后来我们给TCP通信加了带时间戳的日志,确认了一个关键信息:断线发生前,机器人发出的最后一包数据到达了上位机,但上位机回复的ACK,机器人并没有收到。也就是说,这是一个真正的网络层问题,不是业务逻辑的锅。
1.2 现场网络拓扑与通信方案
这套产线的通信拓扑不复杂:FANUC机器人控制器通过网线接到车间二级交换机,二级交换机再通过光纤上联到中控室的核心交换机,上位机软件部署在中控室的工控机上。
机器人端的通信方案,是典型的Socket方式:机器人作为TCP客户端,主动连接上位机上的服务端口。建立连接后,机器人每完成一个动作周期,就发送一次到位信号,并等待上位机的确认报文。这个方案本身没有任何问题,很多产线都这么用,但正因为“大家都这么用”,现场很多隐患反而不被重视。
我把拓扑画出来后,第一个怀疑目标就是那根从机器人控制柜到二级交换机的网线。因为这段距离在现场实测有七十多米,而且是走电缆桥架,和动力电缆平行捆扎了很长一段距离。按标准,工业环境超过50米的非屏蔽双绞线就值得复查,70米加上强电并行,信号质量确实可能出问题。
1.3 泰国工厂环境对TCP通信的隐性挑战
这里必须聊聊泰国工厂的特殊性,不是地域偏见,而是很多做国内项目的朋友真的不会留意这些。
第一是温度。东南亚常年高温,车间顶部虽然装了风扇,但设备控制柜内部的实测温度在下午轻松超过40度。电子元件在这样的温度下长时间运行,网口PHY芯片、交换机端口的稳定性都会下降,偶发丢包率会显著上升。我们后来排查到控制柜散热风扇被棉絮堵了一部分,这直接改变了整条链路的可靠性。
第二是供电波动。雨季雷电频繁,园区电网有过压、欠压的记录。虽然机器人控制器和交换机都有UPS,但车间里一些大功率设备启停时,电压跌落仍然会造成部分设备的网卡瞬间复位。这种“复位”非常快,快到上位机的socket连接还没意识到已经断了,这就会形成半开连接。
第三是团队沟通成本。现场有中、英、泰三种语言,故障记录和交接班文档又都是混合语言写的,很多关键信息在传递中丢失。比如“上次断线的时候是不是刚开完焊机”“当时有没有人动过网线”,这类细节往往是最有效的线索,但在多语言环境下很难及时收集。
2. 排查链路:从网线查到控制柜,把直觉问题变成数据
2.1 先排掉最好处理的:网卡节能、防火墙、IP冲突
接手排查后,我的原则是:先排除所有“一键就能解决、但会反复出现的低级问题”,再去动网络架构,否则容易把问题复杂化。
第一个动作,是检查工控机网卡的电源管理。Windows默认会允许系统关闭以太网网卡以节能,这个设置会在电脑长时间空闲后让网卡进入低功耗状态,一旦进入低功耗,TCP长连接就会出现类似断线的现象。我们把工控机网卡的“允许计算机关闭此设备以节约电源”取消勾选后,掉线频率确实有小幅下降,但问题依旧,说明这不是根因。
第二个动作,检查IP地址冲突。在车间这种场地,AGV、扫码枪、摄像头都会抢地址,如果机器人和某台设备发生了IP冲突,会出现非常诡异的间歇性断线。我们专门查了交换机ARP表,对比了机器人控制器地址和网关的对应关系,确认没有冲突。
第三个动作,是防火墙。机器人控制器本身没有防火墙,但工控机上的安全软件会把陌生设备访问当作潜在威胁,存在自动切断连接的可能性。我们把上位机的通信端口在内网策略里彻底放行,也临时关闭过安全软件做对照测试,结果还是偶尔断线。
到这里,我基本确定:问题不在上位机软件配置层面,而在网络链路或物理环境。
2.2 链路层检查:网线、水晶头、交换机端口错误计数
第二阶段的排查,我开始转变思路:不再看“连接是不是断了”,而是看“链路上的数据是不是已经脏了”。
我拿了一台笔记本,跑到机器人控制柜旁,直接接在控制柜到二级交换机的网线上,做长时间ping和丢包测试。ping包的结果很意外:丢包率不到1%,连续ping三百次只丢了两次。更让人迷惑的是,丢包发生时间和产线报警时间对不上,说明“断线”的感受和实际链路质量之间,有明显的滞后。
随后我登录了车间二级交换机的CLI,查看端口统计信息。这里有个很容易被忽略的点:很多国产网管交换机默认不显示CRC错误包数,需要在端口明细里单独翻。我翻出来后,发现连接机器人控制柜的这个端口,CRC错误计数明显高于其他端口,而且还在持续增长。
这说明物理层一定有东西在干扰或劣化信号。我把现场网线拆下来检查,发现水晶头做的还算干净,但是走到桥架那段,发现网线的一段护套破了,铜缆直接贴着动力电缆外皮。变频器启动时,动力电缆会产生很强的电磁干扰,如果网线屏蔽层没有良好接地,这个位置就是天然的干扰耦合点。
2.3 抓包分析:RST、FIN、无响应分别说明什么
链路层有了疑点,还需要应用层的证据。我跑到中控室,在核心交换机上做了端口镜像,用Wireshark抓了三个班次的报文。这次抓包给我最重要的收获,是学会了用“断开前的最后一个TCP报文”来锁定问题方向。
我整理了一个经验对照表,现场排查时非常有用:
| 断开前最后一个报文 | 含义 | 常见原因 |
|---|---|---|
| 对端发来 FIN, ACK | 对端应用主动关闭连接 | 上位机程序重启、连接池管理逻辑释放socket |
| 对端发来 RST, ACK | 对端协议栈直接拒绝连接 | 端口未监听、socket资源被占用、防火墙RST、网卡驱动异常复位 |
| 本端反复重传,对端无ACK | 链路中间断开或对端掉电 | 交换机端口异常、网线被拔出、机器人控制器死机、网卡断电 |
| 本端发送Keep-Alive,对端无回应 | 半开连接已产生 | 对端网卡休眠、对端程序死锁、对端设备掉线但TCP栈未关闭 |
回到抓包数据,我发现断线前机器人发出的TCP数据包在持续重传,对端一直没有ACK。这说明对端设备——也就是上位机——并没有发送RST或FIN,而是“消失”了。最可能的解释就是上位机网卡瞬间复位,或交换机端口瞬间丢掉了转发表项。
这里有个非常反直觉的结论:机器人报“通信错误”的时候,真正出问题的往往不是机器人,而是对端上位机或中间链路。所以在设计重连机制时,千万不能只盯着机器人端,上位机端必须有对应的连接回收逻辑。
2.4 真正的元凶:环境温度与供电干扰的耦合
前面排查了网线干扰、网卡配置,但总觉得还差一层。直到我把控制柜门打开,红外测温枪一扫,才发现问题比我想的复杂。
机器人控制柜内部温度已经接近45度,散热风扇被棉絮堵了三分之一,转速明显下降。控制器的网口模块和主板靠得很近,在高温环境下,以太网PHY的工作稳定性会打折扣。温度曲线和报警时间记录放在一起看,报警主要集中在下午高温时段和前一天做过焊接设备测试的时间段。这说明罪魁祸首不是单一因素,而是“高温抬高底噪,电磁干扰如最后一根稻草”的耦合效应。
处理方式分两步:一是清散热系统,把控制柜风扇的滤网换掉,并给机器人网口附近加了一个工业级的小风扇辅助散热;二是在网线上加了一层金属软管做机械与电磁防护,同时把网线桥架内和动力电缆之间的间距拉开到30厘米以上。经过这些物理整改,报警频率降到原来的十分之一左右,但还没有完全归零。
这让我意识到:物理层再改,也只能把“大概率事件”变成“小概率事件”,不能做到绝对。真正让产线放心跑下去的,必须在软件层有自动重连机制兜底。
3. 重连机制设计:为什么“断开重连”远不是一句重连那么简单
3.1 半开连接与TCP连接状态
很多人写Socket通信,会把“连接断开”理解成“对方发来FIN”或“网络层报错”。但实际上,TCP最坑的就是半开连接:链路已经物理不通,或者对端网卡已经复位,但对端没有机会发出FIN,本端协议栈还认为连接存在。
在工业现场,网卡瞬间复位、交换机端口闪断、设备快速重启,都会造成半开连接。这时候你去查机器人侧的Socket状态,它可能会显示“已连接”,因为本端不知道对端已经死了。这就是为什么我们在机器人端不能只看连接状态,而必须靠应用层心跳。
TCP自带的Keep-Alive机制虽然存在,但默认探测周期很长,Windows下通常是2小时一次探测,这在生产节拍中以分钟计的产线上根本来不及。所以必须自己做应用层心跳:每隔几秒发送一个固定报文,如果连续N个周期没有收到回复,就判定连接失效。
3.2 机器人侧状态机设计
我把机器人的通信逻辑拆成了四个状态:空闲、连接中、通信正常、需要重连。这样写程序比“一行一行看是否超时”清晰得多。
连接中状态,主要处理TCPConnect的结果。FANUC的Socket指令在建立连接时是异步的,连接失败不会立刻让TP程序崩溃,而是通过状态变量返回。我们在连接失败后等待一小段间隔再重试。
通信正常状态下,机器人周期性地发送心跳报文和业务报文,同时监控最近一次接收到上位机回复的时间。如果连续两个心跳周期都没有收到任何数据,就认为连接已失效,立刻跳转到需要重连状态。
需要重连状态下,机器人先执行TCPClose,清理掉当前可能残留的连接会话,再重新执行TCPConnect。如果连接重试了N次还不行,就把一个DI信号置ON,让PLC产生声光报警,提示现场介入。
3.3 上位机侧状态机设计
很多人只改机器人端,上位机端不处理,就会遇到一个问题:机器人断了重连,但上位机里的旧连接资源没有回收,导致新连接无法建立,或者建立了却被当成旧连接处理。
上位机端的设计,我建议做两件事。第一,监听socket使用独立线程,不断accept新连接,不为单个连接阻塞。第二,为每个连接记录“最后心跳时间”,一旦超过阈值,强制关闭该连接。不管这是一个旧连接还是异常连接,关闭后资源就释放了,机器人重连时不会有阻碍。
这里还要注意端口复用的问题。上位机监听端口时,最好设置SO_REUSEADDR,这样即使上一个socket还处于TIME_WAIT状态,也能正常监听同一个端口。这是实际联调时最常见的坑之一。
3.4 心跳与退避参数的确定方法
参数不是拍脑袋定的,要结合产线节拍和故障恢复时间预期。
| 参数 | 建议值 | 依据 |
|---|---|---|
| 心跳周期 | 3秒 | 小于产线动作周期的1/5,不影响带宽,也能在10秒内感知断线 |
| 断线判定阈值 | 6秒 | 连续2次心跳无回复即判定断线,避免误杀偶发丢包 |
| 重连初始间隔 | 5秒 | 预留网络恢复时间,给交换机、上位机一点重建时间 |
| 重连退避上限 | 60秒 | 避免高频重试冲击网络,也保证在节拍内可恢复 |
| 连续失败报警阈值 | 6次 | 超过6次说明问题不是瞬时,需要人工介入 |
确定断线阈值时有个技巧:把心跳周期设短,但不要短到把偶发拥堵当作断线。我们现场测过,3秒心跳,网络波动时偶尔会丢一包,但下一包就正常了。如果阈值设成3秒,按一包也没有回复来判,就会误判断线,导致频繁重连。所以阈值应该是心跳周期的2到3倍。
4. 落地实现:机器人端与上位机端的完整重连逻辑
4.1 机器人端:基于Socket指令的自动重连主循环
FANUC机器人在TP程序里操作Socket,用的是一组专门的通信指令:TCPConnect建立连接、TCPSend发送数据、TCPRecv接收数据、TCPClose关闭连接、TCPStatus查询连接状态。具体指令写法会根据控制器型号和系统版本略有不同,我这里用示意逻辑说明思路,你可以直接映射到自己设备上的格式。
重连主循环的核心逻辑如下:
! 主循环:一次循环处理一个通信周期 IF 当前状态 = 空闲 THEN CALL 连接上位机 如果连接成功,状态设为通信正常 END IF IF 当前状态 = 通信正常 THEN TCPSend[1] 发送心跳或业务报文 CALL 接收上位机回复 更新最近回复时间 如果连续两个心跳周期没有收到回复,状态设为需要重连 END IF IF 当前状态 = 需要重连 THEN TCPClose[1] 等待重连退避时间 状态设为连接中 END IF实际操作时,我倾向于把这段逻辑放到KAREL后台程序里跑,而不是全部堆在TP主程序里。原因很简单:TP程序是按顺序逐行执行的,如果通信等待占用了主程序的执行时间,机器人的动作逻辑就会跟着卡顿。KAREL后台程序可以独立运行,只在需要时通过共享寄存器或I/O和主程序通信。
重连退避在机器人端也要实现。我用了类似计数器的逻辑:每次重连失败,重试间隔乘2,直到上限60秒;一旦连接成功,计数器清零。这样在网络恢复的瞬间,机器人最多等60秒就会自动接回来。
4.2 上位机端:Python重连客户端实例
上位机端我用Python写了一个具备心跳检测和自动重连的客户端骨架,后续接到MES业务逻辑上很顺畅。这段代码不是玩具,是直接从现场验证过提炼出来的核心脉络。
import socket import time import logging REMOTE_ADDR = ("192.168.10.20", 5010) HEARTBEAT_INTERVAL = 3 HEARTBEAT_TIMEOUT = 8 RECONNECT_BASE_DELAY = 5 RECONNECT_MAX_DELAY = 60 logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s" ) def connect_with_retry(): delay = RECONNECT_BASE_DELAY while True: sock = None try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(HEARTBEAT_TIMEOUT) sock.connect(REMOTE_ADDR) logging.info("连接成功: %s", REMOTE_ADDR) return sock except OSError as exc: if sock is not None: sock.close() logging.warning("连接失败: %s,%d秒后重试", exc, delay) time.sleep(delay) delay = min(RECONNECT_MAX_DELAY, delay * 2) def heartbeat_loop(sock): last_success = time.time() while True: try: sock.sendall(b"HB") data = sock.recv(128) if not data: raise ConnectionError("对端关闭连接,收到空数据") last_success = time.time() time.sleep(HEARTBEAT_INTERVAL) except OSError as exc: logging.warning("心跳异常: %s,准备重连", exc) return def run(): while True: sock = connect_with_retry() logging.info("开始心跳监控") heartbeat_loop(sock) sock.close() time.sleep(RECONNECT_BASE_DELAY) if __name__ == "__main__": run()这个骨架有几个点值得说明。第一,connect_with_retry里用了指数退避,避免断网时高频重试。第二,heartbeat_loop里通过recv的返回值和异常感知对端关闭。第三,外层套了一个while True,相当于看门狗,任何一层死掉都会回到连接流程。实际生产环境我还会在这个基础上加一个文件日志和邮件告警,但核心逻辑不变。
4.3 联调时要注意的端口与socket资源问题
代码写出来只是第一步,真正让我翻车的是联调时的三个坑。
第一个坑是端口复用。机器人端刚断开,立刻重新连接时,老连接可能还处于TIME_WAIT状态。如果上位机监听socket没有设置SO_REUSEADDR,新连接就会被系统拒绝。Python里这个设置要在bind之前加,很多人等到accept报错才查到这里。
第二个坑是上位机连接资源的回收。如果上位机把每个连接都保存在列表里,又不清理失效连接,时间一长就会发现监听端口无响应。这不是网络问题,是连接泄漏。解决方案就是前面说的,每个连接配一个最后心跳时间,超时后强制close。
第三个坑是机器人端socket指令的调用时机。FANUC机器人的Socket指令如果在通信异常时还继续调用,可能会阻塞等待。我们在重连前一定先执行TCPClose,然后再重新走TCPConnect,而不是在旧连接上盲目重试。不加这一步的后果,是完全无法建立新连接。
5. 验证与长期运行:从干预恢复到秒级自愈
5.1 断线模拟测试与验收标准
改造完成后,不能直接上线,得先做破坏性测试。我们在停产时段模拟了三种故障:拔掉机器人和交换机之间的网线、重启上位机软件、把交换机断电再上电。
验收标准是:故障恢复后,系统要在30秒内自动恢复通信,不需要人工介入。同时连续模拟断线20次,成功率100%,且业务数据不丢失。
第一次测试就暴露了问题:拔掉网线再插回去,机器人侧能感知到通信异常,但重连并不顺畅,总在TCPClose后卡一段时间。排查后发现是KAREL后台程序里,TCPClose的执行被既有的接收等待阻塞了。后来调整了代码,在关闭连接前先让接收逻辑彻底退出,问题才解决。
5.2 两周现场运行数据
通过物理层整改加软件重连,我们收集了两周的报警记录,数据对比非常直观。
整改前,产线平均每天因为通信断线报警2到3次,每次停线处置时间在5分钟以上。整改后,两周内只出现了两次轻微链路抖动,机器人端检测到心跳超时后执行了自动重连,动作周期几乎没有中断,现场人员完全没感觉到异常。
更值得说的是,这套重连机制不仅兜住了物理层问题,还兜住了上位机软件更新的场景。MES版本升级时,上位机服务会重启,以前重启一次产线就要停半天,现在机器人检测到断线后自动重连,服务起来后十几秒就把队列里的数据补传完。
5.3 留给后人的边界情况
运行稳定之后,我没有急着写“成功总结”,而是把边界情况都记了下来。比如,机器人控制器本身的断电重启,这种场景下后台程序也会重启,连接建立时机要和机器人主程序的启动阶段对齐。再比如,网络恢复后如果上位机和机器人同时初始化,可能出现两个客户端同时抢建连接的情况,这时候服务端的连接回收机制必须足够快。
还有一点,就是通信数据的缓存策略。瞬时中断期间机器人产生的产品数据不能丢,我们的做法是在机器人端用一个队列暂存,连接恢复后先传缓存数据再传实时数据。这块看起来简单,真要在TP程序里做队列和断点续传,还是很考编程功底的。
最后再分享一段个人体会
在泰国工厂熬了那么多天,我最大的感受是:工业设备的TCP通信问题,很少是单一软件问题,大多数是物理环境、硬件配置和软件逻辑共同作用的结果。先把现场环境搞干净,再谈重连机制才有意义;反之,软件层的自愈能力又是现场环境无法完美时的最后防线。
如果你正在处理类似的“间歇性断线”,我的建议很简单:不要被“重启一下就好了”骗过去,先把链路质量做成指标,再用抓包确认断开的方式,最后才动手写重连逻辑。顺序对了,排查时间能省一半。这套方法不只是FANUC机器人适用,任何基于TCP的PLC、视觉系统、AGV调度通信,照着走都能少踩很多坑。