上一篇,我们把服务型机器人的整体软件架构串了起来:

Android 控制屏
↓ TCP
Linux 机器人主控
↓ 串口 / CAN
MCU

底盘、电机、灯光、柜门、传感器

其中:

Android 负责业务与人机交互;

Linux 主控负责机器人核心任务和设备控制;

MCU 负责更加底层的硬件执行。

那么接下来就会遇到一个非常实际的问题:

Android 控制屏和 Linux 机器人主控之间,到底应该怎么通信?

在实际服务机器人项目中,一个非常典型的方案就是:

Android 控制屏

TCP 长连接

Linux 机器人主控

通常由机器人主控启动 TCP Server,Android 控制屏作为 TCP Client 主动连接。

看到这里,很多人可能会产生疑问:

现在 HTTPS、WebSocket、MQTT 都已经非常成熟了,为什么机器人内部通信还会直接使用 TCP?

这一篇,我们就把这条链路彻底串起来。


一、先别急着选协议,先看 Android 和主控之间到底在传什么

Android 控制屏可能会向机器人发送很多控制指令,例如:

  • 开始任务
  • 暂停任务
  • 继续任务
  • 停止任务
  • 前往某个点位
  • 返回充电桩
  • 打开柜门
  • 关闭柜门
  • 切换工作模式

而机器人主控也会不断向 Android 返回状态:

  • 机器人当前状态
  • 任务状态
  • 导航状态
  • 当前位置
  • 当前点位
  • 电量
  • 设备状态
  • 故障状态
  • 指令执行结果

这就意味着:

Android 和机器人主控之间,并不是偶尔通信一次。

而是需要进行长期、持续、双向的通信

例如:

用户点击:

前往会议室

Android 向主控发送:

GO_TO_POINT

pointId = meeting_room

主控接收到以后开始执行导航任务。

随后可能不断向 Android 返回:

NAVIGATION_START

NAVIGATING

NAVIGATING

ARRIVED

Android 根据这些状态不断刷新界面。

所以这里天然存在一个需求:

Android 和机器人主控之间需要保持一条长期存在的双向通信通道。

这就是长连接。


二、为什么不直接使用 HTTP?

如果之前主要做普通 Android App,我们最熟悉的通信方式通常是:

Android App

HTTPS

Spring Boot / 业务服务器

例如:

GET /device/status

POST /task/start

HTTP 非常适合普通业务接口。

但是机器人内部控制场景有一点不一样。

假设 Android 需要实时知道机器人当前的运行状态。

如果只使用普通 HTTP,那么 Android 就需要不断询问:

Android:

机器人现在什么状态?

主控:运行中。

过一会再问:

机器人现在什么状态?

主控:运行中。

再过一会:

机器人现在什么状态?

主控:已经到达。

这种方式其实就是: 轮询。

例如:

每 500ms 请求一次:

GET /robot/status

理论上当然可以做。

但是对于机器人内部这种高频、持续的状态通信来说,就显得比较笨重。

因为 Android 控制屏和 Linux 主控本身就在同一台机器人上。

两者之间通常处于稳定的局域网环境。

既然如此,与其不断:

请求

响应

等待

再次请求

不如直接建立一条长期连接:

Android

TCP

Linux 主控

连接建立以后:哪一方有消息,哪一方直接发送。


三、TCP 为什么适合机器人内部通信?

TCP 有几个特点,非常适合这种场景。

1. 双向通信

TCP 连接建立以后,双方都可以主动发送数据。

例如:

Android → 主控:

START_TASK

主控 → Android:

TASK_STARTED

随后主控还可以继续主动发送:

TASK_RUNNING

TASK_PROGRESS

TASK_FINISHED

也就是说:Android 不需要不断询问机器人状态。

主控状态发生变化以后,可以主动推送给 Android。


2. 长连接

TCP 连接建立以后,只要双方不主动关闭,并且网络没有异常,就可以一直保持。

例如机器人开机:

Android App 启动

连接 Linux 主控

TCP Connected

保持连接

持续收发消息

机器人运行几个小时,这条连接都可以一直存在。

这非常适合机器人这种:

控制端和主控长期同时在线的场景。


3. 可靠、有序传输

TCP 会帮我们处理很多底层网络问题,例如:

  • 数据重传
  • 数据顺序
  • 流量控制
  • 拥塞控制

