目录
[二、 协议实战:数据的一生(从下单到车轮转动)](#二、 协议实战:数据的一生(从下单到车轮转动))
[场景 1:用户下单,车辆启动(MQTT vs HTTP)](#场景 1:用户下单,车辆启动(MQTT vs HTTP))
[场景 2:固件有 Bug,需要紧急修复(HTTP 的优势就体现出来了)](#场景 2:固件有 Bug,需要紧急修复(HTTP 的优势就体现出来了))
[场景 3:大脑思考,规划路线(ROS 2 & DDS)](#场景 3:大脑思考,规划路线(ROS 2 & DDS))
[场景 4:大脑指挥躯干,车轮转动(SocketCAN & CANopen)](#场景 4:大脑指挥躯干,车轮转动(SocketCAN & CANopen))
[场景 5:风险:全车断电(CoAP 极限救场)](#场景 5:风险:全车断电(CoAP 极限救场))
[三、 核心代码实战:打通"大脑"与"小脑"之间的联系](#三、 核心代码实战:打通“大脑”与“小脑”之间的联系)
[四、 总结:协议没有优劣,只有分工](#四、 总结:协议没有优劣,只有分工)
今天,我们就来做一个集大成的项目 。我们要设计并构建一款**"室外自动驾驶物流小车"**的通信架构。
在这个项目中,前 8 天学到的每一个协议(TCP/IP, HTTP, MQTT, CoAP, CAN, CANopen, ROS 2, DDS)都有它不可替代的位置。我会带你看着数据是如何在这些协议之间流转,它们是如何互补、配合,甚至是如何救命的。
假设老板给了你 500 万预算,让你带队研发一款"室外自动驾驶物流配送车"。这辆车有底盘、有雷达、要连云端、还要支持手机 APP 下单。
我们要把这 9 天学到的所有协议,全部塞进这辆车里,让它们像齿轮一样精密咬合。
一、整车通信架构全景图
首先,我们要把这辆车"解剖"成三个核心域。每个域因为任务不同,必须使用完全不同的协议。
-
云端交互域(互联网层): 车与手机、服务器的通信。
-
关键词: 远程、高延迟、弱网。
-
主角: MQTT, HTTP。
-
-
高性能计算域(大脑层): 也就是你的工控机(NVIDIA Orin 或 x86 主机)。
-
关键词: 海量数据、微秒级、进程间通信。
-
主角: ROS 2 (DDS), TCP/UDP。
-
-
实时控制域(小脑/躯干层): 也就是你的 STM32 底盘控制器。
-
关键词: 毫秒级硬实时、电机驱动、绝对安全。
-
主角: CAN, CANopen, micro-ROS。
-
-
极限生存域(黑匣子): 独立供电的定位模块。
-
关键词: 极低功耗、断电报警。
-
主角: CoAP (NB-IoT)。
-
二、 协议实战:数据的一生(从下单到车轮转动)
好,现在让我们跟踪一条指令,看它如何在这些协议之间变身、流转。
场景 1:用户下单,车辆启动(MQTT vs HTTP)
需求: 用户在手机 APP 上点了一个"开始配送"。
-
为什么不用 HTTP?
如果用 HTTP,车子必须每秒钟轮询服务器:"有单吗?有单吗?"这太傻了。
-
最佳实践(MQTT):
车辆一开机,就通过 TCP 连上云端,并 Subscribe(订阅) 主题
vehicle/1001/cmd。用户点击按钮 -> 云端 Broker 收到请求 -> MQTT 推送 -> 车辆瞬间收到 JSON 指令
{"action": "start", "target": "Gate_A"}。(Day 4 的知识点:这里用到了 MQTT 的 QoS 1,保证指令绝对送达,不仅快,还省流量。)
场景 2:固件有 Bug,需要紧急修复(HTTP 的优势就体现出来了)
需求: 车辆收到指令后发现版本过低,需要下载 50MB 的升级包。
-
为什么不用 MQTT?
MQTT 适合传短小的指令,不适合传大文件。如果用 MQTT 传 50MB,一旦中间断了一次,很难断点续传,且 Broker 压力巨大。
-
最佳实践(HTTP):
车辆通过 MQTT 收到一个下载链接(URL)。
然后,车辆启动一个 HTTP GET 请求(Day 3 的知识点),去文件服务器拉取
.bin固件。因为 HTTP 有成熟的断点续传和 CDN 加速,下载大文件稳如老狗。
场景 3:大脑思考,规划路线(ROS 2 & DDS)
需求: 固件更新完毕,车辆开始自动驾驶。激光雷达(Lidar)每秒产生 20MB 的点云数据,摄像头产生高清视频流。
-
为什么不用 MQTT?
数据量太大,而且都在一台电脑内部(或局域网),用 MQTT 绕一圈云端再回来,黄花菜都凉了。
-
为什么不用 CAN?
CAN 一帧才 8 字节,传图片?别闹了。
-
最佳实践(ROS 2 + DDS):
感知节点(Perception Node)发布
/lidar/points主题。规划节点(Planning Node)订阅该主题。
DDS(Day 8 的知识点) 在底层通过 Shared Memory(共享内存) 或者 UDP 组播,以几乎零拷贝的方式,瞬间把海量数据甩给规划算法。
- QoS 配置: 雷达数据用
BestEffort(丢一帧无所谓,我要最新的);控制指令用Reliable(必须送达)。
- QoS 配置: 雷达数据用
场景 4:大脑指挥躯干,车轮转动(SocketCAN & CANopen)
需求: 规划算法算出来了,需要左轮转速 100 RPM,右轮 120 RPM。这个指令怎么发给电机?
这里有一个极其关键的**"跨界桥梁"**。
-
步骤 A(Linux 侧): ROS 2 节点计算出速度,通过 SocketCAN(Day 6 的知识点) 驱动,把速度数据打包成 CAN 报文。
-
步骤 B(CAN 总线): 数据变成了差分信号,抗住电机干扰,传到底盘。
-
步骤 C(电机侧): 伺服电机遵循 CANopen 协议(Day 7 的知识点)。
ROS 2 发来的 8 字节数据,被直接映射到电机的 对象字典 (OD) 索引
0x60FF(目标速度)。- 协议配合: 这里 ROS 2 负责"脑力",CAN 负责"传输",CANopen 负责"翻译"。
场景 5:风险:全车断电(CoAP 极限救场)
需求: 车子掉沟里了,主电池撞坏断电,Linux 关机,MQTT 掉线。车辆彻底失联。我们怎么找到它?
-
最佳实践(CoAP + NB-IoT):
车上还有一个独立的、由纽扣电池供电的黑匣子(GPS 定位器)。
它检测到主电源断开,立刻苏醒。
因为它电量极少,跑不起 TCP,也连不上 4G 高速网。
它连接 NB-IoT 网络 ,通过 UDP 发送一个极其简短的 CoAP 报文(Day 5 的知识点):
POST /alarm payload: {lat: 39.9, lon: 116.4}仅消耗几十字节流量,确保在电池耗尽前把位置发回服务器。
三、 核心代码实战:打通"大脑"与"小脑"之间的联系
整个系统最难的地方,在于 ROS 2 (DDS) 怎么和 CAN 总线 对话。
下面这段代码,就是 "ROS2-CAN Bridge"。它一边通过 DDS 接收 AI 的指令,一边通过 SocketCAN 指挥电机。
#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp" // ROS2 标准速度消息
#include <linux/can.h>
#include <linux/can/raw.h>
#include <sys/socket.h>
// 定义 CANopen 电机控制 ID
#define LEFT_MOTOR_ID 0x201
#define RIGHT_MOTOR_ID 0x202
class CanBridgeNode : public rclcpp::Node {
public:
CanBridgeNode() : Node("can_bridge_node") {
// 1. 初始化 SocketCAN (Day 6 知识点)
can_socket_ = socket(PF_CAN, SOCK_RAW, CAN_RAW);
struct ifreq ifr;
strcpy(ifr.ifr_name, "can0");
ioctl(can_socket_, SIOCGIFINDEX, &ifr);
struct sockaddr_can addr;
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(can_socket_, (struct sockaddr *)&addr, sizeof(addr));
// 2. 订阅 ROS 2 的控制指令 (Day 8 知识点)
// 只要 AI 算法发布了 /cmd_vel,这里就会收到
sub_ = this->create_subscription<geometry_msgs::msg::Twist>(
"/cmd_vel", 10, std::bind(&CanBridgeNode::cmd_callback, this, std::placeholders::_1));
RCLCPP_INFO(this->get_logger(), "ROS2-CAN 网关已启动!");
}
private:
void cmd_callback(const geometry_msgs::msg::Twist::SharedPtr msg) {
// 3. 协议转换逻辑
// ROS 的线速度 (m/s) -> 转换为电机的转速 (RPM)
int32_t left_rpm = (msg->linear.x - msg->angular.z * WHEEL_BASE) * RPM_RATIO;
int32_t right_rpm = (msg->linear.x + msg->angular.z * WHEEL_BASE) * RPM_RATIO;
// 4. 发送 CANopen PDO 报文 (Day 7 知识点)
send_can_frame(LEFT_MOTOR_ID, left_rpm);
send_can_frame(RIGHT_MOTOR_ID, right_rpm);
}
void send_can_frame(uint32_t id, int32_t rpm) {
struct can_frame frame;
frame.can_id = id; // CANopen 节点 ID
frame.can_dlc = 4; // 数据长度 4 字节
// 将 RPM 数据填入 CAN 报文 (小端模式)
// 这里实际上是在通过 PDO 写入电机的 0x60FF 目标速度字典
memcpy(frame.data, &rpm, 4);
// 5. 写入底层 Socket (Day 2/6 知识点)
write(can_socket_, &frame, sizeof(frame));
}
int can_socket_;
rclcpp::Subscription<geometry_msgs::msg::Twist>::SharedPtr sub_;
const float WHEEL_BASE = 0.5; // 轮距
const float RPM_RATIO = 1000.0;
};
四、 总结:协议没有优劣,只有分工
在这 9 天的IOT学习中,我们终于把这张拼图拼完整了。
-
TCP/IP 是修路的(地基)。
-
MQTT 是跑长途物流的(连云端)。
-
HTTP 是搞大宗贸易的(下固件)。
-
CoAP 是特种兵通信(低功耗救命)。
-
ROS 2 (DDS) 是大脑内部的高速神经网络(AI 协同)。
-
CAN/CANopen 是控制肌肉的神经末梢(硬实时控制)。
真正的嵌入式/机器人架构师,不是只懂某一个协议,而是懂得在什么场景下,把最合适的协议放在最合适的位置。
这 9 天的IOT学习到此结束,但我的技术之路才刚刚开始。之后有时间准备拿着这份地图,去构建我自己的机器人!
这套架构图其实就是目前市面上主流无人车(美团、京东、百度 Apollo)的简化版通信模型。
大家觉得这个系列怎么样?如果未来我要开新的坑,你们想看 Linux 驱动开发 ,还是 RTOS 内核源码分析?评论区告诉我!