为什么机器人通信不用 JSON?「固定长度二进制协议」的设计智慧
文章目录
为什么机器人通信不用 JSON?「固定长度二进制协议」的设计智慧
最近在读一个机器人的通信协议 SDK,一开始很不理解:为什么不用 JSON 这种"人类友好"的格式,偏要搞一堆看不懂的字节、还规定每个字段固定在第几个字节?彻底想明白后,发现这套设计简单、快、可靠,非常适合嵌入式/实时通信。写这篇文章记录一下。
一、先看这套协议长什么样
一个机器人系统有三个"端"要互相通信:上位机(C++)、主控板(ROS)、单片机(C)。它们之间传的数据帧格式是固定的:
AA 55 | ver | len | type | payload | crc16
其中"控制指令"这一帧,payload 里有十几个字段,每个字段固定在某个字节位置:
| 偏移 | 字段 | 类型 | 说明 |
|---|---|---|---|
| 0 | mode | uint8 | 运行模式 |
| 1 | enabled | uint8 | 使能标志 |
| 2 | reserved | uint8×2 | 保留 |
| 4 | vx | float32 | 前后速度 |
| 8 | vy | float32 | 左右速度 |
| 12 | vz | float32 | 上下速度 |
注意"偏移"这一列:vx 固定在第 4 字节,vy 固定在第 8 字节,永远不变。
二、核心概念一:固定偏移(字段位置写死)
payload 就是几十个字节排成的一排,从 0 开始编号。偏移 = “这个字段从第几个字节开始”。
字节编号: 0 1 2 3 4 5 6 7 8
内容: mode en res res vx vx vx vx vy
↑ ↑ ↑ ↑ ↑
偏移0 偏移1 偏移2 偏移4(vx 从这里开始,占 4 字节)
为什么 vx 在偏移 4?因为前面 mode(1字节) + enabled(1字节) + reserved(2字节) = 4 字节,占掉了 0~3,vx 就从第 4 字节开始。
三、核心概念二:全量发送(每次都发完整状态)
这套协议是全量发送:不管字段变没变,每次都把整帧发出去。
比如你只想让机器人前进(改 vx),但 mode、enabled、vy、vz……所有字段都会一起发,没变的就发它当前的值。
对比"增量发送"(只发变化的部分):
| 全量发送 | 增量发送 | |
|---|---|---|
| 接收方要做啥 | 直接读全部字段 | 先记住上次状态,再打补丁 |
| 解析难度 | 最简单 | 复杂 |
| 丢一帧会怎样 | 下一帧还是完整状态,没事 | 状态错乱(丢了增量) |
实时控制场景特别需要全量发送——通信容易受干扰丢包,全量发送丢一帧也无所谓,下一帧又是完整状态。
四、最妙的一点:接收方不需要知道"哪里变了"
一开始我最困惑的是:“每次发全量,解析时不知道哪个字段变了,岂不是很费劲?”
后来想明白:根本不需要知道哪里变了。
接收方拿到一帧,解码后直接就是完整的最新状态:
auto tele = link.recv_frame_as<TelemetryPacket>(...);
tele.position; // 现在的位置
tele.angle; // 现在的角度
tele.voltage; // 现在的电压
你只需要知道"现在 position 是多少",直接显示就行,不用关心它从多少变到了多少。
打个比方:全量发送 = 每次发一张完整的仪表盘截图,你看截图就知道所有指针位置;增量发送 = 只告诉你"哪个指针动了",你还得记住上一张截图才能拼出当前状态。
如果确实要"检测变化"(比如某个状态从 0 变 1 时报警),那是上层应用自己的逻辑,自己比较新旧值就行,协议不负责:
if (tele.alarm == 1 && last_tele.alarm == 0) {
// 触发报警
}
last_tele = tele;
五、为什么不用 JSON?
同样一条控制指令,用 JSON 长这样:
{"mode":0,"enabled":1,"vx":0.5,"vy":0,"vz":0}
对比固定二进制布局:
| JSON(文本协议) | 固定二进制协议 | |
|---|---|---|
| 字节数 | 多(一堆字段名 + 标点) | 少(全是值,几十字节) |
| 解析 | 慢(逐字符扫、找 key、切字符串) | 快(按偏移直接读) |
| 字段顺序 | 可能乱,要处理各种情况 | 固定,不可能乱 |
| 嵌入式友好 | 差(解析开销大) | 好(直接 memcpy 到 struct) |
数据量其实很小:就算 10Hz 发送,也才约 1~2 KB/秒,而网线带宽是 MB 级,随便发。
六、为什么这套设计适合嵌入式/实时场景
- 快:位置固定,接收方直接按偏移读,比解析 JSON 快几个数量级。
- 省:不传字段名和标点,字节最少。
- 可靠:帧长固定、位置固定,多个端不可能不一致(都用同一张偏移表)。
- 嵌入式友好:单片机资源少,收到字节直接 memcpy 到结构体就还原成字段了。
- 确定性强:帧长固定,接收方精确知道一帧多长,好做校验。
七、总结
这套协议的设计哲学就一句话:
固定位置 + 全量发送 = 最简单、最快、最可靠。
它牺牲了"只发变化部分"这种省字节的技巧,换来了:
- 解析不需要任何查找/判断,按位置读就行
- 接收方不需要关心"哪里变了",直接拿完整状态
- 多个端(C++/C/ROS)用同一张偏移表,字节级一致
规律:越是底层、越实时、越资源受限的地方,越爱用"固定长度二进制协议";越是 Web、越要给人看的地方,越爱用 JSON 这种"自描述文本协议"。两种没有好坏,只有合不合适。
如果你也在做多端通信、嵌入式、实时控制这类需求,强烈建议理解并尝试这套"固定偏移 + 全量发送"的思路——它简单得优雅。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)