PX4 的任务模型与调度思路——任务、线程与 WorkQueue 在飞控系统中的角色

在前面的章节中,我们已经介绍了:

  • PX4 系统启动流程

  • rcS 启动脚本

  • 各个模块如何被启动

但一个核心问题仍然没有回答:

这些模块是如何被调度运行的?

无人机飞控系统属于 实时控制系统,需要保证:

  • IMU 数据高频稳定采集(400Hz–1kHz)

  • 姿态控制算法周期稳定执行

  • 飞行状态机实时响应

因此 PX4 必须具备一套高效且可控的 任务调度模型

本章将从工程角度介绍:

  1. PX4 运行的操作系统基础

  2. Task(独立任务)模型

  3. WorkQueue 工作队列机制

  4. 优先级与实时性设计

  5. 当前 PX4 版本的调度架构

同时回答一个很多开发者关心的问题:

现代 PX4 还有 Task 吗?

答案是:

有,但大多数控制模块已经迁移到 WorkQueue


一、PX4 运行在什么系统之上

PX4 在硬件飞控(Pixhawk 系列)上通常运行在

NuttX RTOS 之上

NuttX 是一个 POSIX 风格的实时操作系统,其调度核心包括:

  • 任务(Task)

  • 线程(pthread)

  • 优先级调度

  • 实时策略(FIFO / Round Robin)

在 NuttX 中,调度单元基于 TCB(Task Control Block)

无论是:

  • task_create()

  • pthread_create()

最终都由同一调度器管理

因此在 PX4 工程实践中:

Task 可以理解为一个独立执行单元,由 NuttX 调度器管理


二、PX4 模块(Module)的执行方式

PX4 的核心功能以 模块(Module) 的形式存在

源码路径:

src/modules/

例如常见模块:

commander
navigator
sensors
mc_att_control
ekf2
land_detector
logger

这些模块通常提供 CLI 命令:

module start
module stop
module status

例如:

commander start

模块启动后会进入自己的运行循环

例如典型结构:

while (!should_exit()) {
    Run();
}

但问题是:

这些模块究竟是如何运行的?

PX4 提供两种执行方式:

  • 独立 Task(任务)

  • WorkQueue 工作队列


三、PX4 的 Task(独立任务)

在 PX4 早期版本中,大量模块运行在 独立 Task

Task 的特点:

  • 每个模块运行在 独立执行单元

  • 具有 独立栈空间

  • 可以设置 独立优先级

任务通常通过以下接口创建:

px4_task_spawn_cmd()

示例代码:

px4_task_spawn_cmd(
    "commander",
    SCHED_DEFAULT,
    SCHED_PRIORITY_DEFAULT,
    4096,
    (px4_main_t)&run_trampoline,
    argv);

1、Task 的底层实现

px4_task_spawn_cmd() 是 PX4 对任务创建接口的封装

源码路径:

platforms/common/px4_platform_common/tasks.cpp

NuttX 平台上,该函数最终调用:

task_create()

kthread_create()

也就是说:

PX4 创建的是 NuttX 的任务(task)

虽然接口风格参考 POSIX,但底层并不是 pthread_create()


2、Task 的优点

独立优先级

例如不同模块可以设置不同优先级:

EKF2
commander
navigator
logger

关键控制模块可以优先执行


任务之间互不阻塞

独立任务可以进行:

  • poll()

  • sleep()

  • 阻塞 IO

不会影响其他模块运行


3、Task 的缺点

Task 模型也存在明显问题:

RAM 消耗较大

每个任务需要独立栈空间:

4KB
8KB
甚至更大

对于 MCU(如 STM32)来说,RAM 是非常宝贵的资源


上下文切换开销

多个任务运行时,RTOS 会产生:

  • context switch

  • cache miss

  • 调度延迟

对于高频控制任务来说不理想


四、WorkQueue 工作队列模型

为了减少资源消耗,PX4 引入了 WorkQueue 调度机制

WorkQueue 的核心思想是:

多个模块共享一个线程运行

结构示意:

WorkQueue Thread
   ├── Module A
   ├── Module B
   └── Module C

也就是说:

  • 模块不再各自拥有线程

  • 而是作为 WorkItem 被调度执行


1、WorkQueue 基类

