4. 软件架构设计:三余度软件分层模型、同步与异步机制、数据一致性保障

各位同学,今天我们来聊聊三余度飞控的软件架构。说实话,这部分内容是我个人觉得整个课程里最考验系统思维的地方。硬件搞了三套,软件要是没设计好,那三余度反而可能变成三倍麻烦。

我见过不少团队,硬件冗余做得挺扎实,一上电三套飞控各自为政,数据乱飞,表决结果比单机还差。嗯,问题就出在软件架构上。今天我把这些年踩过的坑和总结的经验,掰开了讲给你们听。

4.1 三余度软件分层模型

先说说分层。我习惯把三余度软件分成三层:应用层同步管理层硬件抽象层。为什么这么分?说白了,就是为了解耦。

核心思想:每一层只关心自己的事,不越界。应用层只管飞控算法,同步管理层管三机协同,硬件抽象层管传感器和舵机。这样哪一层出了问题,不会牵连全局。

我在项目中遇到过一件事:某次飞行测试,三套飞控的IMU数据总是对不上。查了两天,发现是硬件抽象层里,不同通道的SPI时钟相位设置差了那么一点点。如果当时没有分层,这种问题根本没法定位。

具体分层如下:

  • 硬件抽象层(HAL):封装传感器、舵机、通信接口。每个通道独立运行,互不干扰。
  • 同步管理层(SML):负责三机之间的时钟同步、数据交换、故障检测。这是三余度的灵魂。
  • 应用层(APP):姿态解算、控制律计算、任务管理。跑的是单机飞控算法,但输入输出都经过同步管理层。

你想想看,如果应用层直接去读传感器,那三套飞控读到的数据天然就有差异,后面再怎么表决也白搭。所以,应用层只能读同步管理层处理过的数据,这是铁律。

我的建议:分层时,接口定义要严格。我一般用结构体+版本号的方式,确保各层之间的数据格式一致。别小看这个,版本号能救你命。

4.2 同步与异步机制

三余度系统里,同步是个大问题。三套飞控怎么对齐时间?怎么保证同一时刻执行同一段代码?

我常用的方案是混合同步机制

  • 帧同步:每个控制周期开始,三机通过CCDL交换同步帧头。谁先收到两帧,谁就发起同步。说白了,就是三机互相喊「1、2、3,开始!」
  • 异步补偿:如果某通道因为中断延迟或任务抢占,没赶上同步窗口,那就进入异步模式。异步模式下,该通道用上一周期的数据继续运行,同时尝试重新同步。

为什么会这样设计?我记得有一次做半实物仿真,发现三机同步后,总有一台慢几十微秒。查到最后,是某款MCU的DMA传输占用了总线。从那以后,我就在同步机制里加了超时回退逻辑。

注意:异步模式不能一直跑。我曾经见过一个系统,某通道长期处于异步状态,结果表决时它的数据总是滞后,导致控制输出抖动。所以,异步模式必须有个计数器,连续异步超过N个周期,就要触发故障切换。

代码层面,我习惯这样实现同步逻辑:

// 伪代码:三余度同步管理器
void sync_manager_run() {
    // 等待CCDL同步帧
    if (ccdl_wait_sync_frame(SYNC_TIMEOUT_US)) {
        // 同步成功,进入帧同步模式
        current_mode = SYNC_MODE_FRAME;
        // 执行应用层任务
        app_task_run();
    } else {
        // 同步超时,进入异步模式
        current_mode = SYNC_MODE_ASYNC;
        async_counter++;
        // 使用上一周期数据
        app_task_run_with_old_data();
    }

    // 如果连续异步超过5个周期,上报故障
    if (async_counter > 5) {
        fault_report(CHANNEL_SYNC_LOST);
    }
}

这段代码看着简单,但实际工程里要考虑的东西很多。比如同步超时时间设多少?我一般设成控制周期的10%,太短容易误报,太长影响实时性。

4.3 数据一致性保障(CCDL)

CCDL,全称Cross-Channel Data Link,是三余度系统的数据高速公路。它的核心任务就一个:让三套飞控看到同一份数据

我刚开始做三余度时,觉得CCDL不就是个通信协议嘛,随便写写就行。结果第一次联调,三机数据对不上,有的通道读到的是旧数据,有的读到的是新数据,表决结果一塌糊涂。

后来我总结了一套CCDL设计原则:

  1. 数据版本化:每条数据都带时间戳和序列号。接收方根据序列号判断数据新旧,丢弃旧数据。
  2. 三机互传:每个通道把自己的数据广播给另外两个通道,同时接收另外两个通道的数据。形成全互联拓扑。
  3. 多数表决:收到三份数据后,取中间值或平均值。如果某通道数据与其他两通道偏差过大,标记为可疑。

关键点:CCDL的数据传输必须是确定性的。不能出现「这次传了,下次没传」的情况。我一般用固定时间片轮转,每个通道在固定的时隙发送数据。

下面这张图展示了CCDL的数据流和表决逻辑:

通道A 通道B 通道C 多数表决器 表决输出 图例: 通道A数据 通道B数据 通道C数据

这张图里,每个通道都把自己的数据发给另外两个通道。三份数据汇集到多数表决器,最终输出一致的结果。注意看,数据链路是双向的,没有主从之分。

实际工程中,CCDL的物理层我常用CAN总线或高速串口。CAN总线天生支持多主通信,很适合三余度场景。但要注意,CAN的带宽有限,一个控制周期内要传完所有数据,得算好时间。

避坑指南:我曾经在CCDL里用过软件校验和,结果发现偶尔会漏检。后来全部改成CRC32,再没出过问题。数据一致性这事,硬件校验比软件靠谱。

最后说一句,数据一致性不是靠一次交换就能保证的。我习惯在每个控制周期结束时,再做一次交叉校验——三机互相确认对方的数据和自己的一致。如果不一致,下个周期重新交换。这样虽然多花点时间,但能确保万无一失。

好了,软件架构这部分就讲到这里。记住,分层是骨架,同步是节拍,CCDL是血管。三者缺一不可。


专注资料整理