例如 Android 连续发送:

START_TASK

PAUSE_TASK

RESUME_TASK

TCP 能够保证应用层最终读取到的字节顺序与发送顺序一致。

这对于控制类业务非常重要。

不过这里一定要注意一个词:

字节流。

TCP 保证的是:

可靠、有序的字节流。

但 TCP 并不知道什么叫:

“一条机器人消息”。

这会直接引出 TCP 开发里一个非常重要的问题:

粘包和半包。

这个问题我们后面会单独拿一篇讲。


四、为什么通常是机器人主控做 TCP Server?

服务机器人中很常见的一种架构是:

Linux 主控:TCP Server

Android 控制屏:TCP Client

也就是:

Android
↓ connect()
Linux 主控 TCP Server

为什么通常这样设计?

因为 Linux 主控才是机器人真正长期运行的核心节点。

机器人开机以后,主控通常会一直运行,并负责:

  • 导航
  • 定位
  • 任务
  • 底盘
  • 硬件
  • 状态管理
  • 设备通信

Android 控制屏更像是一个接入主控的业务终端。

所以主控启动:

TCP Server

监听一个固定端口,例如:

9000

Android 启动以后:

Socket(host, 9000)

主动连接主控。

整个关系就非常自然:

Linux 主控

提供机器人控制服务

Android

连接机器人控制服务

这和我们平时连接服务器的思维其实非常类似。


五、Android 怎么知道机器人主控的 IP?

这里又会出现一个很现实的问题。

Android 既然是 TCP Client,那么:

它必须知道 TCP Server 在哪里。

也就是至少需要知道:IP Port

例如:

192.168.1.10:9000

在机器人产品中,这个问题通常不会像互联网 App 那么复杂。

因为 Android 控制屏和机器人主控往往属于同一台机器人。

两者之间的网络拓扑通常是固定的。

比如:

Android 控制屏

192.168.10.2

Linux 主控

192.168.10.1

那么 Android 就可以直接连接:

192.168.10.1:9000

所以这种通信环境和:

手机 → Internet → 云服务器

完全不同。

它更像:

两个长期固定存在的设备,在一个内部局域网里进行点对点通信。

这也是原生 TCP 非常适合这种场景的重要原因。


六、TCP 和 WebSocket 到底是什么关系?

很多 Android 开发第一次接触这块都会有一个疑问:

既然 WebSocket 也能长连接、双向通信,为什么不直接使用 WebSocket?

这个问题非常重要。

因为:

WebSocket 底层本身就是建立在 TCP 之上的。

可以简单理解成:

应用协议

WebSocket

↓

TCP

↓

IP

也就是说,WebSocket 并没有绕开 TCP。

它是在 TCP 上面又定义了一层标准化协议。

因此

原生 TCP:自己定义消息协议

WebSocket:在 TCP 之上使用标准 WebSocket 协议

比如原生 TCP 可以自己定义:

  • 包头
  • 长度
  • 命令字
  • Sequence
  • Data
  • Checksum

而 WebSocket 已经帮你定义了一套 Frame 结构。

所以二者真正的区别并不是:

“一个能长连接,一个不能。”

实际上两个都能。

真正的区别是:

你是否需要 WebSocket 这一层应用协议


七、为什么机器人内部经常直接选择 TCP?

如果场景是:

Android 控制屏

↓

局域网

↓

Linux 机器人主控

而且双方都是自己开发的客户端和服务端,那么使用原生 TCP 往往非常自然。

因为这里并不需要解决浏览器兼容问题。

也不需要经过互联网中的各种 HTTP 基础设施。

系统通常就是:

一个 Android

对应

一个机器人主控

甚至网络环境本身都是固定的。

例如:

Android

192.168.1.2

↓

网线 / Wi-Fi / 内部网络

↓

Linux 主控

192.168.1.1

这种场景非常适合:

TCP + 自定义机器人协议。


八、什么时候 WebSocket 更合适?

这并不是说 WebSocket 不适合机器人。

WebSocket 很适合另外一种场景:

Web 管理后台

↓

互联网

↓

云平台

例如运营人员打开浏览器后台以后,需要实时查看:

  • 机器人位置
  • 任务状态
  • 告警
  • 电量
  • 在线状态

这时:

Browser

WebSocket

Cloud Server