PX4 提供一个核心基类:

ScheduledWorkItem

源码路径:

platforms/common/include/px4_platform_common/px4_work_queue/

主要文件:

ScheduledWorkItem.hpp
WorkItem.hpp
WorkQueue.hpp

模块只需继承该类:

class McAttControl :
    public ModuleBase<McAttControl>,
    public px4::ScheduledWorkItem

2、WorkQueue 调度示例

典型初始化方式:

McAttControl::McAttControl() :
    ScheduledWorkItem(MODULE_NAME,
        px4::wq_configurations::rate_ctrl)
{
    ScheduleOnInterval(4000);
}

这里表示:

每 4000us 执行一次

即:

250Hz 控制循环

五、WorkQueue 的优先级设计

WorkQueue 不是统一优先级

PX4 定义了多个工作队列,每个队列拥有:

  • 独立线程

  • 独立优先级

例如:

rate_ctrl
hp_default
lp_default

典型用途:

WorkQueue 用途
rate_ctrl 控制回路
hp_default 高频任务
lp_default 低优先级任务

同一 WorkQueue 中的模块:

  • 共享线程

  • 共享栈

  • 共享优先级

不同 WorkQueue 之间则具有不同优先级


六、WorkQueue 的触发方式

WorkQueue 任务通常由三种方式触发


1 定时调度

最简单方式:

ScheduleOnInterval()

例如:

400Hz
250Hz
100Hz

常见于控制算法


2 uORB 数据触发

现代 PX4 更常用的方式是:

uORB 回调机制

例如:

uORB::SubscriptionCallbackWorkItem _sensor_sub{
    this,
    ORB_ID(sensor_combined)
};

初始化:

_sensor_sub.registerCallback();

执行流程:

IMU driver publish
        ↓
uORB notify
        ↓
callback
        ↓
WorkQueue 调度 Run()

这样可以保证:

控制算法紧跟传感器更新频率执行


3 延迟执行

例如:

ScheduleDelayed()

适用于:

  • 参数更新

  • 状态检测

  • 周期检查任务


七、当前 PX4 的 Task 与 WorkQueue 分布

现代 PX4(1.14 / 1.15)采用:

Task + WorkQueue 混合模型

大多数控制模块运行在 WorkQueue

而少数模块仍然使用独立 Task


1、WorkQueue 模块(多数)

例如:

sensors
mc_att_control
mc_rate_control
fw_att_control
land_detector
ekf2

这些模块具有:

  • 高频运行

  • 不允许阻塞

  • 周期明确

因此适合 WorkQueue


2、独立 Task 模块(少数)

例如:

commander
navigator
mavlink
logger
micrortps

这些模块可能会:

  • 进行 IO

  • 阻塞等待

  • 执行复杂状态机

因此需要独立任务


八、PX4 调度设计的核心思想

PX4 的调度架构遵循三个原则


1 控制任务优先级最高

例如:

IMU
Rate Control
Attitude Control

运行频率通常:

250Hz – 1000Hz

必须保证实时执行


2 数据驱动执行

PX4 的很多任务不是简单周期运行,而是:

数据驱动

例如:

IMU → attitude control

IMU 更新后立即触发控制算法


3 WorkQueue 任务避免阻塞

在 WorkQueue 中禁止:

sleep()
poll()
阻塞 IO

否则会阻塞整个队列


九、如何查看 PX4 运行中的任务

PX4 提供了一些非常有用的调试命令

查看系统任务:

top

可以看到:

  • PID

  • CPU 使用率

  • 优先级

  • 栈使用情况

查看工作队列:

work_queue status

查看模块状态:

mc_att_control status

监听 uORB 数据:

listener sensor_combined

这些命令在调试飞控系统时非常有用


十、总结

PX4 的调度架构经历了明显演进

早期 PX4:

大量使用 Task

现代 PX4:

WorkQueue 为主
Task 为辅

整体执行结构可以理解为:

Module
   ↓
WorkItem / Task
   ↓
Thread
   ↓
NuttX Scheduler

其中:

  • Task:独立任务

  • WorkQueue:共享线程任务

这种设计既保证了:

  • 实时控制性能

又兼顾:

  • MCU 资源限制

是飞控系统工程设计中的一种经典调度架构

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