二、软件架构设计原则:模块化、分层化、高内聚低耦合、实时性保障、可靠性设计

好,咱们接着聊。上一章我把制导系统的整体架构铺开了,这一章咱们深入聊聊设计原则。

说实话,这些原则在教科书上都有,但真正在导弹上落地,味道完全不一样。我这些年踩过的坑,十有八九都是违背了这些原则。你想想看,导弹飞在天上,代码崩了可没法OTA升级。所以这些原则不是摆设,是保命用的。

2.1 模块化:把大象装进冰箱分几步?

模块化,说白了就是「分而治之」。把一个大系统拆成若干小模块,每个模块干好自己的事。

我个人习惯,拆模块时遵循两个标准:

  • 功能内聚:一个模块只做一件事。比如导航模块只管算位置,别掺和控制律的事。
  • 接口清晰:模块之间通过定义好的接口通信,别搞全局变量满天飞。

我在项目中遇到过最典型的反面教材——有人把IMU数据读取、姿态解算、控制输出全写在一个函数里,两千多行。后来换IMU型号,改了一个参数,结果控制律也跟着崩了。嗯,这就是模块化没做好的代价。

模块化设计要点:

  • 每个模块有唯一的职责
  • 模块内部实现对外部不可见
  • 模块间通过函数调用或消息队列通信

2.2 分层化:别让底层代码操心上层业务

分层架构,我理解就是「各司其职」。每一层只关心自己这一亩三分地,不越级汇报。

在制导系统里,我通常分四层:

层次 职责 典型模块
应用层 制导律、控制律、任务管理 比例导引、PID控制器
中间层 数据融合、状态估计、坐标变换 卡尔曼滤波、四元数更新
驱动层 传感器读取、执行器控制 IMU驱动、舵机PWM输出
硬件抽象层 寄存器操作、中断管理 SPI/I2C接口、定时器配置

为什么要这么分?我举个例子。有一次我们换陀螺仪芯片,从模拟输出换成数字SPI接口。因为驱动层和上层是分开的,我只需要重写驱动层的SPI读取函数,上层卡尔曼滤波的代码一行没动。你想想看,如果当初不分层,那得改多少地方?

我的经验:分层时,层与层之间用结构体传递数据,别用全局变量。这样每层都可以独立测试。

2.3 高内聚低耦合:模块内部拧成一股绳,模块之间松耦合

这两个词经常一起出现,但含义不同。

  • 高内聚:模块内部元素紧密相关。比如一个「导航解算模块」,里面所有函数都围绕位置、速度、姿态计算展开。
  • 低耦合:模块之间依赖少。改一个模块,不影响其他模块。

我曾经接手过一个项目,导航模块里居然调用了控制模块的舵机限幅函数。这就是典型的耦合过紧。后来调试时,导航模块一改,舵机乱转,查了两天才发现是耦合问题。

怎么做到低耦合?我建议:

  1. 模块间只通过接口函数交互,别直接访问内部变量
  2. 用回调函数或消息队列解耦时间依赖
  3. 避免使用全局变量,改用参数传递

避坑指南:我曾经见过一个项目,为了「方便」,把IMU数据放在全局结构体里,结果三个模块同时读写,数据竞争导致姿态解算偶尔跳变。嗯,从那以后我再也不敢滥用全局变量了。

2.4 实时性保障:导弹不等人

制导系统是硬实时系统。什么叫硬实时?就是任务必须在规定时间内完成,超时就是失败。导弹飞过目标了,你控制指令才到,那还有什么意义?

实时性保障,我主要关注三点:

  • 任务优先级:控制律任务优先级最高,日志任务优先级最低。别让日志打印耽误了舵机控制。
  • 中断响应:关键传感器用中断触发,别用轮询。轮询浪费CPU,还容易丢数据。
  • 时间确定性:每个任务的执行时间要可预测。避免使用动态内存分配、递归调用这些不确定行为。

我记得有一次调试,发现控制周期偶尔从1ms抖到3ms。查了半天,原来是某个模块里用了malloc分配临时缓冲区。动态分配的时间是不确定的,在实时系统里就是定时炸弹。

实时性设计检查清单:

  • 所有任务是否有明确的截止时间?
  • 高优先级任务是否会被低优先级任务阻塞?
  • 中断服务程序是否足够短?
  • 是否使用了锁或信号量?小心优先级反转。

2.5 可靠性设计:代码要经得起折腾

导弹上的代码,可靠性怎么强调都不过分。我总结了几条铁律:

  1. 冗余设计:关键传感器用双余度或三余度。比如IMU,两个陀螺仪同时工作,数据交叉校验。
  2. 看门狗:硬件看门狗必须开,而且喂狗时机要合理。别在主循环里喂,要在关键任务完成后喂。
  3. 数据校验:通信数据加CRC校验,传感器数据做合理性检查。我曾经遇到过IMU输出全0的情况,幸亏有校验逻辑,及时切到了备份传感器。
  4. 异常处理:每个模块都要有异常处理路径。比如导航解算发散时,要能自动切换到备用算法。

一个小技巧:我习惯在关键函数入口和出口加断言,检查参数合法性。虽然调试阶段会多花点时间,但能避免很多隐蔽的bug。

最后说一句,这些原则不是孤立的。模块化和分层化是结构基础,高内聚低耦合是质量要求,实时性和可靠性是性能底线。它们互相支撑,缺一不可。

下一章,咱们聊聊具体的软件架构模式——看看到底用状态机还是用任务调度,哪种更适合制导系统。