1. RTOS基础概念:实时操作系统定义、任务调度、内核对象、无人机为何需要RTOS
1.1 到底什么是实时操作系统?
先说说我自己的理解。实时操作系统,英文叫RTOS,说白了就是「在规定时间内必须干完活」的操作系统。
你想想看,我们平时用的Windows、Linux,它们是什么系统?是分时系统。什么叫分时?就是大家轮流用CPU,你等一会儿,我等一会儿。你点个鼠标,系统可能过几十毫秒才响应,你感觉不到,因为人反应没那么快。
但无人机不一样。飞控每1毫秒就要读一次陀螺仪数据,每2毫秒就要算一次姿态,每5毫秒就要输出一次PWM给电机。这些时间节点,一个都不能晚。晚1毫秒,飞机可能就翻了。
所以,实时操作系统的核心就一句话:任务的执行时间是可预测的,最坏情况下的响应时间是确定的。
实时 ≠ 快速
很多新手会搞混。实时不是说你跑得有多快,而是说你能不能保证在截止时间前完成。哪怕你跑得慢一点,但只要每次都能在截止时间前完成,那就是实时系统。
1.2 任务调度——RTOS的「大脑」
任务调度是RTOS最核心的机制。我刚开始学RTOS时,觉得调度不就是「谁先来谁先走」吗?后来发现完全不是这么回事。
RTOS里常用的调度策略有这么几种:
| 调度算法 | 特点 | 无人机场景 |
|---|---|---|
| 优先级抢占式调度 | 高优先级任务随时打断低优先级任务 | 最常用,比如姿态控制优先级最高 |
| 时间片轮转调度 | 同等优先级任务轮流执行固定时间 | 用于非关键任务,如日志记录 |
| 单调速率调度(RMS) | 周期越短的任务优先级越高 | 适合传感器采集这类周期性任务 |
我个人习惯用优先级抢占式调度。为什么?因为无人机里有些任务就是「天大的事」,比如电机控制。一旦电机控制任务就绪,其他任务都得让路。
避坑指南
我曾经在一个项目中,把所有任务都设成了同一个优先级。结果呢?高频率的传感器中断把低优先级的通信任务饿死了,GPS数据一直发不出去,飞机在空中乱飘。后来我学乖了:关键任务给高优先级,非关键任务给低优先级,中间留一两个优先级做缓冲。
1.3 内核对象——RTOS的「工具箱」
RTOS内核提供了几个常用的对象,用来协调任务之间的工作。我挑几个最重要的说说。
1.3.1 任务(Task)
任务就是一段独立的代码,有自己的栈空间和优先级。在FreeRTOS里创建一个任务很简单:
// 创建一个姿态控制任务
xTaskCreate(
attitude_control_task, // 任务函数
"AttitudeCtrl", // 任务名称
512, // 栈大小(单位:字)
NULL, // 参数
5, // 优先级(5为高优先级)
NULL // 任务句柄
);
嗯,这里要注意:栈大小一定要算好。我见过有人给任务分配了128字节的栈,结果函数调用一深,栈溢出了,飞控直接死机。
1.3.2 信号量(Semaphore)
信号量用来做同步。比如,传感器数据采集完了,通知姿态控制任务开始算。这就是典型的「生产者-消费者」模式。
// 传感器中断中释放信号量
void IMU_IRQHandler(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(xIMUSemaphore, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
// 姿态控制任务等待信号量
void attitude_control_task(void *pvParameters) {
while(1) {
xSemaphoreTake(xIMUSemaphore, portMAX_DELAY);
// 读取传感器数据,计算姿态
}
}
1.3.3 消息队列(Message Queue)
消息队列用来传递数据。比如GPS模块把位置数据打包成消息,发给导航任务。
我建议:能用消息队列就别用全局变量。全局变量容易被多个任务乱改,出了bug极难排查。消息队列自带互斥保护,省心很多。
1.3.4 互斥量(Mutex)
互斥量用来保护共享资源。比如两个任务都要写同一个串口,不加互斥量的话,数据会混在一起。
特别注意:优先级反转
互斥量有个经典问题叫「优先级反转」。低优先级任务拿着锁,高优先级任务等锁,中间优先级的任务反而先执行了。解决办法是用「优先级继承」协议。FreeRTOS的互斥量默认支持这个特性,记得用 xSemaphoreCreateMutex() 而不是 xSemaphoreCreateBinary()。
1.4 无人机为什么需要RTOS?
这个问题,我直接说结论:没有RTOS,你很难写出稳定可靠的飞控程序。
为什么?我给你拆开来看:
- 多任务并发:飞控要同时处理传感器采集、姿态解算、控制输出、通信、日志记录……如果用裸机写,你得在一个大循环里手动调度这些任务。任务一多,代码就成了一团乱麻。
- 实时性要求:姿态控制环通常跑在1kHz,也就是1毫秒一个周期。裸机里一个延时函数就能把整个系统卡死。RTOS里,高优先级任务可以精确地按时执行。
- 模块化开发:RTOS让每个功能模块独立成一个任务。传感器驱动一个任务,控制算法一个任务,通信一个任务。出了问题,定位也快。我曾经接手过一个裸机飞控项目,一个main函数写了3000行,改一个bug要花三天。
- 资源管理:RTOS帮你管理CPU时间、内存、外设。你只需要关心业务逻辑,不用操心「这个任务跑了多久」「那个中断会不会冲突」。
我的真实经历
2018年我做一款四轴飞行器,刚开始用裸机写。飞起来倒是能飞,但一遇到强风,飞控就卡顿,因为传感器中断和PWM输出抢CPU时间。后来移植了FreeRTOS,把姿态控制任务优先级提到最高,电机控制用定时器硬件触发,问题就解决了。那次之后,我再也没用裸机写过飞控。
1.5 小结
这一章我们聊了RTOS的基本概念。记住三个关键词:任务调度、内核对象、实时性。下一章,我会带你实际移植一个RTOS到STM32上,手把手教你配置时钟、中断和任务栈。
嗯,先消化一下这些概念。有问题随时问我。