1. MAVLink协议基础:协议概述、消息结构、CRC校验机制、心跳包机制

各位好,我是老张。做无人机通信这行十几年了,MAVLink 算是我打交道最多的协议之一。今天咱们就从最基础的东西聊起。

MAVLink 是什么?说白了,它就是无人机和地面站之间对话的「语言」。你想想看,飞控要告诉地面站「我现在高度 100 米,电池电压 12.5V」,地面站要告诉飞控「你往东飞 50 米」——这些信息怎么传?靠的就是 MAVLink 协议。

我最早接触 MAVLink 是在 2013 年,那时候还在用 v1.0 版本。当时有个项目,无人机在天上飞着飞着突然失联了,排查了三天,最后发现是消息 ID 冲突。嗯,从那以后我就养成了一个习惯:任何 MAVLink 项目,先把协议版本和消息 ID 表打印出来贴在墙上。

1.1 协议概述

MAVLink 是 Micro Air Vehicle Link 的缩写,由苏黎世联邦理工学院的 Lorenz Meier 在 2009 年提出。目前主流版本是 v1.0 和 v2.0。

它的核心特点就三个:

  • 轻量——消息体最小只有 8 个字节,适合低带宽链路
  • 可靠——每个消息都带 CRC 校验,丢包重传机制完善
  • 可扩展——支持自定义消息,厂商可以自己加私有协议

我个人习惯把 MAVLink 比作「航空界的 Modbus」。结构简单,但该有的都有。你如果之前做过串口通信,上手 MAVLink 会非常快。

关键点:MAVLink 不是无线协议,它是应用层协议。它可以在串口、UDP、TCP 甚至蓝牙上跑。很多新手以为 MAVLink 只能走数传电台,其实不是的。

1.2 消息结构

MAVLink 的消息结构,我建议你把它拆成三部分来看:帧头、负载、校验尾。

先看 v1.0 的格式,这是最基础的:

字节偏移  字段名        说明
0         STX          帧起始标志,固定 0xFE
1         LEN          负载长度(字节数)
2         SEQ          消息序列号(0~255 循环)
3         SYS          系统 ID(飞控、相机等设备编号)
4         COMP         组件 ID(同一系统内的模块编号)
5         MSG ID       消息 ID(标识消息类型)
6~N       PAYLOAD      消息负载(最多 255 字节)
N+1       CKA          校验字节 A
N+2       CKB          校验字节 B

你看,一个完整的 MAVLink 消息,帧头只有 6 个字节。我当年第一次看到这个结构时,第一反应是:这也太省了吧?

但省有省的好处。在 9600 波特率的数传链路上,一个 8 字节的负载消息,传输时间不到 10 毫秒。这对实时控制来说太重要了。

v2.0 版本在 v1.0 基础上做了扩展:

  • 帧起始标志改为 0xFD
  • 增加了 2 字节的「不签名长度」字段
  • 消息 ID 扩展到 3 字节(支持更多消息类型)
  • 增加了可选的安全签名机制

我建议你:新项目直接用 v2.0。v1.0 的消息 ID 只有 256 个,现在主流飞控的自定义消息早就超了。我在一个农业无人机项目里,光喷洒系统的私有消息就用了 30 多个 ID,v1.0 根本不够用。

我的经验:如果你在调试时发现地面站收不到某些消息,先检查协议版本是否匹配。飞控发 v2.0,地面站配成 v1.0,那肯定对不上。我曾经在 PX4 和 QGroundControl 之间遇到过这个问题,折腾了半小时才发现是版本没对齐。

1.3 CRC校验机制

CRC 校验是 MAVLink 的「守门员」。每个消息末尾都有 2 个字节的 CRC,用来检测传输过程中有没有数据被篡改或丢失。

MAVLink 用的 CRC 算法是 CRC-16-IBM(多项式 0x8005)。但有个特殊之处:它把消息 ID 也参与 CRC 计算。

为什么要这么做?

你想啊,如果只校验负载,那消息 ID 传错了怎么办?比如飞控发的是「高度数据」(MSG ID = 110),但地面站收到的消息 ID 变成了「电池数据」(MSG ID = 147)。负载内容一样,CRC 校验通过,但解析出来的数据完全错了。

