第二章:系统架构基础——分层架构、模块化设计、实时操作系统(RTOS)选型

好,咱们正式开始聊系统架构。

说实话,很多刚入行的朋友一上来就盯着算法看——卡尔曼滤波怎么调、PID参数怎么整。这些当然重要,但我个人觉得,真正决定一个飞控系统能不能稳定飞起来的,其实是底层的架构设计。你想想看,算法再牛,跑在一个不靠谱的调度系统上,该实时的时候被别的任务堵住了,那飞机不摔才怪。

2.1 分层架构:把复杂问题拆成几层

飞控软件有多复杂?我举个例子。你既要读传感器数据,又要跑控制算法,还要处理遥控器信号,同时得把日志写到SD卡里。如果所有代码都揉在一起,那维护起来就是一场噩梦。

分层架构的核心思想,说白了就是「各司其职」。每一层只关心自己的事,层与层之间通过定义好的接口通信。

在飞控系统里,我习惯把软件分成这么几层:

  • 硬件抽象层(HAL):负责和具体的芯片、传感器打交道。比如读IMU的SPI数据、配置PWM输出。这一层把硬件细节藏起来,上层不用管你用的是STM32还是GD32。
  • 中间件层:提供一些通用服务,比如消息队列、参数管理、日志系统。这一层是「管道工」,负责把数据从A点搬到B点。
  • 功能模块层:姿态解算、位置估计、控制律计算。这些是飞控的核心算法模块。
  • 应用层:飞行模式管理、任务规划、用户指令解析。这一层离用户最近。

关键原则:每一层只能调用它下面那一层的接口。绝对不能出现应用层直接去操作SPI寄存器的情况。我在项目中见过有人这么干,结果换了个传感器型号,整个应用层代码都得重写——那叫一个酸爽。

嗯,这里要注意一点:分层不是越细越好。层数太多,数据在层间传来传去,延迟就上去了。飞控是个实时系统,延迟就是安全隐患。我个人建议,3到4层就够用了。

应用层(飞行模式、任务管理) 功能模块层(姿态解算、控制律) 中间件层(消息队列、参数管理) 硬件抽象层(HAL:SPI、I2C、PWM) 硬件平台(MCU、传感器、执行器)

2.2 模块化设计:高内聚、低耦合

分层架构解决的是纵向的层次关系,那横向的模块之间怎么组织?这就轮到模块化设计出场了。

模块化设计的核心就四个字:高内聚、低耦合

  • 高内聚:一个模块只做一件事,并且把它做好。比如姿态解算模块,它只负责从陀螺仪和加速度计数据算出姿态角,不关心这些数据是从哪个传感器来的,也不关心算出来之后要发给谁。
  • 低耦合:模块之间的依赖关系尽量少。一个模块改了,其他模块不需要跟着改。

我举个例子你就明白了。假设你有一个GPS模块和一个光流模块,它们都输出位置信息。如果你在代码里写死「位置数据来自GPS模块」,那哪天你想换成光流,就得改一堆代码。正确的做法是定义一个统一的「位置源」接口,GPS和光流都实现这个接口。上层模块只管调用接口,不管底层是谁在干活。

我的一个小技巧:在飞控项目里,我习惯给每个模块定义一个结构体,里面放函数指针。这样模块之间的调用就变成了通过函数指针间接调用,耦合度一下子就降下来了。代码看起来也更清爽。

2.3 实时操作系统(RTOS)选型:别选错了,会出人命的

飞控系统必须用RTOS,这一点没什么好争论的。裸机跑循环?那只能做玩具。真正的飞控,任务调度、优先级管理、时间确定性,这些都得靠RTOS来保证。

市面上的RTOS很多,我挑几个常见的说说我的看法。

RTOS 许可证 内核大小 实时性 生态 我的评价
FreeRTOS MIT 极小(4-9KB) 优秀 极好 首选,社区资源多,坑少
RT-Thread Apache 2.0 中等 良好 好(国产,中文文档全) 适合国内团队,组件丰富
uC/OS-III 商业(部分免费) 中等 优秀 一般 稳定但许可证麻烦
Zephyr Apache 2.0 较大 良好 好(Linux基金会) 适合资源丰富的芯片

我个人最推荐的是 FreeRTOS。为什么?

  • 第一,它足够轻量。飞控的MCU通常资源有限,FreeRTOS的内核只有几KB,省下来的Flash和RAM可以留给算法用。
  • 第二,它的调度策略是抢占式优先级调度,正好满足飞控的需求——控制任务必须最高优先级,日志任务可以慢慢来。
  • 第三,生态好。你遇到的大部分问题,网上都有人踩过坑了。

避坑指南:我曾经在一个项目里选了uC/OS-III,结果做到一半发现商业许可证要花钱,而且技术支持响应慢。最后不得不花两周时间把整个系统移植到FreeRTOS上。那两周,我每天都在跟调度器死磕。所以,选型的时候一定要把许可证问题搞清楚,别等代码写完了才发现踩雷。

选型的时候,还有几个点要特别注意:

  • 任务栈大小:别给太小,否则会栈溢出。我一般给每个任务至少512字节,关键任务给1KB以上。调试的时候打开栈溢出检测钩子函数,能帮你省很多排查时间。
  • 优先级反转:这是RTOS里的经典问题。低优先级任务拿着锁,高优先级任务等着用,结果中优先级任务插进来把低优先级任务抢占了。解决办法是使用优先级继承协议,FreeRTOS里叫互斥量(Mutex),别用二值信号量。
  • 时间确定性:飞控里有些任务必须严格按时执行,比如PWM输出。这时候要用硬件定时器中断,而不是依赖RTOS的软件定时器。软件定时器的精度受任务调度影响,关键时刻会掉链子。

嗯,说到时间确定性,我再啰嗦一句。我见过有人把PWM生成放在RTOS任务里,结果任务被更高优先级的任务抢占了,PWM波形就乱了。正确的做法是:PWM生成用MCU的定时器硬件直接做,RTOS任务只负责更新占空比参数。这样即使任务被抢占了,PWM波形也不会断。

好了,关于系统架构的基础,咱们就聊到这儿。记住一句话:架构设计不是为了好看,是为了让系统在极端情况下也能稳定工作。飞控系统,稳定压倒一切。


专注资料整理