4. HEARTBEAT报文深度解析:心跳机制、系统状态、模式切换、自定义模式
HEARTBEAT,说白了就是飞控的「我活着」信号。在MAVLink协议里,它是所有通信的基础。没有心跳,地面站根本不知道飞机在哪、什么状态、能不能响应指令。
我刚开始做SITL仿真时,第一件事就是抓心跳报文。为什么?因为只要心跳正常,整个链路就通了。心跳断了,后面所有报文都是白搭。
4.1 心跳报文的结构
HEARTBEAT的报文ID是0,优先级最高。它的载荷只有9个字节,但信息量很大。我们直接看结构:
| 字段名 | 类型 | 字节数 | 说明 |
|---|---|---|---|
| type | uint8_t | 1 | 系统类型(MAV_TYPE) |
| autopilot | uint8_t | 1 | 飞控类型(MAV_AUTOPILOT) |
| base_mode | uint8_t | 1 | 基础模式位掩码(MAV_MODE_FLAG) |
| custom_mode | uint32_t | 4 | 自定义模式(飞控特有) |
| system_status | uint8_t | 1 | 系统状态(MAV_STATE) |
| mavlink_version | uint8_t | 1 | MAVLink协议版本(固定为3) |
嗯,这里要注意:custom_mode占了4个字节,是唯一一个超过1字节的字段。为什么?因为飞控的模式太多了,1个字节根本不够用。
4.2 心跳机制:1Hz的约定
MAVLink标准规定,心跳报文以1Hz的频率发送。也就是每秒一条。这个频率是经过权衡的——太快浪费带宽,太慢又无法及时感知系统状态变化。
我在项目中遇到过一个问题:某个定制飞控的心跳频率被改成了0.5Hz,结果地面站一直报「连接超时」。查了半天才发现是心跳间隔超标了。所以,不要轻易改心跳频率,除非你同时改了地面站的超时阈值。
mavlink console 或 mavproxy --heartbeat 来监控心跳。如果看到心跳中断,先检查串口波特率,再检查飞控是否卡死在某个循环里。
4.3 系统状态:MAV_STATE枚举
system_status字段告诉你飞控当前的整体健康状态。常用的值有:
- MAV_STATE_UNINIT (0):未初始化,刚上电
- MAV_STATE_BOOT (1):正在启动
- MAV_STATE_CALIBRATING (2):校准中
- MAV_STATE_STANDBY (3):待机,已就绪但未解锁
- MAV_STATE_ACTIVE (4):活跃,已解锁或正在执行任务
- MAV_STATE_CRITICAL (5):严重警告,比如电池电压过低
- MAV_STATE_EMERGENCY (6):紧急状态,比如遥控器失控
- MAV_STATE_POWEROFF (7):正在关机
- MAV_STATE_FLIGHT_TERMINATION (8):飞行终止,比如触发了急停
我个人习惯在SITL仿真中,用Python脚本轮询心跳的system_status字段,来判断飞控是否真的进入了预期状态。比如解锁后,状态应该从STANDBY变成ACTIVE。如果没变,说明解锁指令没生效。
4.4 模式切换:base_mode与custom_mode
模式切换是心跳报文最核心的功能之一。它通过两个字段配合实现:
base_mode是一个位掩码,定义了一些通用标志:
| 位 | 标志 | 含义 |
|---|---|---|
| 0 | MAV_MODE_FLAG_CUSTOM_MODE_ENABLED | 启用了自定义模式 |
| 1 | MAV_MODE_FLAG_TEST_ENABLED | 测试模式 |
| 2 | MAV_MODE_FLAG_AUTO_ENABLED | 自动模式 |
| 3 | MAV_MODE_FLAG_GUIDED_ENABLED | 引导模式 |
| 4 | MAV_MODE_FLAG_STABILIZE_ENABLED | 增稳模式 |
| 5 | MAV_MODE_FLAG_HIL_ENABLED | 硬件在环仿真 |
| 6 | MAV_MODE_FLAG_MANUAL_INPUT_ENABLED | 手动输入 |
| 7 | MAV_MODE_FLAG_SAFETY_ARMED | 已解锁 |
custom_mode则是飞控特有的模式编码。比如ArduPilot里,每个模式都有一个数字ID:
- STABILIZE = 0
- ACRO = 1
- ALT_HOLD = 2
- AUTO = 3
- GUIDED = 4
- LOITER = 5
- RTL = 6
- ...
你想想看,如果只靠base_mode,你只能知道「是否启用了自动模式」,但不知道具体是AUTO还是RTL。而custom_mode补全了这个信息。
base_mode和custom_mode。只看一个,信息是不完整的。
4.5 自定义模式:飞控的「方言」
MAVLink标准只定义了通用框架,具体模式由各飞控自己定义。这就是custom_mode存在的意义。
我曾经在PX4和ArduPilot之间做桥接,发现同一个custom_mode值,在两个飞控里代表完全不同的模式。比如值4,在ArduPilot里是GUIDED,在PX4里可能是OFFBOARD。所以,解析custom_mode时,一定要知道飞控类型。
在SITL仿真中,我们可以用Python直接读取心跳报文:
# 伪代码示例:解析心跳报文
def parse_heartbeat(msg):
base_mode = msg.base_mode
custom_mode = msg.custom_mode
system_status = msg.system_status
# 检查是否解锁
armed = bool(base_mode & 0x80) # 第7位
# 检查是否启用了自定义模式
custom_enabled = bool(base_mode & 0x01)
if custom_enabled:
# 根据飞控类型解析custom_mode
if msg.autopilot == MAV_AUTOPILOT_ARDUPILOTMEGA:
mode_name = ardumode_to_name(custom_mode)
elif msg.autopilot == MAV_AUTOPILOT_PX4:
mode_name = px4mode_to_name(custom_mode)
else:
mode_name = f"Unknown({custom_mode})"
return armed, mode_name, system_status
custom_mode时,直接用了ArduPilot的映射表去解析PX4的心跳,结果地面站显示的模式全是错的。后来我加了一个判断:先检查autopilot字段,再选择对应的解析表。这个坑,你早晚会遇到。
4.6 心跳与模式切换的流程
模式切换的完整流程是这样的:
- 地面站发送
MAV_CMD_DO_SET_MODE指令 - 飞控收到指令,尝试切换模式
- 飞控在下一条心跳中更新
base_mode和custom_mode - 地面站收到心跳,确认模式已切换
整个过程是异步的。地面站不能假设指令发出后模式立刻改变,必须等心跳确认。我见过有人写代码时,发完指令立刻读模式,结果读到的还是旧模式。正确的做法是:发指令,然后等心跳,直到模式字段变成目标值。
下面这张图展示了心跳报文在模式切换中的核心作用:
4.7 实战:在SITL中抓取并解析心跳
我们直接用Python在SITL仿真中抓心跳。假设你已经启动了SITL并连接了MAVLink:
from pymavlink import mavutil
# 连接SITL
master = mavutil.mavlink_connection('udp:127.0.0.1:14550')
# 等待第一个心跳
master.wait_heartbeat()
print("已收到心跳,系统启动成功")
# 持续监听心跳
while True:
msg = master.recv_match(type='HEARTBEAT', blocking=True)
if msg:
base_mode = msg.base_mode
custom_mode = msg.custom_mode
system_status = msg.system_status
armed = (base_mode & 0x80) != 0
mode = custom_mode
print(f"状态: {system_status}, 解锁: {armed}, 模式: {mode}")
这段代码会每秒打印一次心跳信息。你可以试着在SITL里切换模式,看看custom_mode的变化。
msg.get_msgbuf() 可以拿到原始数据。
4.8 常见问题与避坑
- 心跳中断:检查网络连接、串口波特率、飞控是否卡死。SITL中可以用
--heartbeat参数强制发送。 - 模式解析错误:确认
autopilot字段,选择正确的模式映射表。不要混用ArduPilot和PX4的映射。 - 解锁状态不更新:检查
base_mode的第7位。有些地面站只看custom_mode,忽略了base_mode的解锁标志。 - 版本不匹配:
mavlink_version字段固定为3。如果收到其他值,说明协议版本不对,需要升级库。
好了,心跳报文的核心内容就这些。它虽然简单,但却是整个MAVLink通信的基石。理解透了,后面所有报文解析都会顺手很多。