把消息 ID 加入 CRC 计算后,这种错误就能被检测出来。这个设计细节,我是在一次排查「地面站显示高度乱跳」的问题时才注意到的。后来翻源码,发现 MAVLink 的 CRC 初始化函数里确实把 MSG ID 传进去了。

CRC 计算的伪代码如下:

// 初始化 CRC
uint16_t crc = 0xFFFF;

// 对每个字节进行 CRC 计算
for each byte in (LEN + SEQ + SYS + COMP + MSG ID + PAYLOAD):
    crc = crc_accumulate(byte, crc);

// 最后异或 0xFFFF
crc = crc ^ 0xFFFF;

实际开发中,你不需要自己写 CRC 算法。MAVLink 官方提供了代码生成工具,会自动生成每个消息的 CRC 校验函数。但理解原理很重要——当你的数据链路出现「偶发性的消息丢失」时,CRC 错误率是一个关键的排查指标。

注意:不要随意修改 CRC 算法。有些开发者为了「优化性能」把 CRC 校验跳过了,结果数据错乱都不知道。我见过一个极端案例:某团队在数传链路上关闭了 CRC 校验,结果无人机在强电磁干扰环境下,把「降落」指令误解析成了「返航」,差点炸机。

1.4 心跳包机制

心跳包是 MAVLink 里最重要的消息,没有之一。它的消息 ID 是 0(HEARTBEAT)。

心跳包的作用就一句话:告诉对方「我还活着」。

飞控会以固定频率(通常是 1Hz)向地面站发送心跳包。地面站收到心跳包,就知道飞控还在线。如果连续几个心跳包没收到,地面站就会触发「通信超时」告警。

心跳包的结构很简单:

字段名          字节数  说明
type            1      飞行器类型(多旋翼、固定翼、地面车辆等)
autopilot       1      飞控类型(PX4、ArduPilot 等)
base_mode       1      系统状态(是否已解锁、是否在自动模式等)
custom_mode     1      自定义模式(具体飞控的飞行模式)
system_status   1      系统状态(待机、激活、紧急等)

只有 5 个字节,但信息量很大。我举个例子:

  • type = 2 表示这是多旋翼
  • autopilot = 12 表示这是 PX4 飞控
  • base_mode = 129(0x81)表示系统已上电、已解锁、已进入控制模式
  • custom_mode = 4 表示当前是「悬停」模式
  • system_status = 3 表示系统状态正常

你看,一个 5 字节的消息,就把飞控的「身份」和「状态」全交代清楚了。

我在实际项目中,经常用心跳包来做「设备发现」。比如一个地面站要同时连接 3 架无人机,每架无人机的心跳包里 type 和 autopilot 字段不同,地面站就能区分它们。

避坑指南:我曾经遇到过一个情况——飞控明明在正常工作,但地面站一直报「通信超时」。排查后发现,是飞控的心跳包发送频率被改成了 0.2Hz(5 秒一次),而地面站的超时阈值是 3 秒。两边参数没对齐,导致误报。所以,心跳包频率和超时阈值一定要配套设置。

知识体系总览

下面这张图,把 MAVLink 协议的核心脉络串起来了。你可以把它当作本章的「思维导图」:

MAVLink 协议核心知识体系 MAVLink 协议 协议概述 消息结构 CRC 校验 心跳包机制 v1.0 / v2.0 轻量 / 可靠 / 可扩展 帧头 + 负载 + 校验尾 6 字节帧头 / 最多 255 字节负载 CRC-16-IBM (0x8005) 消息 ID 参与校验 MSG ID = 0 1Hz 固定频率 核心:理解消息结构是排查通信故障的基础 心跳包是判断链路是否正常的「第一指标」

本章小结

好了,咱们把 MAVLink 协议的基础过了一遍。总结几个关键点:

  • 协议版本:新项目用 v2.0,兼容性更好
  • 消息结构:帧头 6 字节 + 负载 + CRC 2 字节,简单高效
  • CRC 校验:消息 ID 参与计算,别跳过校验
  • 心跳包:MSG ID = 0,1Hz 发送,是链路健康的「晴雨表」

这些基础概念,后面每一章都会用到。尤其是心跳包和 CRC 校验,在故障排查时是最先检查的两个地方。你先把这些记牢了,后面遇到问题才能快速定位。