第二章:系统架构基础——分层架构、模块化设计、实时操作系统(RTOS)选型
好,咱们正式开始聊系统架构。
说实话,很多刚入行的朋友一上来就盯着算法看——卡尔曼滤波怎么调、PID参数怎么整。这些当然重要,但我个人觉得,真正决定一个飞控系统能不能稳定飞起来的,其实是底层的架构设计。你想想看,算法再牛,跑在一个不靠谱的调度系统上,该实时的时候被别的任务堵住了,那飞机不摔才怪。
2.1 分层架构:把复杂问题拆成几层
飞控软件有多复杂?我举个例子。你既要读传感器数据,又要跑控制算法,还要处理遥控器信号,同时得把日志写到SD卡里。如果所有代码都揉在一起,那维护起来就是一场噩梦。
分层架构的核心思想,说白了就是「各司其职」。每一层只关心自己的事,层与层之间通过定义好的接口通信。
在飞控系统里,我习惯把软件分成这么几层:
- 硬件抽象层(HAL):负责和具体的芯片、传感器打交道。比如读IMU的SPI数据、配置PWM输出。这一层把硬件细节藏起来,上层不用管你用的是STM32还是GD32。
- 中间件层:提供一些通用服务,比如消息队列、参数管理、日志系统。这一层是「管道工」,负责把数据从A点搬到B点。
- 功能模块层:姿态解算、位置估计、控制律计算。这些是飞控的核心算法模块。
- 应用层:飞行模式管理、任务规划、用户指令解析。这一层离用户最近。
关键原则:每一层只能调用它下面那一层的接口。绝对不能出现应用层直接去操作SPI寄存器的情况。我在项目中见过有人这么干,结果换了个传感器型号,整个应用层代码都得重写——那叫一个酸爽。
嗯,这里要注意一点:分层不是越细越好。层数太多,数据在层间传来传去,延迟就上去了。飞控是个实时系统,延迟就是安全隐患。我个人建议,3到4层就够用了。
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波形也不会断。
好了,关于系统架构的基础,咱们就聊到这儿。记住一句话:架构设计不是为了好看,是为了让系统在极端情况下也能稳定工作。飞控系统,稳定压倒一切。