上一篇我们分享了从「各管各的」走向「统一调度平台」的踩坑经历。这一篇我们深入到通讯层——机器人和云端之间到底用什么协议通信?我们试过 UDP、试过纯 WebSocket、最后定了 WebSocket + MQTT 双通道。本文分享这个选型过程中的踩坑和经验,以及心跳、重连、幂等这些可靠性机制在实际项目中是怎么做的。
一、一开始我们想用 UDP,因为「快」
做机器人调度平台,第一个要解决的问题就是:机器人和云端之间用什么协议通信?
当时团队里有不同意见:
- 一派说用 UDP:「UDP 快啊,延迟低,没有 TCP 的握手和重传开销,适合实时控制和遥测数据传输。机器人领域不都在用 UDP 吗?」
- 一派说用 WebSocket:「WebSocket 双向通信,浏览器原生支持,前端做实时监控方便。而且基于 TCP,可靠。」
- 一派说用 MQTT:「MQTT 是物联网标准协议,轻量、支持 QoS、有遗嘱消息、支持离线消息,专门为不稳定网络设计的。」
最后我们决定——先试 UDP,因为「机器人控制对实时性要求高,UDP 快」。
现在回头看,这个决定让我们踩了大坑。
二、UDP 踩坑实录:「快」的代价是「不可靠」
我们在一个客户现场试了 UDP 方案,结果问题一个接一个。
坑一:「急停」指令丢了,吓出一身冷汗
UDP 是「发了就不管」的协议——发送方把数据包发出去,不确认对方收到没有,丢了就丢了。
大部分时候没问题,但有一次出了险情:
客户现场一台配送机器人在过道里行驶,前面突然走过来一个人。运维人员赶紧在后台点「急停」——结果急停指令通过 UDP 发出去,丢包了。
机器人没收到急停,继续往前走,差点撞到人。幸好那个人反应快,躲开了。
事后排查,发现就是 UDP 丢包——当时车间里 WiFi 信号有干扰,UDP 丢包率突然升高,急停指令正好丢了。
这件事之后,客户直接说:「你们要是还用 UDP,这项目我们不敢上了。」
在机器人控制场景里,「可靠」比「快几毫秒」重要一万倍。 急停、取消任务、参数配置这些指令,一条都不能丢。UDP 不保证送达,用在控制通道上就是定时炸弹。
坑二:企业防火墙把 UDP 全拦了
另一个客户现场,部署的时候发现机器人连不上平台。
我们排查了半天——网络通的、端口开的、配置没错,但就是连不上。最后找客户的 IT 部门一问,人家说:「我们防火墙默认拦截所有 UDP 流量,只允许 80/443 端口的 TCP 流量。要开 UDP 也行,走审批流程,大概两周。」
工厂、园区这种对网络安全要求高的地方,防火墙规则通常很严格,UDP 很可能被拦。你总不能每个客户都去走两周审批吧?
基于 TCP 的协议(WebSocket、MQTT over TLS)走 443 端口,防火墙一般不会拦,部署顺畅得多。
坑三:为了让 UDP 可靠,我们重新实现了 TCP
UDP 不可靠怎么办?我们想:「那我们在应用层加确认机制、重传机制、顺序保证不就行了?」
于是我们开始给 UDP 加:
- 消息序号——保证顺序
- ACK 确认——发了等对方确认,没确认就重传
- 超时重传——等一段时间没确认就重发
- 去重机制——收到重复的消息要丢弃
加着加着我们发现——这不就是在重新实现 TCP 吗? 而且我们实现的还不如 TCP 成熟可靠,TCP 的拥塞控制、滑动窗口、快速重传这些机制,我们根本做不到那个水平。
与其用 UDP + 自己实现可靠性,不如直接用 TCP,成熟、稳定、经过几十年验证。
试了两个月 UDP 之后,我们彻底放弃了,转向基于 TCP 的方案。
三、纯 WebSocket 也不够:任务和遥测要分开
放弃 UDP 之后,我们一开始用的是纯 WebSocket 方案——所有通信(任务下发、状态上报、遥测数据、告警)都走 WebSocket。
WebSocket 确实好用——双向通信、基于 TCP 可靠、浏览器原生支持、前端做实时监控方便。用了一段时间,发现了新问题。
问题一:高频遥测数据「淹没」了低频任务指令
机器人的遥测数据(位置、速度、电量、温度)是高频的——每 100ms 发一次,一台机器人每秒发 10 条。50 台机器人就是每秒 500 条遥测消息。
而任务指令是低频的——可能几分钟才发一条任务。
这两种消息走同一个 WebSocket 通道,问题就来了:
- 高频遥测消息把通道占满了,低频任务指令的延迟变大
- 有时候任务指令要等好几条遥测消息发完才能发出去
- 网络不好的时候,遥测消息堆积,任务指令被堵在后面
就像一条马路上,自行车(遥测,多而轻)和救护车(任务,少但紧急)混在一起走,救护车被自行车堵了。
问题二:遥测数据丢一帧没事,任务指令丢一条就是事故
遥测数据和任务指令的可靠性要求也不一样:
- 遥测数据:位置数据 100ms 发一次,丢一帧没关系,下一帧就补上了。不需要确认、不需要重传。
- 任务指令:「急停」「取消任务」「开始执行」这些指令,一条都不能丢,丢了可能出事故。必须确认、必须重传、必须保证送达。
如果都走 WebSocket,要么都加确认机制(浪费资源,遥测数据不需要确认),要么都不加(任务指令不可靠)。
问题三:离线消息怎么办?
机器人在移动过程中,网络可能短暂中断(进电梯、过拐角、WiFi 切换)。如果中断期间平台下发了任务,机器人上线后能收到吗?
纯 WebSocket 没有离线消息机制——连接断了之后发的消息,重连后收不到。任务下发了但机器人没收到,平台以为下发成功了,机器人以为没任务,两边状态不一致。
这时候我们意识到,需要一个支持 QoS 和离线消息的协议来做任务通道——MQTT 正好合适。
四、最终方案:WebSocket + MQTT 双通道
踩了这些坑之后,我们最终定了 WebSocket + MQTT 双通道方案:
- WebSocket 通道:传实时遥测数据(位置、速度、电量、健康状态、关键事件)。高频、可容忍偶尔丢包、追求低延迟。
- MQTT 通道:传任务下发、执行反馈、控制指令、配置变更。低频、必须可靠送达、需要 QoS 保证和离线消息。
就像马路分车道——自行车走非机动车道,救护车走应急车道,互不干扰。
为什么这样分工?
| 特性 | WebSocket(遥测通道) | MQTT(任务通道) |
|---|---|---|
| 消息类型 | 位置、速度、电量、状态 | 任务、指令、配置、反馈 |
| 频率 | 高频(10-100ms 一次) | 低频(几秒到几分钟一次) |
| 可靠性要求 | 可容忍偶尔丢包 | 必须可靠送达 |
| QoS | 不需要 | QoS 1(至少一次) |
| 离线消息 | 不需要 | 需要(断网期间的任务上线后投递) |
| 延迟要求 | 极低(毫秒级) | 低(百毫秒级即可) |
| 前端支持 | 浏览器原生支持 | 需要 MQTT.js 库 |
双通道的好处
1. 职责分离,互不干扰
高频遥测数据不会堵塞低频任务指令,任务指令的延迟有保障。
2. 可靠性分级,资源优化
遥测数据不做确认和重传,节省带宽和算力;任务指令用 QoS 1 保证送达,该花的资源花在刀刃上。
3. 故障隔离
WebSocket 通道出问题(比如遥测数据太多把连接撑爆了),不影响 MQTT 任务通道——任务还能正常下发和执行。反过来也一样。
4. 技术选型灵活
WebSocket 网关和 MQTT broker 可以用不同的技术栈、不同的服务器、独立扩容。连接数多了扩 WebSocket 网关,消息量大了扩 MQTT 集群。
五、可靠性机制实战:心跳、重连、幂等、离线缓存
协议选好了只是第一步,要让通讯在生产环境里稳定运行,还需要一套可靠性机制。分享几个我们实际项目中用的机制。
心跳:怎么知道对方还活着?
连接建立了,怎么知道对方还活着?网络可能断了、设备可能死机了、电缆可能被碰掉了。
心跳机制就是双方定期发一个「我还活着」的小消息:
- WebSocket 通道:客户端每 30 秒发一个 ping,服务器回 pong。如果连续 90 秒没收到 pong,认为连接断了,触发重连。
- MQTT 通道:MQTT 协议内置了 Keep Alive 机制,连接时设置 60 秒,broker 自动检测连接状态。如果客户端异常断开,broker 会发布遗嘱消息(Last Will),通知其他客户端「这台机器人掉线了」。
心跳间隔的选择:太短(5 秒)浪费带宽和算力,太长(5 分钟)断开后很久才发现。我们的经验是 WebSocket 30 秒、MQTT 60 秒,兼顾实时性和资源消耗。
重连:断了怎么自动恢复?
机器人在移动中,网络不可能永远稳定——WiFi 漫游、4G/5G 信号盲区、网线被碰掉,都可能导致连接中断。
自动重连机制必须做好:
- 指数退避:断了之后等 1 秒重连,失败等 2 秒,再失败等 4 秒、8 秒、16 秒……最长不超过 5 分钟。避免服务器挂了的时候客户端疯狂重连把服务器压垮。
- 重连后恢复状态:WebSocket 重连后,要重新订阅需要的数据流,请求最新的状态快照(因为断线期间可能有状态变化)。MQTT 如果设置了 Clean Session = false,重连后 broker 会自动恢复订阅和投递离线期间的消息。
- 断线期间本地缓存:机器人端断线期间产生的遥测数据和任务反馈,先存在本地(内存或磁盘),重连后批量上报。缓存要有大小限制和过期策略——遥测数据超过 5 分钟的就可以丢了(重连后直接获取最新状态就行),任务反馈要存久一点(24 小时)。
幂等:重复消息怎么办?
网络不稳定的时候,消息可能重复——MQTT QoS 1 保证「至少一次」,可能收到两次;重连后离线消息重新投递,可能和之前的消息重复。
如果消息不是幂等的,重复处理就会出问题:
- 任务下发重复——同一个任务执行两次
- 「完成数量 +1」重复——数据统计错误
- 急停指令重复——第二次急停可能把已经恢复的机器人又停下
幂等的实现方式:
- 每条消息带唯一 msg_id(UUID v4),接收方维护一个「最近处理过的 msg_id 列表」(比如最近 1000 条或最近 1 小时),收到重复的直接丢弃。
- 状态机约束——任务状态是单向流转的,已经是 Success 的任务,再收到「执行中」的反馈直接忽略。
- 操作本身幂等——「设置速度为 0.5m/s」「移动到坐标 (1000,500)」「删除 ID 为 123 的任务」这些操作天然幂等,执行一次和十次结果一样。设计消息的时候尽量让操作本身幂等。
超时与回执:任务下发了怎么知道成没成?
任务下发给机器人,怎么知道机器人收到了、开始执行了、执行完了?
我们用三层回执:
- 受理回执——机器人收到任务,校验通过,发回「已受理」。平台收到后把任务状态从「待执行」改成「执行中」。如果 10 秒没收到受理回执,重发任务。
- 步骤回执——每个步骤完成时发回「步骤 X 完成」。平台更新进度。如果某个步骤超时(比如导航 60 秒没完成),触发告警或重试。
- 终态回执——任务最终完成(成功或失败)时发回「任务结束」。平台更新为终态。如果总超时(比如 10 分钟还没结束),主动查询机器人状态。
三层回执就像寄快递——「已揽收」(受理)、「运输中」(步骤)、「已签收」(终态),每个环节都有确认,不会出现「寄出去了就石沉大海」的情况。
六、踩坑总结:通讯架构选型的几点经验
回头看通讯架构选型这段经历,总结几点:
1. 不要为了「快」用 UDP
在机器人控制场景里,可靠性比延迟重要。UDP 不保证送达,用在控制通道上是安全隐患。而且企业防火墙经常拦 UDP,部署麻烦。基于 TCP 的 WebSocket 和 MQTT 足够快,也足够可靠。
2. 遥测和任务要分通道
高频遥测数据和低频任务指令的特性完全不同——频率、可靠性要求、延迟要求都不一样。混在一个通道里互相干扰,分通道才是正确做法。WebSocket 传遥测,MQTT 传任务,各司其职。
3. MQTT 的 QoS 和离线消息是真的香
做物联网/机器人通信,MQTT 的 QoS 机制(保证送达)、遗嘱消息(异常断开通知)、离线消息(断网期间消息不丢)这些特性,能帮你省掉很多自己造轮子的工作。EMQX 这个开源 MQTT broker 也很好用,性能强、功能全、社区活跃。
4. 可靠性机制要做全
心跳、重连、幂等、超时、回执——这些机制看起来琐碎,但每一个都是生产环境里踩坑踩出来的。少了任何一个,都可能在某个时刻出问题。建议一开始就把这些机制做好,不要等出了问题再补。
5. 监控通讯质量
要监控关键指标——连接数、连接成功率、平均连接时长、消息延迟、重连次数、消息丢失率、MQTT 队列深度。这些指标能帮你提前发现问题,而不是等客户投诉了才知道通讯出了故障。
写在最后
通讯架构是机器人调度平台的「血管」——血管不通,再聪明的大脑也指挥不了身体。我们在这个问题上踩了不少坑,从 UDP 到纯 WebSocket 再到 WebSocket + MQTT 双通道,试了好几轮才找到合适的方案。
这些经验都是我们(越微智能)在实际项目中一点点踩坑踩出来的,不是纸上谈兵。我们团队一直在做机器人二次开发、多品牌调度、工业 AI 视觉这些落地工作,通讯架构是每天都要面对的问题。如果你也在做机器人通信、ROS2 开发、多品牌调度相关的工作,欢迎交流,我们一起踩坑、一起进步。
关于作者: 越微智能(Yuewell)是一支专注具身智能与工业 AI 视觉落地的技术团队,提供全品牌机器人二次开发(适配宇树/优必选/智元/傅利叶等)、ROS2 系统开发、工业级视觉算法定制(30+ 算法)、具身机器人调度管理平台、RK3588 边缘一体机等产品和服务,支持从算法、硬件到产线实机部署的全栈交付。
下一篇预告: 《一套任务模型适配所有品牌:我们是怎么设计参数化命令数组的》,我们将分享任务模型设计的踩坑经历——为什么命令字符串、嵌套结构都不好用、参数化命令数组好在哪里、标识符/单位/时间这些「小事」为什么决定了平台的可维护性,敬请关注。