3、心跳与健康监测:心跳报文设计、超时判定策略、健康状态机

各位同学,咱们今天聊一个飞控系统里特别实在的话题——心跳与健康监测。

说白了,就是怎么让两个飞控板互相知道对方还活着,活得怎么样。我当年第一次做双余度飞控时,觉得这事儿特简单:不就是定时发个包嘛?结果真上了试飞台,才发现坑多得很。嗯,咱们今天就把这些坑一个一个填上。

3.1 心跳报文设计

心跳报文,你可以理解成两个飞控之间的“打招呼”。但打招呼也有讲究,不能太啰嗦,也不能太敷衍。

报文结构,我个人习惯用固定长度、紧凑型的设计。举个例子:

typedef struct {
    uint32_t timestamp;      // 时间戳,单位微秒
    uint8_t  source_id;      // 源节点ID:0x01为主控,0x02为备控
    uint8_t  status;         // 健康状态:0x00=健康,0x01=亚健康,0x02=故障
    uint8_t  seq_num;        // 序列号,用于检测丢包
    uint8_t  crc8;           // 校验字节
} __attribute__((packed)) HeartbeatPacket_t;

你看,总共才8个字节。为什么这么小?因为心跳报文要高频发送,我一般设成50Hz到100Hz。你想想看,如果报文太大,CPU时间都花在打包解包上了,不值当。

这里有个细节seq_num 字段很多人会忽略。我在项目中遇到过,单纯靠时间戳判断丢包其实不准,因为两个飞控的时钟可能有偏差。序列号就不一样了,连续递增,中间跳号了,那就是丢包了。

小技巧:CRC8校验用多项式0x07(标准CRC-8),计算量小,硬件支持也好。别用CRC32,太浪费。

3.2 超时判定策略

心跳报文发出去了,对方怎么知道“没收到”呢?这就涉及到超时判定。

最简单的做法:设定一个固定超时时间,比如50ms内没收到下一包,就认为超时。但这样做有个问题——万一网络抖动呢?

我建议用滑动窗口法。具体来说:

  • 维护一个长度为N的接收时间窗口(比如N=5)
  • 每次收到心跳,记录当前时间
  • 计算最近N个时间间隔的平均值
  • 超时阈值 = 平均间隔 × 3(或4)

为什么会这样?因为飞控在不同工况下,CPU负载不一样。高负载时,任务调度可能延迟几毫秒。固定超时要么太敏感导致误判,要么太迟钝导致切换滞后。滑动窗口能自适应调整,说白了就是更聪明。

我曾经在一个项目中,固定超时设了100ms,结果飞控在高机动时偶尔丢一包,备控立马认为主控挂了,直接切换。切换过程中飞机抖了一下,试飞员差点骂娘。后来改成滑动窗口,再也没出过这种问题。

注意:超时阈值不能设得太宽松。我见过有人设成10倍平均间隔,结果主控真挂了,备控等了500ms才反应过来。对于飞控来说,500ms足够飞机翻好几个跟头了。

3.3 健康状态机

好了,现在能检测到心跳超时了,但光有“活着”和“死了”两种状态够吗?不够。你想想看,一个飞控可能只是某个传感器数据异常,但还能勉强工作。这时候直接切到备控,不一定是最优解。

所以我设计了三级健康状态:健康 → 亚健康 → 故障

状态转移图如下:

健康 (正常) 亚健康 (降级) 故障 (失效) 连续2次超时 连续5次正常 连续3次超时 系统复位后 健康状态机转移图 虚线:复位恢复路径 | 实线:正常转移路径

你看这个状态机,核心思想是“迟判早切”。什么意思?

  • 进入亚健康要快:连续2次超时,就降级。别犹豫,宁可误判也不能漏判。
  • 恢复健康要慢:连续5次正常心跳,才能回到健康。防止频繁抖动。
  • 进入故障要稳:连续3次超时,才判定故障。给亚健康状态一个缓冲。

我在实际项目中,还加了一个“亚健康计数器”。每次进入亚健康状态,计数器加1。如果一小时内亚健康次数超过3次,直接强制切到备控,然后主控做一次完整自检。这个机制帮我抓出过好几次间歇性硬件故障。

核心要点:健康状态机不是简单的“活/死”二值判断。亚健康状态是缓冲带,也是决策依据。备控看到主控是亚健康,可以提前准备接管,但不用立即行动。

3.4 实际部署中的注意事项

嗯,最后聊几个实战中容易踩的坑。

  1. 心跳发送要独立于主任务。我见过有人把心跳发送放在控制循环里,结果控制循环卡住了,心跳也停了。备控以为主控挂了,直接切换。其实主控只是算力过载。正确做法是用一个独立定时器或RTOS任务来发心跳。
  2. 接收端要做去抖动。硬件中断可能连续触发两次,导致同一包心跳被处理两次。加个简单的去抖逻辑,比如10ms内只处理最新的一包。
  3. 注意时钟同步。两个飞控的晶振有误差,时间戳会慢慢漂移。我建议不要直接用时间戳做超时判断,而是用“收到心跳后重置看门狗”的方式。
我的经验:在调试阶段,把心跳报文打印出来看。我习惯在串口终端用彩色显示——绿色代表健康,黄色代表亚健康,红色代表故障。一眼就能看出系统状态变化趋势。

好了,关于心跳与健康监测,今天就聊这么多。记住一句话:心跳是飞控的脉搏,状态机是飞控的神经。这两样东西设计好了,冗余切换就成功了一半。


专注资料整理