4、双模冗余(DMR)架构:DMR原理、主备切换机制、比较器设计

4.1 DMR 的基本原理

双模冗余,说白了就是「两套干活,一套输出」。

我习惯把 DMR 叫做「双胞胎架构」。两个完全一样的通道,同时接收同样的输入,执行同样的算法。但最终谁说了算?得看比较器的脸色。

DMR 的核心逻辑其实很简单:

  • 两个通道并行运行:通道 A 和通道 B,硬件一样,软件一样,输入一样。
  • 比较器实时比对:每个计算周期,比较器都会拿两个通道的输出做比对。
  • 一致则输出,不一致则切换:如果结果一致,说明没问题,正常输出。如果不一致,说明至少有一个通道出错了。

你可能会问:「两个通道都错了一样的概率有多大?」嗯,这个问题问得好。对于随机硬件故障,两个通道同时出同样错误的概率确实极低。但如果是软件 bug 呢?那就麻烦了——两个通道跑同样的代码,bug 会同时触发。所以 DMR 主要防的是硬件故障,不是软件故障。

关键认知:DMR 只能检测到故障,不能定位故障。它知道「出问题了」,但不知道「是 A 还是 B 出了问题」。这是 DMR 和 TMR(三模冗余)最大的区别。

4.2 主备切换机制

主备切换,是 DMR 架构里最考验设计功底的地方。我在项目中遇到过好几次切换失败导致系统重启的惨痛教训,嗯,这里面的坑不少。

典型的切换流程是这样的:

  1. 正常状态:通道 A 是主(Active),通道 B 是备(Standby)。比较器持续比对,结果一致。
  2. 故障检测:比较器发现 A 和 B 的输出不一致。
  3. 重试机制:不立即切换,先重试 1-2 次。为什么?因为有些故障是瞬态的,比如电磁干扰导致的位翻转,重试一次可能就恢复了。
  4. 确认故障:连续多次不一致,确认故障存在。
  5. 切换决策:将主通道切换到 B,A 降为备。
  6. 输出切换:输出选择器从 A 的输出切换到 B 的输出。

这里有个细节——切换时机。我曾经在某个项目中,切换逻辑写在了中断服务函数里,结果中断优先级不够高,切换指令被延迟了 200 微秒。对于飞控来说,200 微秒足够让飞机抖一下了。

我的建议:切换逻辑一定要放在最高优先级的中断里,或者用硬件逻辑直接完成。软件切换再快,也比不上硬件切换的确定性。

还有一个容易被忽略的问题——切换后的状态同步。当 B 从备升为主时,它的输出状态必须和 A 切换前的状态保持一致。否则,输出会有一个跳变,这在飞控里是致命的。

举个例子:假设 A 输出的是 PWM 占空比 50%,切换瞬间 B 的输出是 0%。那飞机舵机就会猛地抽动一下。所以,切换前必须做状态同步,把 A 的当前输出值「告诉」B。

4.3 比较器设计

比较器是 DMR 架构的「裁判」。裁判的水平,直接决定了整个系统的可靠性。

比较器设计要考虑三个核心问题:

  • 比什么?——比较的对象是什么
  • 怎么比?——比较的算法是什么
  • 比完怎么办?——比较结果的处理

4.3.1 比较对象

比较器可以比很多东西:

比较层级 比较内容 优缺点
输出级 最终输出值(如 PWM、控制量) 简单直接,但无法定位中间错误
中间级 关键中间变量(如姿态角、加速度) 可以更早发现故障,但比较点多
指令级 CPU 执行的指令流 最细粒度,但开销极大

我个人习惯的做法是:输出级 + 关键中间级。输出级保证最终结果正确,关键中间级(比如 IMU 数据、控制律输出)做早期故障检测。这样既不会太复杂,又能覆盖大部分故障场景。

4.3.2 比较算法

比较不是简单的「相等」或「不相等」。因为浮点数计算有精度问题,两个通道的计算结果可能会有微小差异。

我常用的比较算法是这样的:

// 比较两个通道的输出是否一致
// 返回 1 表示一致,0 表示不一致
int compare_outputs(float ch_a, float ch_b, float threshold) {
    float diff = fabs(ch_a - ch_b);
    float max_val = fmax(fabs(ch_a), fabs(ch_b));
    
    // 绝对误差比较
    if (diff < threshold) {
        return 1;
    }
    
    // 相对误差比较(防止大数值下的误判)
    if (max_val > 1.0f) {
        if (diff / max_val < 0.01f) {  // 1% 的相对误差
            return 1;
        }
    }
    
    return 0;
}

这里有个坑——阈值怎么选?阈值太小,容易误报;阈值太大,漏报风险高。我一般会根据实际系统的噪声水平来定。比如飞控的 IMU 数据,噪声大概在 0.01g 左右,那阈值就设在 0.05g,留点余量。

注意:比较器的阈值不能是固定值。不同工况下,信号的动态范围不一样。起飞阶段和巡航阶段,控制量的差异很大。我建议做动态阈值,根据当前信号的幅值自动调整。

4.3.3 比较结果处理

比较器发现不一致后,不能直接判定故障。我见过一些设计,比较器一报错就切换,结果系统频繁切换,反而更不稳定。

正确的做法是:

  • 计数机制:用一个计数器记录连续不一致的次数。超过阈值才判定为故障。
  • 去抖处理:类似按键去抖,短暂的不一致忽略掉。
  • 故障记录:记录故障发生的时间、通道、比较值,方便事后分析。

我曾经在一个项目中,比较器没有做去抖处理。结果每次电机启动时,电磁干扰导致比较器频繁误报,系统一直在切换。后来加了 3 次连续不一致才判故障的逻辑,问题就解决了。

4.4 DMR 架构的局限性

说了这么多 DMR 的好处,也得说说它的短板。

DMR 最大的问题是——无法容忍第二次故障。一旦一个通道故障,系统就变成了单通道运行。这时候如果另一个通道也出问题,系统就彻底挂了。

所以,DMR 适合用在什么场景?

  • 短时任务:比如火箭的主动段飞行,时间短,故障概率低。
  • 可降级系统:故障后可以降级到安全模式,比如无人机返航。
  • 成本敏感场景:TMR 太贵,DMR 是性价比之选。

但如果你做的是载人飞行器,我建议还是上 TMR。人命关天的事,别省那点钱。

总结一下:DMR 的核心是「检测故障,切换通道」。比较器是灵魂,切换机制是骨架。设计得好,DMR 可以做到 99.9% 的故障覆盖率。设计得不好,那就是个摆设。

DMR 双模冗余架构流程图 输入信号 通道 A(主) 传感器 + 控制律 + 输出 通道 B(备) 传感器 + 控制律 + 输出 比较器 比对 + 去抖 + 计数 一致? 输出通道 A 切换通道 B 最终输出

经验之谈:DMR 架构设计时,一定要把「切换瞬间的输出连续性」作为关键指标来测试。我习惯在示波器上抓切换时刻的输出波形,看有没有毛刺或跳变。没有跳变,才算合格的 DMR 设计。

专注资料整理