2. MAVLink协议栈架构:物理层、数据链路层、网络层、传输层在MAVLink中的体现

很多刚接触MAVLink的朋友会问我:「这玩意儿到底算哪一层的协议?」说实话,我第一次看MAVLink的代码时也有这个困惑。它不像TCP/IP那样层次分明,但仔细拆解后你会发现,MAVLink确实在四个层面上都有自己的实现逻辑。只不过,它把很多工作揉进了数据链路层和传输层里。

我个人习惯把MAVLink的协议栈想象成一个「轻量级的通信骨架」。它不需要像OSI七层模型那样面面俱到,但该有的东西一个不少。下面我带你一层一层剥开来看。

核心观点: MAVLink的协议栈是「按需裁剪」的。物理层完全交给硬件,数据链路层是MAVLink的主战场,网络层只在UDP模式下存在,传输层则靠消息ID和确认机制来保证可靠性。

MAVLink协议栈四层架构图 MAVLink 协议栈四层架构 物理层(Physical Layer) UART / USB / WiFi / 蓝牙 / 以太网 数据链路层(Data Link Layer) 帧结构(STX/LEN/SEQ/SYS/COMP/MSG/PAYLOAD/CK) 网络层(Network Layer) 系统ID(System ID)+ 组件ID(Component ID)→ 逻辑寻址 传输层(Transport Layer) 消息ID + 确认机制 + 重传策略 + 分片重组 MAVLink 协议栈 硬件 核心 可选 增强

2.1 物理层:MAVLink不关心,但你不能不管

MAVLink本身不定义物理层。它只规定「数据以字节流的形式在链路上传输」。至于这个字节流走的是串口、USB虚拟串口、WiFi UDP还是蓝牙RFCOMM,MAVLink一概不管。

我在项目中遇到过最典型的场景:飞控通过UART与数传电台连接,电台走433MHz或915MHz频段。这时候物理层就是UART+无线电。后来换成了4G模块,物理层变成了PPP over LTE。MAVLink代码一行没改,照样跑。

我的建议: 选物理层时重点关注两个指标——波特率和误码率。串口场景下,115200 bps是起步,我现在习惯用57600甚至921600。误码率高的链路(比如远距离数传),一定要在数据链路层开CRC校验,MAVLink 2.0默认就开着。

常见的物理层载体包括:

  • UART/串口:最经典,飞控与数传、地面站之间的主力通道
  • USB:地面站直连飞控,走CDC ACM虚拟串口
  • WiFi UDP:高带宽场景,比如机载计算机与地面站之间的视频+数据混合链路
  • 蓝牙/BLE:手机地面站与小型无人机之间的短距连接
  • 以太网:集群无人机的地面骨干网络

2.2 数据链路层:MAVLink的「主战场」

这一层是MAVLink最核心的部分。说白了,它定义了「一帧数据长什么样」。你打开任何一段MAVLink的抓包日志,看到的十六进制数据就是按照这个帧结构排列的。

MAVLink 2.0的帧结构如下:

偏移 名称 长度(字节) 说明
0STX1帧起始标志,MAVLink 2.0 固定为 0xFD
1LEN1有效载荷长度(0-255)
2INC_FLAGS1不兼容标志位(如签名启用)
3CMP_FLAGS1兼容标志位
4SEQ1序列号,用于丢包检测
5SYS1系统ID(发送方)
6COMP1组件ID(发送方)
7-9MSG3消息ID(低24位)
10..10+LEN-1PAYLOADLEN有效载荷数据
10+LENCK2CRC16校验(包含整个帧头+载荷)
10+LEN+2SIG13可选签名(启用INC_FLAGS位时存在)

你看这个结构,每个字段都有明确职责。STX是帧头,告诉接收方「新的一帧开始了」。SEQ序列号是我个人觉得最实用的设计——接收方只要检查SEQ是否连续,就能知道中间有没有丢包。

我曾经踩过的坑: 早期用MAVLink 1.0时,STX是0xFE,MSG ID只有1字节(最多255种消息)。后来MAVLink 2.0把STX改成0xFD,MSG ID扩展到3字节。如果你在同一个链路上混用1.0和2.0的设备,接收方会解析错帧。我当年调试时花了整整两天才发现是版本混用的问题。切记:同一链路上所有设备的MAVLink主版本必须一致。

2.3 网络层:系统ID + 组件ID = 逻辑寻址