就非常合理。

  • 因为浏览器天然支持 WebSocket。
  • 而浏览器不能像普通 Native 程序一样随意创建原生 TCP Socket。

所以经常可以看到这样的组合:

Android 机身屏
↕ TCP
Linux 主控

手机 App
↕ HTTPS / MQTT / WebSocket
云平台

Web 后台
↕ WebSocket
云平台

并不是哪一种协议更高级。

而是:

不同通信场景选择不同的协议。


九、TCP 和 MQTT 又有什么区别?

还有一个很常见的问题:

既然机器人项目已经用了 MQTT,为什么 Android 控制屏和主控之间不直接 MQTT?

因为两者解决的问题不同。

TCP 更适合:点对点

Android ↔ 主控

双方明确知道对方是谁。

MQTT 更适合:

设备 ↔ 云平台

以及:

发布 / 订阅

例如:

Robot A

↓

MQTT Broker

↓

云平台

↓

手机 App

MQTT 中间通常存在: Broker

而 Android 控制屏和机器人主控就在同一台机器人里。

=============================================================

如果仅仅为了:

Android ↔ 主控

再引入一个 Broker:

Android

Broker

主控

反而增加了系统复杂度。

=============================================================

所以:

机器人内部点对点通信:TCP

机器人连接云平台:MQTT

是一个非常常见的组合。


十、一条机器人 TCP 消息可能长什么样?

既然使用的是原生 TCP,那么应用层通常需要自己设计协议。

一个机器人消息可能类似:

  • Header
  • Length
  • Command
  • Sequence
  • Data
  • Checksum

例如:

AA55 | 0012 | 1001 | 000023 | DATA | CRC

其中:

Header

用于判断一个数据包从哪里开始。

Length

表示整个消息有多长。

Command

表示是什么指令。

例如:

1001 = START_TASK

1002 = STOP_TASK

1003 = GO_TO_POINT

Sequence

表示当前消息编号。

后面可以用于:

  • 指令回执
  • 超时
  • 重试
  • 消息匹配
  • Data

则是真正的业务数据。

例如:

pointId = 123

这些设计已经不属于 TCP 本身了。

而属于:

机器人应用层通信协议。

这也是机器人通信开发非常有意思的地方。

TCP 只负责:把字节可靠地送过去。

至于:这些字节是什么意思?

需要应用层自己定义。


十一、建立 TCP 连接只是第一步

看到这里,可能会感觉:

Android 创建一个 Socket 不就完了吗?

其实真正的机器人 TCP 开发,难点才刚刚开始。

一个稳定的机器人 TCP 模块至少还要考虑:

  • 连接状态
  • 心跳保活
  • 断线检测
  • 自动重连
  • 发送队列
  • 消息接收
  • 线程 / 协程模型
  • 粘包
  • 半包
  • 协议解析
  • 指令回执
  • 超时
  • 重试
  • 重复消息
  • 生命周期
  • 网络切换
  • 后台运行

所以:

Socket.connect()

可能只有几行代码。

但是一个真正稳定运行的机器人通信模块,背后其实是一套完整的连接管理系统。


十二、总结

Android 控制屏和机器人主控之间使用 TCP,并不是因为 TCP “比较底层”。

而是因为这个场景天然符合 TCP 的特点:

双方长期在线

↓

局域网点对点通信

↓

需要双向实时收发消息

↓

通信频率较高

↓

连接关系稳定

因此非常适合:

Android

TCP 长连接

Linux 机器人主控

而不同机器人通信场景,又会继续使用不同协议:

Android ↔ Linux 主控

TCP

Linux 主控 ↔ MCU

串口 / CAN

机器人 ↔ 云平台

MQTT / HTTPS

手机 / Web ↔ 云平台

HTTPS / WebSocket

实时音视频

WebRTC / RTC

到了这里,我们只是把:为什么选择 TCP 解释清楚了。

但真正开始写 TCP 代码以后,很快就会遇到另外一个问题:

  • 机器人运行过程中网络断了怎么办?
  • 连接什么时候算断开?
  • 多久发送一次心跳?
  • 重连应该一直疯狂重连吗?
  • 消息发送的时候连接刚好断了怎么办?

这就是下一篇要解决的问题:

第三篇:《机器人 TCP 长连接如何稳定运行:心跳、掉线检测与自动重连》

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