4. PX4代码结构与驱动目录

在PX4中开发自定义传感器驱动,第一步不是写代码,而是理解代码放在哪里、如何被编译、如何被启动。PX4采用“模块化驱动 + 启动脚本 + CMake构建 + 模块加载”的组合机制,驱动并不是自动运行的,而是通过启动脚本显式加载。掌握这条链路,才能让新驱动真正进入系统。

一、src/drivers 的组织方式

PX4源码中,src/drivers 目录是传感器和执行器驱动的主要存放位置。它按“总线类型 + 设备类别”进行分层组织,常见结构如下:

src/drivers/
├── barometer/        # 气压计驱动
├── distance_sensor/  # 测距/激光雷达驱动
├── gps/              # GPS驱动
├── imu/              # 惯性测量单元驱动
├── magnetometer/     # 磁力计驱动
├── optical_flow/     # 光流驱动
├── px4io/            # PX4IO协处理器
├── rgbled/           # RGB LED驱动
├── telemetry/        # 数传驱动
└── ...

每个驱动子目录通常包含:驱动源文件(.cpp)、头文件、CMakeLists.txt 以及可选的 Kconfig 或参数定义文件。以 src/drivers/imu 为例,内部还会按具体芯片型号继续细分,例如 invensense/icm42688p、bosch/bmi088 等。这种组织方式让驱动与硬件型号一一对应,便于裁剪和复用。

自定义驱动建议放在 src/drivers/ 下新建独立子目录,例如 src/drivers/my_sensor/,避免直接修改公共目录,方便后续升级和版本管理。

二、CMake构建:驱动如何被编译进固件

PX4使用CMake作为构建系统。每个驱动目录下的 CMakeLists.txt 负责声明该驱动的源文件、依赖库和编译选项。典型内容如下:

px4_add_module(
  MODULE drivers__my_sensor
  MAIN my_sensor
  SRCS
    my_sensor.cpp
  DEPENDS
    drivers__imu
)

其中 MODULE 是模块名,MAIN 是模块入口,SRCS 列出源文件,DEPENDS 声明依赖。构建时,CMake会递归扫描 src/drivers 下的所有 CMakeLists.txt,根据目标板配置决定是否将该驱动编入固件。对于PX4,常用命令为:

make px4_fmu-v6x_default
# 或
make px4_sitl_default

如果驱动只针对特定硬件,可通过 CONFIG_DRIVERS_MY_SENSOR=y 在板级配置中启用,避免影响其他目标。

三、启动脚本:驱动加载的入口

PX4启动脚本位于 ROMFS/px4fmu_common/init.d/,常见文件包括 rcS、rc.sensors、rc.mc_defaults 等。驱动加载通常写在 rc.sensors 或板级启动脚本中,例如:

# 启动自定义传感器驱动
my_sensor start -X -b 1

# 启动IMU驱动
icm42688p start -s

启动脚本在系统上电后由 rcS 依次执行,负责初始化总线、加载驱动、设置参数。驱动模块通常提供 start、stop、status 等命令,通过 module 机制注册到系统。若驱动未在脚本中启动,即使编译进固件也不会运行。

常见误区:只修改了 CMakeLists.txt 并编译成功,却忘记在启动脚本中添加 start 命令,导致驱动“编译进去了但没跑起来”。排查时先用 my_sensor status 确认模块是否存在。

四、驱动加载机制与模块生命周期

PX4驱动本质上是可加载模块。模块通过 px4_add_module 注册,运行时由 module 命令管理。加载流程可概括为:

  1. 系统启动,执行 rcS 及子脚本;
  2. 脚本调用 my_sensor start;
  3. 模块入口函数执行,初始化总线、注册 uORB 主题;
  4. 驱动进入工作循环,周期读取传感器数据并发布;
  5. 可通过 my_sensor stop 停止,释放资源。

驱动与飞控之间的数据交互通过 uORB 完成。例如自定义传感器发布 sensor_my_data 主题,其他模块订阅后即可使用。加载机制的核心是“脚本驱动 + 模块管理 + uORB通信”。

PX4 驱动从源码到运行的加载链路 src/drivers/ my_sensor/ my_sensor.cpp CMakeLists.txt px4_add_module 编译进固件 固件镜像 px4_fmu-v6x 包含 my_sensor 模块 启动脚本 rc.sensors my_sensor start 模块管理 module start/stop status 查询 uORB 通信 sensor_my_data 发布/订阅 烧录/运行 关键检查点 1. 驱动目录是否被 CMake 扫描到? → 检查 src/drivers/CMakeLists.txt 是否 add_subdirectory 2. 模块是否编入固件? → 查看 build 日志或使用 module list 确认 3. 启动脚本是否调用 start? → 检查 rc.sensors 或板级 init.d 脚本

五、案例:自定义驱动接入流程

假设要新增一个I2C温度传感器驱动 my_temp,完整接入步骤如下:

步骤 操作 文件/命令
1 创建驱动目录 src/drivers/my_temp/
2 编写驱动源码 my_temp.cpp、my_temp.hpp
3 声明CMake模块 CMakeLists.txt 使用 px4_add_module
4 注册到父级CMake 在 src/drivers/CMakeLists.txt 添加 add_subdirectory(my_temp)
5 添加启动命令 rc.sensors 中加入 my_temp start
6 编译并烧录 make px4_fmu-v6x_default upload
7 验证运行 my_temp status、listener sensor_temp

六、常见误区与复盘

  • 只改驱动,不改CMake:驱动文件不会被编译,固件中不存在该模块。
  • 只改CMake,不改启动脚本:模块存在但不会自动运行,需手动 start。
  • 模块名冲突:MODULE 名必须唯一,否则链接或加载会失败。
  • 依赖缺失:若驱动依赖其他模块,需在 DEPENDS 中声明,否则运行时报错。
  • 启动顺序错误:传感器驱动应在总线初始化之后启动,否则I2C/SPI访问失败。
复盘建议:每次新增驱动后,按“编译日志 → module list → 启动脚本 → uORB 数据”四步逐项确认,可快速定位问题所在环节。

理解 src/drivers 的组织方式、CMake构建规则、启动脚本和模块加载机制,是PX4自定义传感器驱动开发的基础。只有把驱动正确放入目录、编入固件、写入启动脚本,并通过uORB发布数据,才算真正完成一次驱动接入。