MAVLink的网络层非常轻量。它没有IP地址,没有路由表,只靠两个ID来定位通信节点:

  • 系统ID(System ID):标识一个独立的无人机系统。比如一架飞机是一个系统,地面站是另一个系统。
  • 组件ID(Component ID):标识系统内的一个功能模块。比如飞控是1号组件,GPS是2号组件,相机是3号组件。

你想想看,一个地面站同时控制三架无人机,每架无人机上又有飞控、GPS、云台三个组件。这时候通信怎么寻址?

很简单:地面站发送一条消息,目标系统ID=2,目标组件ID=1,这条消息就只被2号飞机的飞控接收。其他飞机和组件看到ID不匹配,直接丢弃。

关键点: MAVLink的网络层是「广播+过滤」模式。所有设备共享同一个物理信道,但每个设备只处理与自己系统ID和组件ID匹配的消息。这种设计在串口总线上特别高效——不需要路由协议,不需要ARP,零开销。

不过要注意,MAVLink的网络层只在多系统场景下才真正发挥作用。如果你只是飞控直连地面站,系统ID固定为1和255,组件ID固定为1,网络层基本是透明的。

2.4 传输层:消息ID + 确认 + 重传

传输层是MAVLink里最容易被忽视的一层。很多人以为MAVLink只是「发个消息就完事了」,其实它有一套完整的传输保障机制。

传输层的核心要素有三个:

  1. 消息ID(MSG ID):每个消息类型有唯一的24位ID。比如HEARTBEAT是0,SYS_STATUS是1,GPS_RAW_INT是24。接收方根据MSG ID就知道该用哪个结构体来解析PAYLOAD。
  2. 确认机制(ACK/NACK):MAVLink定义了COMMAND_ACK等消息,用于确认命令是否被正确执行。不是所有消息都需要确认,但关键指令(比如解锁、起飞、航点上传)必须有ACK。
  3. 重传策略:MAVLink协议本身不强制重传,但上层协议(比如Mission Protocol、Command Protocol)会定义超时重传逻辑。我习惯在发送关键指令后启动一个500ms的定时器,没收到ACK就重发,最多重试3次。

我的经验: 在丢包率高的链路上(比如远距离数传),建议把HEARTBEAT的发送间隔从1秒缩短到0.5秒。这样接收方可以更快地检测到链路中断。但别太短——我见过有人设成50ms,结果整个信道被心跳包塞满,正常数据反而发不出去了。

另外,MAVLink 2.0还支持消息分片(Fragmentation)。当一条消息的PAYLOAD超过255字节时,发送方会自动拆成多个帧,接收方再重组。这个功能在传输大尺寸的日志文件或参数列表时特别有用。

2.5 四层架构的协同工作

说了这么多,你可能觉得四层是各自独立的。其实在实际运行中,它们紧密配合。我画个流程图你就明白了:

MAVLink消息发送流程 MAVLink 消息发送流程(四层协同) 应用层(用户代码) 传输层:分配MSG ID 网络层:填入SYS/COMP 数据链路层:封装帧+CRC 各层职责 ① 用户调用 mavlink_msg_xxx_pack() ② 传输层设置消息ID ③ 网络层设置系统ID/组件ID ④ 数据链路层添加帧头、 序列号、CRC校验 ⑤ 物理层将字节流发送出去 ⑥ 接收方反向解析 (CRC验证→ID匹配→解析)

你看这个流程,从应用层调用打包函数开始,一路向下经过传输层、网络层、数据链路层,最后通过物理层发出去。接收方收到后,从物理层往上反向解析,每一层做自己的事。

我个人觉得,理解这个四层架构最大的价值在于:当你遇到通信问题时,能快速定位是哪一层出了故障。

  • 如果完全收不到数据,先查物理层——串口线松了?波特率配错了?
  • 如果能收到但解析乱码,查数据链路层——CRC校验失败?帧头不对?
  • 如果能解析但消息没反应,查网络层——系统ID和组件ID配对了没?
  • 如果能收到但命令没执行,查传输层——ACK超时了?重传次数用完了?

总结一下: MAVLink的四层架构虽然不像TCP/IP那样严格分层,但每一层都有明确的职责边界。物理层交给硬件,数据链路层负责帧封装和校验,网络层负责逻辑寻址,传输层负责消息标识和可靠性保障。你在实际开发中,只要把这四层的分工理清楚,遇到问题就能像剥洋葱一样一层一层排查。