第3章:实时操作系统(RTOS)基础:为什么飞控需要RTOS、FreeRTOS与uC/OS的对比、任务调度与优先级管理

3.1 为什么飞控必须用RTOS?

很多刚入行的朋友问我:飞控能不能跑裸机?我的回答是:能,但你会很痛苦。

飞控系统本质上是一个多任务实时系统。你想想看,它要同时干好几件事:

  • 以1000Hz的频率读取IMU数据
  • 以250Hz的频率运行姿态解算
  • 以50Hz的频率处理遥控器信号
  • 以10Hz的频率更新GPS位置
  • 随时响应紧急降落指令

这些任务对时间的要求完全不同。有的要求微秒级响应,有的允许几毫秒延迟。裸机用大循环轮询,一旦某个任务卡住,整个系统就崩了。

我记得有一次帮客户排查问题,他们的飞控在特定姿态下会失控。查了三天,发现是裸机代码里一个浮点运算占用了太长时间,导致IMU数据读取被延迟。这就是典型的裸机痛点。

核心结论:RTOS的核心价值不是“多任务”,而是“确定性”。它保证高优先级任务在指定时间内得到响应,这是飞控安全的基础。

3.2 FreeRTOS vs μC/OS:怎么选?

这两个RTOS我都用过,各有千秋。直接上对比表:

对比项 FreeRTOS μC/OS-III
开源协议 MIT(商用友好) 商业授权(需付费)
内核大小 4-9KB(极精简) 6-12KB(功能更全)
任务数量 无限制(取决于RAM) 无限制
优先级数量 可配置(通常32级) 无限制(但建议64级以内)
调度方式 抢占式+时间片轮转 抢占式+时间片轮转
IPC机制 队列、信号量、互斥量 队列、信号量、互斥量、事件标志、消息邮箱
内存管理 5种heap方案 固定内存块管理
调试支持 Tracealyzer、SystemView μC/Probe、uC/View
社区生态 极其活跃 较成熟但相对封闭

我个人习惯是:开源项目用FreeRTOS,商业产品看情况。FreeRTOS的MIT协议太友好了,而且现在被AWS收购后,生态越来越好。μC/OS的代码质量确实高,文档也详细,但那个授权费用对小团队不太友好。

我的建议:如果你刚开始学RTOS,从FreeRTOS入手。资料多、坑少、社区活跃。等你把调度、同步、通信这些概念搞明白了,再看μC/OS会轻松很多。

3.3 任务调度:飞控的“交通指挥”

调度说白了就是:这么多任务,谁先跑?跑多久?

RTOS里最常用的调度策略有两种:

3.3.1 抢占式调度(Preemptive)

这是飞控的默认选择。高优先级任务可以打断低优先级任务。举个例子:

// 伪代码示例:抢占式调度
void HighPriorityTask(void *pvParameters) {
    while(1) {
        // 读取IMU数据,优先级最高
        ReadIMU();
        vTaskDelay(1);  // 1ms周期
    }
}

void LowPriorityTask(void *pvParameters) {
    while(1) {
        // 更新LED状态,优先级最低
        UpdateLED();
        vTaskDelay(100);  // 100ms周期
    }
}

当HighPriorityTask就绪时,不管LowPriorityTask跑没跑完,CPU立刻切换过去。这就是“抢占”的含义。

3.3.2 时间片轮转(Round-Robin)

同优先级的任务,大家轮流跑。每个任务分到一个时间片(通常1-10ms),时间到了就换下一个。

我在PX4飞控里看到过一种混合策略:高优先级用抢占式,低优先级用时间片轮转。这样既保证了关键任务的实时性,又让非关键任务公平共享CPU。

注意:千万不要把所有任务都设成同一个优先级!我曾经见过一个团队,为了省事把所有任务优先级都设成5,结果系统响应完全不可控。优先级是RTOS的灵魂,一定要认真设计。

3.4 优先级管理:飞控的“红绿灯系统”

优先级管理是RTOS里最容易出问题的地方。我总结了几条铁律:

3.4.1 优先级分配原则

  1. 时间关键性:响应时间要求越高的任务,优先级越高。比如IMU读取(1ms周期)优先级应该高于GPS处理(100ms周期)。
  2. 安全关键性:与安全直接相关的任务,优先级必须高。比如故障检测、紧急降落。
  3. 执行时间:执行时间短的任务,优先级可以高一些。因为高优先级任务应该“快进快出”,不影响低优先级任务。

3.4.2 优先级反转问题

这是RTOS里的经典坑。简单说就是:低优先级任务拿着高优先级任务需要的资源,导致高优先级任务被阻塞。

我曾经在无人机项目中遇到过:一个低优先级的日志写入任务占用了I2C总线,导致高优先级的IMU读取任务被阻塞了2ms。就这2ms,飞机开始剧烈抖动。

解决方案有两个:

  • 优先级继承:当低优先级任务持有高优先级任务需要的资源时,临时提升低优先级任务的优先级。μC/OS和FreeRTOS都支持。
  • 互斥量(Mutex):使用带优先级继承的互斥量,而不是普通信号量。
// FreeRTOS中使用互斥量避免优先级反转
SemaphoreHandle_t xMutex;

void HighPriorityTask(void *pvParameters) {
    while(1) {
        if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
            // 安全访问共享资源
            ReadIMU();
            xSemaphoreGive(xMutex);
        }
    }
}

3.4.3 实际飞控的优先级设计案例

以我参与过的一个四旋翼飞控项目为例:

优先级 任务名称 周期 说明
最高(31) IMU数据采集 1ms 时间最敏感,必须优先
高(25) 姿态控制 2ms 控制环路,延迟不能超过1ms
中(15) 位置控制 10ms 相对宽松
低(5) 遥测数据发送 50ms 允许偶尔延迟
最低(1) 日志记录 100ms 不关键,可被抢占

经验之谈:优先级不要用满。比如FreeRTOS支持32级,我一般只用0-30,留一级给系统空闲任务。另外,相邻优先级之间留一些间隔,方便以后调整。

3.5 本章小结

RTOS是飞控系统的基石。它解决了裸机无法保证的“确定性”问题。FreeRTOS和μC/OS各有优劣,我个人更推荐从FreeRTOS入手。任务调度和优先级管理是RTOS的核心,设计得好,飞控稳定可靠;设计得不好,各种诡异问题层出不穷。

下一章我们会深入任务间通信和同步机制,这是多任务系统里另一个容易踩坑的地方。到时候我会分享一个因为信号量使用不当导致飞机坠毁的真实案例,敬请期待。