2. ArduPilot系统架构概览
理解 ArduPilot 的外设驱动与设备接入,不能只盯着某一个驱动文件,而要先看清它在整个飞控软件中的位置。ArduPilot 采用分层架构:上层是飞行控制栈(飞行模式、姿态控制、导航、任务逻辑),中间是硬件抽象层(AP_HAL),下层是具体硬件平台(ChibiOS、Linux、ESP32 等)以及挂载其上的传感器驱动和外设总线。外设接入的本质,就是让一个物理设备经过总线、驱动、抽象层,最终以统一的数据接口暴露给飞行控制栈。
一、五层结构:从飞行逻辑到物理引脚
ArduPilot 的整体关系可以概括为五个层次,自顶向下依次是:
- 飞行控制栈(Vehicle/Flight Stack):包含 ArduCopter、ArduPlane、ArduRover 等机型代码,负责姿态控制、位置控制、飞行模式切换、任务执行。它通过 scheduler 周期调用,读取传感器数据并输出电机/舵机控制量。
- 传感器与估计层(Sensors / EKF):AP_InertialSensor、AP_Baro、AP_Compass、AP_GPS、AP_RangeFinder 等库,把原始数据融合成姿态、位置、速度估计,供控制栈使用。
- 硬件抽象层(AP_HAL):定义 I2C、SPI、UART、GPIO、RCInput、RCOutput、Storage、Scheduler 等统一接口,屏蔽底层 RTOS 与芯片差异。
- 平台实现层(HAL 实现):如 AP_HAL_ChibiOS、AP_HAL_Linux、AP_HAL_ESP32,把 AP_HAL 接口映射到具体外设控制器、DMA、中断和引脚。
- 物理设备与总线:IMU、磁罗盘、气压计、GPS、光流、测距仪、电调、接收机等,通过 I2C、SPI、UART、CAN、PWM 等总线连接。
这五层不是简单的“上下级”,而是“接口契约 + 实现替换”的关系。飞行控制栈调用 AP_HAL 接口,AP_HAL 调用平台实现,平台实现操作总线,总线连接设备。任何一层替换,只要接口不变,上层代码无需修改。
二、外设接入的完整数据流
以最常见的 IMU 为例,一次数据采集要经过如下路径:
- IMU 芯片通过 SPI 或 I2C 总线连接飞控主控;
- 平台实现层(如 AP_HAL_ChibiOS)配置 SPI 时钟、DMA 通道和中断;
- AP_InertialSensor 驱动通过 AP_HAL::SPIDevice 发起寄存器读取;
- 原始加速度、角速度数据经过校准、滤波、温度补偿;
- 数据进入 EKF 融合,输出姿态估计;
- 飞行控制栈读取姿态估计,计算控制量并输出到电机。
这条链路中,任何一环出问题都会表现为“传感器不健康”或“姿态漂移”。排障时要沿着链路逐段确认:设备是否被探测到、总线是否通信正常、驱动是否注册成功、数据是否进入 EKF。
三、关键接口与参数对照
下表列出外设接入中最常打交道的接口与对应参数,便于快速定位配置位置:
| 层次 | 典型接口/对象 | 关键参数示例 | 作用 |
|---|---|---|---|
| 飞行控制栈 | AP_Vehicle、模式切换 | FLTMODE_CH、ARMING_CHECK | 决定何时解锁、使用哪些传感器 |
| 传感器库 | AP_InertialSensor | INS_USE、INS_ACCEL_FILTER | 选择 IMU 实例、滤波与校准 |
| 硬件抽象层 | AP_HAL::I2CDevice | HAL_I2C_DEVICE | 统一 I2C 读写接口 |
| 平台实现 | SPIDevice、UARTDriver | SERIALx_PROTOCOL、SERIALx_BAUD | 串口协议与波特率映射 |
| 物理总线 | I2C/SPI/UART/CAN | CAN_P1_DRIVER、BRD_CAN_ENABLE | 总线使能与设备发现 |
四、典型场景:接入一个 I2C 测距仪
假设要接入一个 I2C 接口的激光测距仪,接入流程如下:
1. 确认总线:I2C 地址 0x29,挂载在 I2C1
2. 在 hwdef.dat 中声明设备:
I2C_ORDER I2C1
define HAL_RANGEFINDER_ENABLE 1
3. 驱动注册:AP_RangeFinder 探测到设备并创建实例
4. 参数配置:
RNGFND1_TYPE = 20 # 对应驱动类型
RNGFND1_ADDR = 0x29
RNGFND1_MAX_CM = 4000
RNGFND1_MIN_CM = 20
5. 验证:地面站查看 rangefinder 距离是否稳定
这个过程中,hwdef.dat 属于平台实现层,RNGFND1_* 属于传感器库参数,而实际读写发生在 AP_HAL 的 I2C 接口上。理解这个映射关系,就能明白为什么有些配置要改硬件定义、有些只需改参数。
五、常见误区与复盘
- 误区一:把驱动当成独立程序。ArduPilot 的驱动是编译进固件的库,通过探测和注册机制接入,不是运行时加载的插件。
- 误区二:忽略平台实现层。同一个驱动在不同 HAL 平台上可能行为不同,因为底层 SPI/I2C 时序、DMA 和中断实现有差异。
- 误区三:只改参数不改硬件定义。如果总线未在 hwdef 中使能,参数再正确也无法通信。
- 误区四:不区分“探测失败”和“数据异常”。探测失败通常是总线或地址问题;数据异常通常是校准、滤波或供电问题。
复盘时建议按“总线 → 平台实现 → AP_HAL → 驱动 → 传感器库 → 控制栈”的顺序逐层验证,而不是一上来就改控制参数。分层架构的价值就在于:每一层都有明确的输入输出,问题可以被隔离在某一层内定位。
掌握这套分层关系后,后续学习具体驱动时就能快速定位代码位置:飞行控制栈在 ArduCopter/,传感器库在 libraries/AP_*,HAL 接口在 libraries/AP_HAL/,平台实现在 libraries/AP_HAL_ChibiOS/,硬件定义在 hwdef/。这条路径是理解 ArduPilot 外设生态的主线。