MAVLink基础:消息结构、心跳包与常用消息集

各位好,欢迎来到第二章。这一章我们聊聊MAVLink——说白了,它就是飞控和地面站之间的“通用语言”。没有它,你调参再牛,数据也传不上来。

我记得刚入行那会儿,第一次用Mission Planner连飞控,看到一堆十六进制数在串口里跳,完全不知道它们在说什么。后来才明白,MAVLink其实没那么玄乎,它就是一套结构化的消息协议。

2.1 消息结构:一帧数据长什么样?

MAVLink的消息帧,就像一封信。信封上有收件人地址、发件人签名,里面才是真正的数据。我习惯把它的结构拆成三部分来看:

  • 帧头(Header):固定以 0xFE(MAVLink 1.0)或 0xFD(MAVLink 2.0)开头。后面跟着消息长度、序列号、系统ID、组件ID。
  • 有效载荷(Payload):真正的数据区。比如姿态数据、GPS坐标,都塞在这里。
  • 校验(Checksum):CRC16校验,确保数据在传输过程中没被篡改。

你想想看,如果帧头错了,地面站根本不知道这包数据该给谁。如果校验错了,数据再漂亮也是废的。

核心要点:MAVLink 1.0 帧最小8字节,最大263字节。有效载荷最长255字节。实际项目中,大部分消息都在30字节以内。

下面是我常用的一张消息结构图,帮你把帧格式理清楚:

MAVLink 1.0 消息帧结构 0xFE 起始标志 Len 载荷长度 Seq 序列号 SysID 系统ID CompID 组件ID Payload(0~255字节) 有效数据 CRC16 校验和 💡 说明:MAVLink 2.0 在帧头后增加了 8 字节的签名区,用于加密和防篡改。 但大部分飞控(如Pixhawk)默认仍以 MAVLink 1.0 通信,兼容性更好。

2.2 心跳包:飞控的“我还活着”信号

心跳包(HEARTBEAT,消息ID #0)是MAVLink里最重要的消息,没有之一。飞控每隔1秒发一次,告诉地面站:我还活着,我的状态是啥。

我曾经在调试一架六旋翼时,地面站突然收不到心跳了。当时第一反应不是查飞控,而是查串口线——果然,USB转串口模块松了。嗯,心跳包就是你的“生命线”。

实战技巧:地面站如果连续3秒没收到心跳,就应该触发“连接丢失”报警。我一般把超时阈值设在5秒,避免偶尔丢包导致误报。

心跳包的核心字段如下:

字段名 类型 说明
type uint8_t 飞控类型(如MAV_TYPE_QUADROTOR = 2)
autopilot uint8_t 自驾仪类型(如MAV_AUTOPILOT_ARDUPILOTMEGA = 3)
base_mode uint8_t 位掩码,表示是否已解锁、是否在自动模式等
custom_mode uint32_t 自定义模式(如Loiter、RTL、Stabilize)
system_status uint8_t 系统状态(如MAV_STATE_STANDBY = 3)

举个例子,如果你看到 base_mode 的 bit 0 为 1,说明飞控已解锁。bit 2 为 1,说明在自动模式。这些位掩码,我建议你背下来,调试时非常有用。

2.3 常用消息集:姿态、IMU、GPS

调参时,你盯着最多的就是这三个消息。我一个个说。

2.3.1 ATTITUDE(消息ID #30)

这个消息告诉你飞机当前的三轴姿态:横滚(roll)、俯仰(pitch)、偏航(yaw),单位是弧度。还附带角速度。

我记得有一次,飞机在空中突然横滚角跳到 90 度,但IMU数据正常。后来发现是姿态解算的协方差矩阵没更新——说白了,就是滤波器“卡住”了。ATTITUDE 消息里的角速度数据,就是用来诊断这类问题的。

ATTITUDE 消息结构:

uint32_t time_boot_ms   // 系统启动时间(毫秒)
float    roll            // 横滚角(弧度)
float    pitch           // 俯仰角(弧度)
float    yaw             // 偏航角(弧度)
float    rollspeed       // 横滚角速度(弧度/秒)
float    pitchspeed      // 俯仰角速度(弧度/秒)
float    yawspeed        // 偏航角速度(弧度/秒)

2.3.2 IMU(消息ID #27,RAW_IMU)

IMU 消息直接输出加速度计、陀螺仪、磁力计的原始值。注意,是原始值,不是经过滤波的。

我一般用这个消息来检查传感器是否“饱和”。比如加速度计如果超过 ±16g,说明飞机可能已经失控了。陀螺仪如果超过 ±2000°/s,嗯,那飞机大概率在自旋。

字段 单位 典型范围 说明
xacc / yacc / zacc mG ±16000 加速度计原始值(1G = 1000 mG)
xgyro / ygyro / zgyro mrad/s ±2000000 陀螺仪原始值(1°/s ≈ 17453 mrad/s)
xmag / ymag / zmag mGauss ±8000 磁力计原始值

避坑指南:我曾经在调试时发现IMU数据跳变,以为是传感器坏了。后来发现是USB线缆的电磁干扰。所以,用MAVLink看IMU数据时,尽量用无线数传,别用USB线。

2.3.3 GPS_RAW_INT(消息ID #24)

这个消息输出GPS的原始数据,包括经纬度、海拔、地面速度、航向角等。注意,经纬度是 int32_t 类型,单位是 1e-7 度。也就是说,你需要除以 10,000,000 才能得到常规的度数。

举个例子,如果 lat 是 373800000,那实际纬度就是 37.38°。我刚开始做飞控时,忘了这个换算,结果飞机定位偏了半个中国——嗯,那画面太美不敢看。

实用换算:

  • 纬度 = lat / 1e7
  • 经度 = lon / 1e7
  • 海拔 = alt / 1000(单位:米)
  • 地面速度 = vel / 100(单位:m/s)

2.4 消息流与实时监控

在实际调参时,你不会只看一条消息。地面站会同时接收心跳、姿态、IMU、GPS等多条消息。我习惯把消息流画成下面这张图:

MAVLink 实时消息流示意 飞控 地面站 HEARTBEAT (1Hz) ATTITUDE (10~50Hz) RAW_IMU (50~100Hz) GPS_RAW_INT (5~10Hz) 💡 频率越高,数据越实时,但带宽占用也越大。我一般把ATTITUDE降到20Hz, IMU降到50Hz,既能看清动态,又不会把串口带宽撑爆。

你想想看,如果地面站同时收到这些消息,你就能实时看到飞机的姿态变化、传感器原始值、GPS定位精度。调参时,我一般把ATTITUDE和RAW_IMU放在同一个图表里,对比看——如果姿态变化了但IMU没变,那肯定是姿态解算出了问题。