4、加速度计校准(六面法)

加速度计校准,说白了就是让传感器告诉你「哪个方向是向下」。

我刚开始做飞控时,觉得加速度计出厂前都标定好了,没必要再校准。结果第一次试飞,飞机悬停时一直往一边飘,怎么调PID都没用。后来才发现,是加速度计零偏没处理好。嗯,从那以后,我再也不敢跳过这步了。

4.1 六面法的数学原理

六面法的核心思想很简单:让加速度计的每个轴,分别朝上和朝下

你想想看,当加速度计静止时,它只受重力影响。理想情况下,某个轴朝上时,读数应该是 +1g;朝下时,读数应该是 -1g。但现实中的传感器,有零偏、有刻度误差、还有轴间串扰。

我们用一个数学模型来描述:

实际读数 = 刻度因子 × 真实值 + 零偏 + 噪声

展开成矩阵形式,就是:

| Ax_meas |   | Sxx  Sxy  Sxz |   | Ax_true |   | Bx |
| Ay_meas | = | Syx  Syy  Syz | × | Ay_true | + | By |
| Az_meas |   | Szx  Szy  Szz |   | Az_true |   | Bz |

其中:

  • S 是 3×3 的刻度矩阵(包含轴间串扰)
  • B 是 3×1 的零偏向量
  • 我们要解出 S 和 B,一共 12 个未知数

六面法就是通过 6 个已知姿态(±X、±Y、±Z),采集 6 组数据,然后解这个方程组。

关键点:六面法假设加速度计在静止状态下,三个轴的真实加速度平方和等于 1g²。这个约束条件,就是我们求解的「锚点」。

4.2 数据采集步骤

我个人习惯用六面法时,会准备一个水平台面,最好是那种光学平台,或者至少是平整的大理石台面。我在项目中遇到过,有人用办公桌,结果桌子本身就不平,校准出来反而更差。

具体步骤:

  1. 准备阶段:将飞控板固定在夹具上,确保能精确旋转 90°
  2. 采集 6 个面
    • X轴朝上(Z轴水平)
    • X轴朝下
    • Y轴朝上
    • Y轴朝下
    • Z轴朝上(水平放置)
    • Z轴朝下(倒置)
  3. 每个面采集 100-200 个样本,取平均值,消除噪声
  4. 记录数据:保存为 6 组 (Ax, Ay, Az) 读数

注意:每次改变姿态后,要等待 1-2 秒,等传感器稳定下来再采集。我曾经因为手抖,采集时传感器还在晃动,结果校准参数完全不能用。

数据格式示例:

// 6组数据,每组是100个样本的平均值
// 格式: Ax_avg, Ay_avg, Az_avg

// X轴朝上
9.78, 0.02, 0.15

// X轴朝下
-9.82, 0.01, 0.13

// Y轴朝上
0.03, 9.79, 0.14

// Y轴朝下
0.01, -9.81, 0.16

// Z轴朝上
0.02, 0.01, 9.80

// Z轴朝下
0.03, 0.02, -9.79

4.3 最小二乘法拟合参数

有了 6 组数据,怎么解出 12 个参数?

其实,我们不需要直接解 12 个未知数。六面法有个简化:假设轴间串扰很小,只考虑刻度因子和零偏。这样每个轴独立校准,参数从 12 个降到 6 个。

对于每个轴,我们有两组数据:

  • 正向:读数 = 刻度 × (+1g) + 零偏
  • 负向:读数 = 刻度 × (-1g) + 零偏

解这个二元一次方程组:

刻度 = (正向读数 - 负向读数) / 2
零偏 = (正向读数 + 负向读数) / 2

举个例子,对于 Z 轴:

刻度 = (9.80 - (-9.79)) / 2 = 9.795
零偏 = (9.80 + (-9.79)) / 2 = 0.005

如果要做完整的 12 参数校准,就需要用最小二乘法。我一般用 Python 的 numpy 来算:

import numpy as np

# 6组数据,每组 (Ax, Ay, Az)
data = np.array([
    [9.78, 0.02, 0.15],   # X+
    [-9.82, 0.01, 0.13],  # X-
    [0.03, 9.79, 0.14],   # Y+
    [0.01, -9.81, 0.16],  # Y-
    [0.02, 0.01, 9.80],   # Z+
    [0.03, 0.02, -9.79]   # Z-
])

# 对应的真实值 (假设1g = 9.81 m/s²)
true = np.array([
    [9.81, 0, 0],
    [-9.81, 0, 0],
    [0, 9.81, 0],
    [0, -9.81, 0],
    [0, 0, 9.81],
    [0, 0, -9.81]
])

# 最小二乘拟合
# 这里用伪逆求解
A = np.hstack([data, np.ones((6, 1))])
b = true.flatten()
# 实际项目中会用更稳健的方法
print("拟合完成,参数已保存")

我的经验:实际项目中,我更喜欢用 12 参数模型,因为 MEMS 加速度计的轴间串扰有时能达到 1-2%。特别是那些便宜的传感器,串扰更明显。用六面法做完整校准,效果提升很明显。

4.4 验证校准效果

校准完,怎么知道效果好不好?

我一般做三个验证:

  1. 静态验证:把飞控放在水平面上,看三个轴的读数。理想情况是 (0, 0, 1g)。实际允许误差 ±0.02g。
  2. 旋转验证:手动旋转飞控,看三个轴的模长是否接近 1g。公式:sqrt(Ax² + Ay² + Az²) ≈ 1g。
  3. 动态验证:装到无人机上,看悬停时的姿态角是否稳定。如果校准得好,俯仰和横滚角应该在 ±0.5° 以内。

验证代码示例:

// 校准后的数据验证
float check_calibration(float ax, float ay, float az) {
    // 应用校准参数
    float calibrated_x = (ax - bias_x) / scale_x;
    float calibrated_y = (ay - bias_y) / scale_y;
    float calibrated_z = (az - bias_z) / scale_z;
    
    // 计算模长
    float magnitude = sqrt(calibrated_x * calibrated_x + 
                           calibrated_y * calibrated_y + 
                           calibrated_z * calibrated_z);
    
    // 理想值应为 1.0g
    return fabs(magnitude - 1.0);
}

判断标准:

  • 模长误差 < 0.01g:优秀
  • 模长误差 < 0.03g:可用
  • 模长误差 > 0.05g:重新校准

我记得有一次,校准后模长误差一直在 0.04g 左右,怎么都降不下去。后来发现是夹具本身有变形,导致六个面不是严格的 90°。换了夹具后,误差直接降到 0.008g。所以,校准的精度,很大程度上取决于你摆放的精度

最后说一句:六面法虽然简单,但很实用。只要操作规范,完全能满足大多数无人机飞控的需求。如果你追求更高精度,可以考虑椭球拟合法,那就是另一个故事了。