3、飞控软件架构总览:前后台系统、RTOS的选择与对比、分层架构设计
聊到飞控软件架构,我脑子里第一个蹦出来的问题就是:你到底需不需要一个操作系统?
这个问题,我在刚入行那会儿也纠结过。那时候做一个小四轴,觉得裸机跑得挺欢,干嘛要上RTOS?后来项目越做越大,任务一多,裸机那套轮询机制就开始捉襟见肘了。嗯,今天咱们就把这块掰开揉碎了讲清楚。
3.1 前后台系统:简单粗暴,但别滥用
前后台系统,说白了就是一个大循环(后台)加中断(前台)。
// 典型的前后台飞控主循环
void main(void)
{
while(1)
{
// 后台:轮询执行
sensor_read(); // 读取传感器
attitude_estimate(); // 姿态解算
control_calc(); // 控制计算
pwm_output(); // 输出PWM
comm_handle(); // 处理通信
}
}
// 前台:中断服务
void TIM1_IRQHandler(void)
{
// 1ms定时中断,标记采样时刻
sensor_ready_flag = 1;
}
这种架构的优点很明显:代码简单、资源占用少、实时性可控。我在早期做一款玩具级飞控时,就用这种结构,跑得挺稳。
但问题也来了——任务之间互相阻塞。你想想看,如果通信处理卡住了,整个控制循环都得等着。这在飞控里是致命的。我曾经遇到过,串口接收一个长数据帧,导致姿态更新延迟了2ms,飞机直接抖成筛子。
避坑指南:前后台系统只适合任务数少于5个、且每个任务执行时间可控的场景。飞控里如果要做复杂的路径规划、视觉处理,趁早放弃裸机。
3.2 RTOS:FreeRTOS vs RT-Thread,我该怎么选?
说实话,现在做飞控,我个人的习惯是直接上RTOS。为什么?因为飞控天然就是多任务系统:传感器采集、姿态解算、控制输出、通信、日志、故障处理……这些任务优先级不同,执行周期不同,用RTOS管理起来太舒服了。
那FreeRTOS和RT-Thread怎么选?我列个表,你一看就明白。
| 对比项 | FreeRTOS | RT-Thread |
|---|---|---|
| 内核大小 | 极小(3-6KB) | 中等(6-12KB) |
| 任务数量 | 无限制 | 无限制 |
| IPC机制 | 队列、信号量、互斥量 | 邮箱、消息队列、信号量、事件集 |
| 组件生态 | 较少,需自己移植 | 丰富(Finsh、DFS、LWIP等) |
| 学习曲线 | 平缓 | 中等 |
| 典型应用 | 资源受限的MCU | 功能复杂的飞控系统 |
我的建议是:
- 如果你用的是STM32F103这类资源紧张的芯片,做简单的四轴,FreeRTOS够用了。
- 如果你做的是带视觉、带RTK、带复杂日志的无人机,RT-Thread更合适。它的组件生态能帮你省很多事。
个人经验:我在一个项目中用FreeRTOS做飞控,后来需要加一个FTP上传日志的功能,结果发现FreeRTOS没有现成的文件系统支持,硬是自己撸了一个。如果当时用RT-Thread,直接挂DFS就完事了。
3.3 分层架构设计:让飞控代码不再是一团乱麻
不管你用裸机还是RTOS,分层架构都是必须的。我见过太多新手把传感器读取、姿态解算、控制输出全写在一个文件里,那维护起来简直是噩梦。
我个人习惯把飞控软件分成四层:
- 硬件抽象层(HAL):封装MCU外设和传感器驱动。换芯片?只改这一层。
- 核心算法层:姿态解算、控制律、导航算法。这部分是飞控的灵魂,与硬件无关。
- 任务管理层:基于RTOS的任务调度、优先级管理、通信机制。
- 应用层:遥控器处理、地面站通信、日志记录、故障处理。
// 分层架构示例(伪代码)
// 硬件抽象层
int mpu6050_read_accel(float *ax, float *ay, float *az);
// 核心算法层
void attitude_ekf_update(float gx, float gy, float gz,
float ax, float ay, float az);
// 任务管理层(FreeRTOS)
void task_control(void *param)
{
while(1)
{
// 等待传感器数据就绪
xSemaphoreTake(sensor_sem, portMAX_DELAY);
// 调用算法层
attitude_ekf_update(gx, gy, gz, ax, ay, az);
control_calc();
// 输出到硬件层
pwm_set(motor1, motor2, motor3, motor4);
}
}
// 应用层
void task_comm(void *param)
{
while(1)
{
// 处理遥控器数据
rc_parse();
// 发送遥测数据
telemetry_send();
vTaskDelay(10);
}
}
你看,这样一分层,每个模块的职责就清晰了。换传感器?只改HAL层。改控制算法?只动核心算法层。加新功能?在应用层加个任务就行。
核心原则:层与层之间通过接口通信,不要跨层调用。比如应用层不能直接操作I2C寄存器,必须通过HAL层。
3.4 避坑指南:我踩过的那些坑
最后分享几个我实际项目中遇到的坑,希望能帮你少走弯路。
- 任务优先级别设反了:我曾经把姿态解算任务的优先级设得比传感器采集还低,结果传感器数据来了,姿态解算还在等,导致控制周期不稳定。记住:传感器采集 > 姿态解算 > 控制输出 > 通信 > 日志。
- 中断里别做复杂运算:有个同事在定时器中断里直接做姿态解算,结果中断时间过长,导致其他中断丢失。中断里只做标记或数据拷贝,复杂运算交给任务。
- 堆栈大小要留余量:FreeRTOS默认的任务堆栈大小往往不够。我习惯在调试阶段把堆栈设大两倍,稳定后再优化。
好了,这一章的内容就到这里。下一章咱们会深入讲讲任务调度与优先级设计,这可是飞控实时性的关键。到时候我会拿一个实际案例,带你一步步设计任务调度策略。