4. 地面站基础架构

参数系统是 ArduPilot 的“神经中枢”。你调的所有参数,最终都要存下来、读出来、传出去。这一章我带你拆开看看,里面到底怎么运作的。

核心要点:参数系统 = 存储介质 + 命名规范 + 分组结构 + 元数据约束。四者缺一不可。

4.1 参数存储机制:FRAM / EEPROM / SD 卡

ArduPilot 支持三种存储介质。优先级从高到低:FRAM > EEPROM > SD 卡。说白了,板子上有 FRAM 就用 FRAM,没有就用 EEPROM,实在不行才写 SD 卡。

存储介质 写入寿命 速度 典型硬件
FRAM 10¹⁴ 次(几乎无限) 纳秒级 Pixhawk 系列(FMUv2+)
EEPROM 10⁵ 次 毫秒级 早期 APM 板、部分 F4 飞控
SD 卡(文件) 取决于卡质量 较慢 无 FRAM/EEPROM 的备用方案

我个人习惯,拿到一块新飞控,第一件事就是看 STORAGE_TYPE 参数。它决定了参数存哪。默认是 0(自动检测),但有时候自动检测会翻车。

避坑:我曾经遇到过一块“山寨”Pixhawk,FRAM 芯片是假的。参数写进去读出来全是 0xFF。飞控能启动,但一断电参数全丢。后来我强制 STORAGE_TYPE=2 改用 SD 卡存储,才稳住。

存储流程其实很简单:

  1. 参数变更时,标记“脏位”(dirty flag)。
  2. 主循环每 1.5 秒检查一次脏位。
  3. 有脏位就批量写入存储介质。
  4. 写入完成后清除脏位。

为什么要批量?因为每次写入都有开销。攒一批再写,效率高得多。你想想看,如果每次调一个参数就写一次,EEPROM 很快就挂了。

4.2 参数 ID 命名规范

ArduPilot 的参数名不是随便起的。它有一套严格的命名规则。看懂命名,你就看懂了一半的参数系统。

标准格式:模块前缀_参数组_参数名

举个例子:ATC_ANG_RLL_P

  • ATC — 模块前缀,这里是 Attitude Controller(姿态控制器)
  • ANG — 参数组,Angle(角度环)
  • RLL — 具体对象,Roll(横滚)
  • P — 参数名,P 增益

再拆几个常见的:

参数名 模块 组 含义
INS_ACC2_OFFS_X INS ACC2 第二个加速度计 X 轴偏移
MOT_PWM_MIN MOT PWM 电机最小 PWM 值
GPS_TYPE GPS (无) GPS 模块类型
BATT2_MONITOR BATT 2 第二块电池的监控方式
小技巧:命名中的数字后缀(如 ACC2、BATT2)通常表示“第几个实例”。ArduPilot 支持多传感器冗余,命名规则帮你一眼区分。

嗯,这里要注意:参数名最长 16 个字符。超过会被截断。我见过有人写 ATC_ANG_RLL_FLTE(滤波器时间常数),刚好 16 个字符,卡在边界上。设计参数名时,心里要绷着这根弦。

4.3 参数分组:Group 与 Param

ArduPilot 内部用 Group 和 Param 两层结构管理参数。Group 是容器,Param 是具体参数。

看一段伪代码你就明白了:

class Parameters {
    struct {
        AP_Int8  type;       // GPS_TYPE
        AP_Int16 baudrate;   // GPS_BAUDRATE
        AP_Int8  auto_config;// GPS_AUTO_CONFIG
    } gps;  // 这是一个 Group

    struct {
        AP_Float p;          // ATC_ANG_RLL_P
        AP_Float i;          // ATC_ANG_RLL_I
        AP_Float d;          // ATC_ANG_RLL_D
    } atc_ang_rll;  // 这是另一个 Group
};

每个 Group 对应一个参数前缀。比如 gps 组对应 GPS_ 开头的参数。atc_ang_rll 组对应 ATC_ANG_RLL_ 开头的参数。

为什么要分组?两个原因:

  1. 代码组织 — 相关参数放在一起,维护起来清爽。
  2. 批量操作 — 可以一次性读写整个 Group,效率高。

我记得有一次调试四轴,发现 ATC_ANG_RLL_P 和 ATC_ANG_PIT_P 总是要成对调。后来我干脆写了个脚本,同时读写整个 ATC_ANG_ 组。省事多了。

4.4 参数元数据:min / max / unit

每个参数都带着“身份证”。元数据就是这张身份证。它告诉地面站和飞控:这个参数能设多大、能设多小、单位是什么。

元数据包含以下字段:

字段 含义 示例
min 最小值 0.0
max 最大值 90.0
unit 单位 deg/s, m, A, Hz
increment 步进值 0.1
default 默认值 4.5

元数据在代码里是这样定义的:

// @Param: P
// @DisplayName: Roll angle P gain
// @Description: P gain for roll angle control
// @Range: 0.0 90.0
// @Increment: 0.1
// @User: Standard
AP_Float p;

注意看 @Range: 0.0 90.0。这就是元数据。Mission Planner 读到这个注释,就知道滑块范围是 0 到 90,步进 0.1。

重要:元数据只是“建议”,不是“强制”。你完全可以通过 MAVLink 直接写一个超出范围的值。但飞控内部会做二次校验。我曾经试过把 ATC_ANG_RLL_P 写到 200,结果飞控直接拒绝写入,返回 PARAM_VALUE 的 param_result 为 PARAM_RESULT_INVALID。

单位字段特别有用。比如 ATC_ANG_RLL_P 的单位是 deg/s,你一看就知道这个增益是把角度误差换算成角速度。理解单位,就是理解物理意义。

4.5 参数系统整体架构图

下面这张图把参数系统的全貌串起来了。从地面站到存储介质,整个链路一目了然。

ArduPilot 参数系统架构图 地面站 (Mission Planner) MAVLink 参数协议 参数管理器 (AP_Param) 脏位标记 / 批量写入 / 元数据校验 参数分组 (Group) GPS 组 ATC_ANG_RLL 组 INS_ACC 组 ... 参数实例 (Param) AP_Float p AP_Int8 type AP_Int16 baudrate ... 参数元数据 min: 0.0 max: 90.0 unit: deg/s default: 4.5 存储层 FRAM (优先) | EEPROM (备选) | SD 卡文件 (兜底) 💡 数据流:地面站 → MAVLink 参数协议 → 参数管理器 → 分组/实例/元数据 → 存储介质 写入时先校验元数据,再标记脏位,最后批量写入存储介质。

从这张图你能看到,参数系统不是简单的“存-读”两件事。它包含了协议解析、分组管理、元数据校验、存储调度等多个环节。每个环节都可能成为瓶颈或坑点。

实战建议:如果你要自己写一个参数调优工具,一定要先理解这个架构。直接从 MAVLink 层读写参数是最简单的,但如果你想做批量调优、参数对比、历史记录,就必须深入到 Group 和元数据层面。

好了,参数系统的骨架就这些。存储介质选型、命名规范、分组结构、元数据约束,这四个东西构成了 ArduPilot 参数系统的基石。理解它们,你调参的时候就不会“盲调”。