2. PX4架构概览

理解PX4的架构,是进行自定义模块与uORB开发的前提。PX4并不是一个“从上到下顺序执行”的单片机程序,而是一个运行在NuttX实时操作系统之上的模块化飞行控制软件栈。它把姿态估计、位置控制、传感器驱动、通信链路、日志记录等功能拆分成一个个独立模块,模块之间通过uORB(micro Object Request Broker)这一轻量级发布/订阅消息总线交换数据。掌握这套结构,才能知道自己的代码应该“插”在哪里、和谁通信、由谁启动。

一、PX4软件栈的分层结构

从下往上,PX4可以划分为四个层次,每一层职责清晰、边界明确:

  • 硬件与驱动层:包括STM32等MCU、IMU、磁力计、气压计、GPS、PWM输出等外设,以及对应的传感器驱动模块(如 px4fmu、ekf2 依赖的IMU驱动)。
  • 操作系统与中间件层:NuttX提供任务调度、内存管理、文件系统;uORB、参数系统(param)、日志系统(logger)、MAVLink通信构成中间件。
  • 核心功能模块层:传感器融合(ekf2)、姿态控制(mc_att_control)、位置控制(mc_pos_control)、导航器(navigator)、commander状态机等。
  • 应用与接口层:MAVSDK/QGC地面站、自定义业务模块、外部机载计算机通过MAVLink或uORB桥接接入。

这种分层让PX4既能保证实时控制回路的确定性,又能灵活扩展上层应用。下图展示了PX4软件栈的层次关系以及uORB在其中的枢纽位置:

PX4 软件栈与 uORB 枢纽位置 应用与接口层 QGC / MAVSDK 自定义业务模块 机载计算机桥接 核心功能模块层 ekf2 状态估计 mc_att_control 姿态控制 mc_pos_control 位置控制 navigator 导航 commander 状态机 uORB 消息总线(发布 / 订阅) sensor_combined · vehicle_attitude · vehicle_local_position · vehicle_command · actuator_controls 操作系统与中间件层 NuttX RTOS param 参数 logger 日志 mavlink 通信 硬件与驱动层 STM32 MCU IMU / 磁力计 气压计 / GPS PWM / 执行机构

二、模块化设计:一切皆模块

PX4的模块化体现在两个层面。第一是编译期模块化:每个模块是一个独立的C++类,继承自 ModuleBase 或 ScheduledWorkItem,通过 CMakeLists.txt 注册进固件。第二是运行期模块化:模块以NuttX任务(task)或工作队列(work queue)的形式运行,可以独立启动、停止、查看状态。

模块之间不直接调用函数,而是通过uORB主题解耦。例如 ekf2 订阅 sensor_combined,发布 vehicle_attitude 和 vehicle_local_position;mc_att_control 订阅 vehicle_attitude 和 vehicle_control_mode,发布 vehicle_attitude_setpoint。这种“发布者不知道订阅者是谁”的设计,使得新增一个自定义模块时,只需订阅已有主题、发布新主题,无需修改任何现有模块。

核心思想:uORB是PX4模块化架构的“神经系统”。没有uORB,模块化就退化成函数调用链;有了uORB,模块才能独立编译、独立运行、独立替换。

三、启动流程:从上电到解锁

PX4的启动流程由启动脚本驱动,核心文件是 ROMFS/px4fmu_common/init.d/rcS 及其调用的 rc.board_defaults、rc.mavlink 等。启动顺序大致如下:

  1. Bootloader:加载固件到内存并跳转。
  2. NuttX初始化:建立任务调度、挂载文件系统、初始化串口。
  3. rcS脚本执行:依次启动传感器驱动、ekf2、commander、navigator、mc_att_control、mc_pos_control、logger、mavlink 等模块。
  4. commander状态机:进入 STANDBY,等待解锁信号,随后进入 ARMED 并驱动控制回路。

在终端中可以用以下命令查看模块运行状态和uORB主题:

# 查看所有运行中的模块
px4> top

# 查看指定模块状态
px4> ekf2 status
px4> commander status

# 列出所有uORB主题
px4> uorb top

# 监听某个主题的发布频率
px4> listener sensor_combined

# 手动启动一个模块
px4> mc_att_control start

四、uORB在架构中的位置与作用

uORB位于中间件层,是模块间通信的唯一标准通道。它的核心机制是发布/订阅:发布者调用 orb_publish() 写入主题,订阅者通过 orb_subscribe() 获取句柄并调用 orb_copy() 读取最新数据。uORB内部为每个主题维护一个环形缓冲区,支持多订阅者、非阻塞读取和超时等待。

主题名称 典型发布者 典型订阅者 作用
sensor_combined 传感器驱动 ekf2 汇总IMU、磁力计、气压计数据
vehicle_attitude ekf2 mc_att_control、logger 输出姿态四元数
vehicle_local_position ekf2 mc_pos_control、navigator 输出本地位置与速度
vehicle_command commander、mavlink navigator、自定义模块 传递解锁、模式切换等指令
actuator_controls mc_att_control mixer、PWM输出 输出归一化控制量

五、案例与常见误区

案例:假设要开发一个“低电量自动返航”自定义模块。正确做法是:订阅 battery_status 和 vehicle_local_position,当电量低于阈值时,向 vehicle_command 发布一条 VEHICLE_CMD_DO_SET_MODE 或 VEHICLE_CMD_NAV_RETURN_TO_LAUNCH 命令,由 commander 和 navigator 执行。这样模块不直接控制电机,符合架构分层。

常见误区:
  • 在自定义模块中直接调用其他模块的函数,破坏模块化解耦。
  • 忘记在 CMakeLists.txt 中注册模块,导致编译后无法启动。
  • 订阅主题后未检查 orb_copy() 返回值,读取到过期数据。
  • 在中断或高优先级任务中执行耗时操作,阻塞uORB发布。

总结来说,PX4的架构可以概括为:NuttX提供运行环境,模块化提供扩展能力,启动脚本负责编排,uORB负责连接。理解这四者的关系,后续编写自定义模块时就能准确找到自己的位置,知道该订阅什么、发布什么、由谁启动、如何调试。