18 KiB
PX4 NuttX 操作系统架构与运行机制
1. 当前系统配置
本文针对当前工程和飞控配置编写:
- 飞控:CUAV 7-Nano V2
- MCU:STM32H753II,ARM Cortex-M7
- PX4版本:
v1.17.0 - 固件目标:
cuav_7-nano_minimal_vtol - 操作系统:Apache NuttX RTOS
- 当前NuttX源码提交:
fb2fadf6f599c1406f052db013efd00a2518e72c - 地址空间模型:Flat Build
- 系统基础Tick:
1 ms - 初始化入口:
nsh_main
准确地说:
NuttX是底层实时操作系统
PX4是运行在NuttX上的飞控软件平台和应用程序集
PX4本身不是操作系统。PX4使用NuttX提供的任务调度、中断、文件系统、设备驱动、内存管理和同步机制,完成传感器采集、状态估计、飞行控制和通信。
2. 整体软件分层
PX4飞行功能
EKF2、姿态控制、位置控制、VTOL、MAVLink、Logger
│
▼
PX4基础框架
uORB、参数系统、Work Queue、设备抽象、事件系统
│
▼
NuttX实时操作系统
任务调度、线程、文件系统、驱动模型、网络、内存管理
│
▼
STM32H753硬件
CPU、Flash、RAM、UART、SPI、I2C、CAN、USB、SD卡、定时器
以当前GPS2为例:
GPS模块输出UBX串口数据
│
▼
STM32 UART硬件
│
▼
NuttX串口驱动:/dev/ttyS6
│
▼
PX4 gps驱动解析UBX协议
│
▼
发布uORB主题sensor_gps
│
▼
EKF2检查并融合GNSS数据
NuttX负责UART字节收发,但不理解GNSS定位。UBX解析、数据健康检查和EKF融合属于PX4功能。
3. NuttX是什么
NuttX是面向微控制器的实时操作系统(RTOS),特点包括:
- 基于优先级的抢占式实时调度。
- 支持任务、POSIX线程和信号。
- 支持信号量、互斥锁和消息队列。
- 提供接近POSIX的编程接口。
- 使用类似Unix的设备文件模型。
- 支持多种文件系统。
- 支持UART、SPI、I2C、CAN、USB、SDMMC和网络。
- 占用空间远小于Linux。
- 可以直接运行在STM32等微控制器上。
飞控需要可预测的实时响应。例如陀螺仪产生新数据后,角速度控制器不能因为SD卡正在写日志或MAVLink正在发送数据而长期得不到CPU。
NuttX通过优先级调度确保高优先级控制任务可以抢占低优先级后台任务。
4. 实时任务调度
NuttX的基本调度方式是:
高优先级任务变为就绪
│
▼
抢占当前低优先级任务
│
▼
执行高优先级实时工作
│
▼
高优先级任务阻塞或完成
│
▼
低优先级任务继续执行
当前构建配置:
CONFIG_USEC_PER_TICK=1000
CONFIG_RR_INTERVAL=0
CONFIG_PRIORITY_INHERITANCE=y
含义:
- 基础系统Tick是
1000 us,即1 ms。 - 当前没有启用同优先级任务的固定时间片轮转间隔。
- 启用了优先级继承。
4.1 为什么1 ms Tick不限制高频控制
PX4的高频控制并不只依赖NuttX的1 ms基础Tick,还会使用:
- 硬件高分辨率定时器(HRT)。
- IMU数据就绪中断。
- DMA完成中断。
- uORB订阅回调。
- PX4定时工作队列。
因此传感器和控制回路可以按照数据到达事件或更精确的时间基准运行。
4.2 优先级继承
优先级反转示例:
低优先级任务持有互斥锁
│
高优先级控制任务需要该锁
│
中优先级任务持续抢占低优先级任务
│
高优先级任务长时间等待
启用优先级继承后,持锁的低优先级任务可以临时继承等待者的高优先级,尽快释放锁,减少高优先级任务的阻塞时间。
5. PX4模块如何运行
PX4模块主要有两种运行形式。
5.1 独立任务或线程
部分模块拥有独立任务、独立栈和调度上下文,适合:
- 需要阻塞等待设备的驱动。
- 独立通信循环。
- 较复杂的后台服务。
- 命令行或管理任务。
在NSH中可以查看任务:
ps
查看CPU和栈使用情况:
top
5.2 PX4 Work Queue
许多PX4模块不各自建立一个永久线程,而是作为WorkItem挂到共享工作队列。
多个模块WorkItem
│
▼
按时间或数据回调加入队列
│
▼
工作队列线程按顺序执行到期任务
优点:
- 减少线程数量。
- 减少每个线程所需的独立栈内存。
- 将相关功能放到合适的优先级。
- 方便使用传感器或uORB事件驱动模块运行。
当前PX4定义的典型工作队列包括:
wq:rate_ctrl 角速度控制相关高优先级队列
wq:nav_and_controllers 导航、姿态和位置控制队列
wq:hp_default 默认高优先级工作
wq:lp_default 低优先级后台工作
wq:INS0~INS3 IMU和惯性传感器工作
wq:SPI0~SPI6 SPI总线相关工作
wq:I2C0~I2C4 I2C总线相关工作
wq:ttyS0~ttyS9 串口相关工作
wq:uavcan DroneCAN/UAVCAN工作
当前部分模块对应关系:
| PX4模块 | 工作队列 |
|---|---|
mc_rate_control |
wq:rate_ctrl |
vtol_att_control |
wq:rate_ctrl |
control_allocator |
wq:rate_ctrl |
vehicle_angular_velocity |
wq:rate_ctrl |
mc_att_control |
wq:nav_and_controllers |
mc_pos_control |
wq:nav_and_controllers |
fw_att_control |
wq:nav_and_controllers |
fw_lateral_longitudinal_control |
wq:nav_and_controllers |
airspeed_selector |
wq:nav_and_controllers |
land_detector |
wq:nav_and_controllers |
load_mon |
wq:lp_default |
temperature_compensation |
wq:lp_default |
可以尝试查看当前工作队列状态:
work_queue status
具体可用命令以当前固件的 help 输出为准。
6. uORB模块通信
PX4模块之间主要使用uORB发布/订阅系统通信:
发布者
│ publish
▼
uORB主题
│ subscribe
▼
一个或多个订阅者
例如:
gps驱动
│ 发布sensor_gps
▼
EKF2和GPS选择模块
│ 发布位置估计
▼
位置控制器和导航模块
uORB是PX4自己实现的中间件,不等于NuttX内核的消息队列。
NuttX提供:
- 线程和任务调度。
- 信号量和互斥锁。
- 等待和唤醒机制。
- 设备驱动接口。
- 内存管理。
PX4在这些机制之上实现uORB,使模块之间不需要直接调用彼此。
例如EKF2不需要知道GPS驱动对象的地址,只需订阅 sensor_gps。
控制台可以观察uORB数据:
listener sensor_gps -n 3
listener vehicle_attitude -n 3
listener vehicle_local_position -n 3
7. Flat地址空间模型
当前实际构建配置:
CONFIG_BUILD_FLAT=y
CONFIG_BUILD_PROTECTED未启用
CONFIG_BUILD_KERNEL未启用
这表示:
NuttX内核
设备驱动
PX4基础框架
所有PX4模块
│
▼
共享同一个地址空间
7.1 优点
- 函数调用开销低。
- 数据交换快。
- 内存占用较小。
- 不需要复杂的系统调用和地址空间切换。
- 适合资源有限的实时微控制器。
7.2 缺点
- 模块之间没有Linux进程级的内存隔离。
- 一个模块发生非法指针访问可能破坏其他模块的数据。
- 单个严重错误可能引起整个飞控HardFault和重启。
- 不能像Linux一样单独隔离一个崩溃进程。
因此PX4中的“模块”通常是同一固件地址空间中的线程、任务或WorkItem,不是Linux中完全隔离的进程。
8. 内存结构
CUAV 7-Nano使用STM32H753,主要存储区域如下。
8.1 内部Flash
0x08000000
┌──────────────────────────────┐
│ PX4 Bootloader │ 128 KiB
├──────────────────────────────┤ 0x08020000
│ PX4应用 │ 1920 KiB
│ NuttX + PX4 + ROMFS │
└──────────────────────────────┘
8.2 RAM
ITCM RAM 64 KiB
DTCM1 RAM 64 KiB
DTCM2 RAM 64 KiB
AXI SRAM 512 KiB
SRAM1 128 KiB
SRAM2 128 KiB
SRAM3 32 KiB
SRAM4 64 KiB
Backup RAM 4 KiB
不同RAM区域有不同的访问延迟、总线和DMA能力。任务栈、堆、DMA缓冲区、关键数据以及备份数据不一定放在同一区域。
NuttX负责:
- 创建和回收任务栈。
- 管理堆内存。
- 管理文件描述符。
- 管理信号量和互斥锁。
- 处理中断和上下文切换。
常用观察命令:
free
top
ps
分析时重点关注:
- 空闲堆内存是否持续下降。
- 某个任务栈是否接近耗尽。
- CPU负载是否持续过高。
- 是否出现HardFault、断言或栈溢出。
9. 设备文件模型
NuttX使用类似Unix的设备文件访问硬件。例如当前板卡的串口映射:
GPS1 → /dev/ttyS0
TELEM3 → /dev/ttyS1
TELEM2 → /dev/ttyS3
TELEM1 → /dev/ttyS5
GPS2 → /dev/ttyS6
USB → /dev/ttyACM0(运行阶段按配置创建)
应用程序通过 open()、read()、write()、ioctl() 等接口访问设备。
例如MAVLink模块打开:
/dev/ttyS5
就可以通过TELEM1收发MAVLink字节。
同一个串口通常不能同时分配给多个模块,否则会出现端口占用冲突或数据协议混乱。
10. 文件系统
当前NuttX构建启用了:
FAT
ROMFS/CROMFS
procfs
BINFS
MTD
10.1 ROMFS/CROMFS
PX4固件内部携带只读文件系统,包含:
/etc/init.d/rcS
/etc/init.d/rc.vtol_defaults
/etc/init.d/airframes/13200_generic_vtol_tailsitter
这些文件在编译时被打包进PX4应用固件并写入MCU内部Flash,不在SD卡中。
10.2 FAT与SD卡
SD卡通常使用FAT文件系统,并挂载到:
/fs/microsd
主要保存:
.ulg飞行日志。- HardFault记录。
- 用户自定义配置文件。
- 自定义日志主题。
- 任务和运行时数据。
常用命令:
ls /fs/microsd
df
mount
10.3 procfs
procfs提供运行中的系统和任务状态。ps、任务栈信息等工具可以利用procfs数据。
10.4 MTD
MTD表示Memory Technology Device,用于访问Flash、FRAM或其他非易失性存储设备及其分区。
PX4可以使用这些区域保存:
- 参数。
- 硬件信息。
- 校准和持久化数据。
- 网络配置等板级数据。
具体保存位置取决于板卡硬件和PX4板级配置。
11. NSH控制台
当前看到的:
NuttShell (NSH)
nsh>
是NuttX Shell。其作用类似简化的Unix Shell。
NuttX/NSH提供的典型命令:
ls
cd
cat
cp
rm
mount
df
ps
kill
free
ifconfig
PX4又把自己的内置模块命令注册到NSH:
gps status
ekf2 status
mavlink status
logger status
commander status
listener sensor_gps
param show GPS_1_CONFIG
执行 gps status 时:
NSH解析命令
│
▼
查找名为gps的PX4内置程序入口
│
▼
调用gps模块的命令处理函数
│
▼
gps模块输出当前状态
因此NSH是操作系统提供的Shell环境,gps、ekf2、mavlink等则是PX4注册进去的程序。
12. 网络功能
当前NuttX配置启用了网络栈,包括:
- Ethernet驱动支持。
- IPv4。
- TCP。
- UDP。
- ICMP。
- ARP。
- DHCP客户端。
- DNS客户端。
- Telnet服务相关能力。
当前CUAV 7-Nano板级默认还配置了一个Ethernet MAVLink实例。
常用检查命令:
ifconfig
netstat
ping <IP地址>
具体命令是否存在取决于当前minimal固件是否编译对应系统命令。
网络栈由NuttX提供,MAVLink UDP、远程NSH或其他PX4网络服务建立在该网络栈之上。
13. 系统启动流程
当前初始化入口为:
CONFIG_INIT_ENTRYPOINT="nsh_main"
完整启动过程可以简化为:
飞控上电或复位
│
▼
PX4 Bootloader运行
│
├─ 有升级请求:停留在Bootloader
│
└─ 应用有效:跳转到0x08020000
│
▼
STM32与NuttX底层初始化
│
▼
创建Idle任务和初始化任务
│
▼
nsh_main
│
▼
board_app_initialize()
│
▼
px4_platform_init()
│
▼
挂载ROMFS、初始化PX4平台
│
▼
执行/etc/init.d/rcS
│
▼
读取参数和选择机型脚本
│
▼
启动驱动、uORB、EKF2、控制器、MAVLink、Logger
板级初始化入口:
boards/cuav/7-nano/src/init.c
PX4平台初始化入口:
platforms/nuttx/src/px4/common/px4_init.cpp
NuttX负责启动操作系统和NSH;rcS负责按照板卡、参数和机型组织PX4模块启动。
14. 中断、驱动与控制链路
以IMU驱动控制回路为例:
IMU硬件产生数据就绪中断
│
▼
STM32中断处理和DMA/SPI传输
│
▼
PX4 IMU驱动取得传感器数据
│
▼
发布sensor_gyro/sensor_accel
│
▼
传感器处理和EKF2更新
│
▼
姿态/角速度控制器运行
│
▼
control_allocator计算执行器输出
│
▼
PWM或DShot驱动更新电机
中断处理程序通常只完成必要的快速操作,然后唤醒线程或Work Queue继续处理,避免在中断上下文中执行耗时算法。
15. 故障检测和HardFault
当前板卡配置启用了:
CONFIG_BOARD_CRASHDUMP=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_DEBUG_HARDFAULT_ALERT=y
CONFIG_DEBUG_MEMFAULT=y
CONFIG_BOARD_RESET_ON_ASSERT=2
系统可以检测或记录:
- 非法内存访问。
- 总线错误。
- UsageFault。
- 栈溢出。
- 断言失败。
- 看门狗复位。
- 部分任务调度和栈使用异常。
发生严重错误时,因为当前是Flat共享地址空间,系统通常无法只终止一个模块并保证其他模块继续安全飞行,可能记录Crash Dump后复位整个飞控。
可检查:
dmesg
hardfault_log check
以及SD卡根目录中的:
fault_*.log
如果 .ulg 日志在空中突然终止,也应同时检查HardFault记录和供电问题。
16. NuttX与Linux的区别
| 项目 | 当前NuttX系统 | Linux系统 |
|---|---|---|
| 主要目标 | 微控制器实时控制 | 通用计算和多用户应用 |
| CPU平台 | STM32 Cortex-M7 | 通常Cortex-A、x86等带MMU的处理器 |
| 地址空间 | 当前为Flat共享地址空间 | 用户进程通常有虚拟地址隔离 |
| 调度重点 | 固定优先级和实时响应 | 公平性、吞吐量及多种调度策略 |
| 程序形式 | 大部分模块编入一个固件 | 独立可执行文件和动态库 |
| Shell | NSH | Bash、Zsh等 |
| 模块通信 | PX4主要使用uORB | Socket、Pipe、共享内存等 |
| 存储 | MCU Flash、FRAM和SD卡 | 磁盘或大容量Flash文件系统 |
| 故障隔离 | Flat模式下较弱 | 进程隔离较强 |
| 启动时间 | 很短 | 通常更长 |
| 内存占用 | 很小 | 通常较大 |
因此不能直接用Linux进程的理解套用PX4模块。例如 ekf2 start 启动的是固件内部编译好的PX4模块任务,而不是从磁盘加载一个独立ELF可执行文件。
17. 常用系统诊断命令
17.1 CPU、任务和内存
top
ps
free
work_queue status
17.2 启动和系统消息
dmesg
ver all
shutdown status
17.3 文件系统和SD卡
mount
df
ls /fs/microsd
logger status
sd_bench -r 100
17.4 设备和通信
gps status
mavlink status
uavcan status
ifconfig
17.5 PX4模块与uORB
commander status
ekf2 status
listener sensor_gps -n 3
listener vehicle_attitude -n 3
listener estimator_status_flags -n 1
某些命令可能在minimal固件中被裁剪,应以控制台执行 help 后显示的命令为准。
18. 阅读源码的建议顺序
如果要从操作系统角度学习当前PX4,可以按以下顺序阅读:
- 板级NuttX配置:
boards/cuav/7-nano/nuttx-config/nsh/defconfig
- CUAV板级初始化:
boards/cuav/7-nano/src/init.c
boards/cuav/7-nano/src/board_config.h
- PX4的NuttX平台初始化:
platforms/nuttx/src/px4/common/px4_init.cpp
- PX4启动脚本:
ROMFS/px4fmu_common/init.d/rcS
- PX4任务和工作队列:
platforms/common/px4_work_queue/
platforms/common/include/px4_platform_common/px4_work_queue/
- uORB实现:
platforms/common/uORB/
src/modules/uORB/
- 一个具体驱动和一个控制模块:
src/drivers/gps/
src/modules/mc_rate_control/
这样可以沿着“操作系统初始化、PX4平台初始化、模块启动、消息通信、实际功能模块”的路径建立完整理解。
19. 核心结论
NuttX负责:
让代码按优先级、按时运行,并管理硬件、内存和文件系统
PX4负责:
采集传感器、估计飞机状态、计算控制量并管理飞行任务
uORB负责:
让PX4各模块通过发布/订阅方式交换数据
Work Queue负责:
让多个PX4模块高效共享实时线程和调度优先级
rcS负责:
根据板卡、参数和机型启动所需的PX4模块
当前CUAV 7-Nano采用NuttX Flat Build以换取较低开销和更高实时效率,但模块之间没有Linux进程级内存隔离。因此编写和修改PX4底层代码时,需要特别重视指针安全、栈使用、执行时间和线程同步。