4. PX4源码结构与编译系统

PX4 的源码并不是一堆散乱的 C++ 文件,而是一套“目录即架构”的工程组织方式。理解目录结构、模块划分、uORB 消息机制、启动脚本和编译烧录流程,是后续修改固定翼与 VTOL 控制逻辑、添加自定义模块、调试飞行行为的基础。本章从工程视角梳理 PX4 的代码骨架,并给出可落地的编译与烧录方法。

一、代码目录:从顶层看模块边界

PX4 固件仓库(PX4-Autopilot)顶层目录大致可分为“平台层、驱动层、模块层、库层、板级配置层”五类。常见目录及其职责如下:

目录职责典型内容
src/modules飞行功能模块commander、navigator、mc_pos_control、fw_pos_control、vtol_att_control
src/drivers传感器与执行器驱动imu、barometer、gps、pwm_out、uavcan
src/lib通用算法与工具库mathlib、ecl、geo、mixer、parameters
src/platformsOS 与硬件抽象posix、nuttx、common
boards板级配置与链接脚本px4_fmu-v5、px4_fmu-v6x、default.px4board
ROMFS启动脚本与混控文件init.d/rcS、mixers、airframes
msguORB 消息定义vehicle_attitude.msg、sensor_combined.msg

其中 src/modules 是开发者最常改动的区域。固定翼相关模块包括 fw_att_control、fw_pos_control、fw_rate_control;VTOL 则额外涉及 vtol_att_control 与 vtol_control,它们负责在多旋翼与固定翼模式之间切换控制律。

二、模块划分与 uORB 消息机制

PX4 采用“模块 + 消息”的解耦设计。每个模块通常是一个独立任务或工作队列,通过 uORB(micro Object Request Broker)发布和订阅消息,而不是直接函数调用。这样做的核心好处是:模块之间无编译依赖,可独立启停,便于在资源受限的飞控上按需加载。

uORB 的基本使用模式是:发布者调用 orb_advertise() 获得句柄,再用 orb_publish() 发送数据;订阅者用 orb_subscribe() 订阅,用 orb_copy() 读取最新数据。消息定义放在 msg/ 目录下,编译时自动生成 C++ 头文件。

// 发布 vehicle_attitude 示例
orb_advert_t att_pub = orb_advertise(ORB_ID(vehicle_attitude), &att);
orb_publish(ORB_ID(vehicle_attitude), att_pub, &att);

// 订阅 sensor_combined 示例
int sub = orb_subscribe(ORB_ID(sensor_combined));
struct sensor_combined_s raw;
orb_copy(ORB_ID(sensor_combined), sub, &raw);

在终端中可用 uorb top 查看各消息的发布频率,用 listener vehicle_attitude 查看具体字段。调试固定翼姿态异常时,先确认 vehicle_attitude 与 vehicle_local_position 是否正常更新,往往比直接读代码更快定位问题。

PX4 模块与 uORB 数据流示意 传感器驱动 imu / gps / baro estimator EKF2 / 姿态估计 navigator 航点 / 任务 fw / vtol 控制 姿态 / 位置控制 uORB 消息总线 sensor_combined / vehicle_attitude / vehicle_local_position / trajectory_setpoint actuators / pwm_out 混控器 → 电机 / 舵机

三、启动脚本:ROMFS 中的 rcS 与 airframe

PX4 上电后由 NuttX 启动,随后执行 ROMFS/px4fmu_common/init.d/rcS。rcS 负责挂载 SD 卡、加载参数、启动传感器驱动、启动 estimator 与控制模块。不同机型的差异通过 rc.interface 和 airframe 配置脚本实现。

# 典型启动片段
if param compare SYS_AUTOSTART 4001
then
    set AIRFRAME px4_fmu-v5_default
    sh /etc/init.d/rc.fw_default
fi

# 启动固定翼姿态控制
fw_att_control start
fw_pos_control start
navigator start

对于 VTOL,启动脚本会同时加载多旋翼与固定翼控制模块,并由 vtol_att_control 根据 VTOL_TYPE 参数决定倾转或尾座式切换逻辑。修改 airframe 后必须重新编译并烧录,否则 ROMFS 中的脚本不会更新。

四、编译流程与固件烧录

PX4 使用 CMake + Ninja 构建系统。标准流程如下:

  1. 安装工具链:./Tools/setup/ubuntu.sh 或使用官方 Docker 镜像。
  2. 进入源码目录,执行 make px4_fmu-v5_default 编译目标板固件。
  3. 编译产物位于 build/px4_fmu-v5_default/,生成 .px4 固件文件。
  4. 使用 QGroundControl 或 make px4_fmu-v5_default upload 烧录。
# 常用编译命令
make px4_fmu-v5_default              # 编译 FMUv5 固件
make px4_fmu-v6x_default             # 编译 FMUv6X 固件
make px4_fmu-v5_default upload       # 编译并上传
make clean                           # 清理构建目录
make distclean                       # 彻底清理

提示:首次编译建议使用 make px4_fmu-v5_default 验证工具链。若出现 Python 依赖错误,优先检查 pip3 install -r Tools/setup/requirements.txt 是否执行成功。

五、案例与常见误区

假设要为固定翼增加一个自定义日志模块。正确做法是:在 src/modules/ 下新建目录,编写 CMakeLists.txt 与模块源文件,在 boards/px4/fmu-v5/default.px4board 中启用该模块,然后重新编译。若只在源码中写模块却未加入板级配置,编译不会报错,但固件中不会包含该模块,运行时执行 my_module start 会提示命令不存在。

误区:直接修改 build/ 下的生成文件。这些文件由 CMake 自动生成,下次编译会被覆盖。所有修改必须落在 src/、msg/、ROMFS/ 或 boards/ 中。

另一个高频问题是 uORB 消息未更新。若 listener vehicle_attitude 显示频率为 0,通常不是控制模块的问题,而是上游 estimator 未启动或传感器驱动未加载。此时应检查 rcS 启动顺序与 uorb top 输出,而不是直接修改控制律参数。

掌握目录结构、uORB 机制、启动脚本与编译烧录流程后,后续修改固定翼与 VTOL 控制模块时,就能快速定位代码位置、验证消息流、完成固件部署,形成“改代码—编译—烧录—验证”的完整闭环。