2、加速度计校准:六面静态校准法、椭球拟合校准原理、温度补偿策略

好,咱们接着聊加速度计的校准。说实话,加速度计是飞控里最基础的传感器之一,但也是最容易被忽视的。很多人觉得加速度计嘛,不就是测个重力方向?其实没那么简单。我见过不少飞控炸机,最后查出来是加速度计没校准好,姿态解算从一开始就是歪的。

今天咱们重点讲三种校准方法:六面静态校准、椭球拟合校准,还有温度补偿。这三种方法,说白了就是解决不同层次的问题。六面法是基础,椭球拟合是进阶,温度补偿是实战中不得不面对的坑。

2.1 六面静态校准法

六面静态校准,这是最经典、最直观的方法。原理很简单:把加速度计分别朝向六个方向,记录每个方向上的输出值。

哪六个方向?就是+X、-X、+Y、-Y、+Z、-Z。每个方向都让重力完全作用在那个轴上。比如Z轴朝上时,理论上输出应该是+1g;Z轴朝下时,输出是-1g。

嗯,这里有个关键点:静态。什么意思?就是校准过程中传感器不能动,不能有振动。我刚开始做飞控时,有次偷懒,用手拿着传感器翻来翻去,结果校准出来的参数一塌糊涂。后来才明白,哪怕手轻微的抖动,都会引入噪声。

六面校准要解决的核心问题是:零偏误差比例因子误差

  • 零偏误差:当加速度为0时,传感器输出不为0。比如静止在桌面上,理论上三个轴输出应该是(0, 0, 1g),但实际可能是(0.02, -0.01, 0.98g)。
  • 比例因子误差:每个轴的灵敏度不完全一致。比如X轴1g对应1000 LSB,Y轴1g对应1020 LSB,这就导致同样的重力加速度,不同轴输出不一样。

校准模型通常是这样:

a_calibrated = (a_raw - bias) * scale_factor

实际操作时,我习惯把传感器固定在水平台上,用水平仪确认每个面都放平了。然后采集每个方向的数据,取平均值。六个方向的数据采集完后,解一个线性方程组就能算出bias和scale_factor。

这里给个简单的代码示例,展示如何计算:

// 六面校准数据示例(单位:g)
// 每个方向采集100个点取平均
float accel_data[6][3] = {
    {0.98, 0.01, 0.02},   // +X
    {-1.02, -0.01, 0.01}, // -X
    {0.01, 0.99, -0.01},  // +Y
    {-0.01, -1.01, 0.02}, // -Y
    {0.01, -0.01, 0.98},  // +Z
    {-0.02, 0.01, -1.02}  // -Z
};

// 计算零偏
float bias[3];
for (int i = 0; i < 3; i++) {
    bias[i] = (accel_data[2*i][i] + accel_data[2*i+1][i]) / 2.0f;
}

// 计算比例因子
float scale[3];
for (int i = 0; i < 3; i++) {
    scale[i] = 2.0f / (accel_data[2*i][i] - accel_data[2*i+1][i]);
}
我的小技巧:采集数据时,每个方向至少保持2秒静止。我一般会采集5秒,然后取中间3秒的数据做平均,避免开始和结束时的手动干扰。

2.2 椭球拟合校准原理

六面校准虽然好用,但它有个前提:传感器安装得足够正,而且每个轴完全独立。现实中呢?传感器芯片焊在PCB上,PCB装在机架上,机架本身可能还有角度。这就引入了轴间耦合误差

说白了,就是X轴受到Y轴和Z轴的影响。比如你只给X轴施加加速度,Y轴和Z轴也会有点输出。这就是所谓的交叉轴灵敏度

椭球拟合校准,就是解决这个问题的。它的原理基于一个物理事实:在静止状态下,加速度计的三轴输出模长应该等于1g(假设已经去除了重力方向)。

你想想看,如果传感器是理想的,所有静止状态下的数据点应该落在一个球面上,球心在原点,半径是1g。但因为有零偏、比例因子误差和轴间耦合,实际数据点落在一个椭球上。

校准的目标就是:把这个椭球拉回成一个标准的球

椭球拟合的数学模型是:

(x - bx)² / sx² + (y - by)² / sy² + (z - bz)² / sz² + 
2 * cxy * (x - bx) * (y - by) + 2 * cxz * (x - bx) * (z - bz) + 2 * cyz * (y - by) * (z - bz) = 1

其中bx, by, bz是零偏,sx, sy, sz是比例因子,cxy, cxz, cyz是交叉耦合系数。一共9个参数需要求解。

怎么求解?最小二乘法。采集大量不同姿态下的数据点,然后拟合出椭球参数。我一般会采集至少50个不同的姿态,覆盖所有方向。

重要提醒:椭球拟合要求数据点均匀分布在三维空间里。不要只采集几个固定方向,那样拟合出来的椭球会严重失真。我见过有人只采集了20个点,结果校准后反而更差了。

这里给个Python伪代码,展示椭球拟合的核心思路:

import numpy as np

def ellipsoid_fit(data):
    """
    data: Nx3 矩阵,每行是一个姿态下的加速度计输出
    返回:bias (3x1), scale (3x3)
    """
    # 构建设计矩阵
    x, y, z = data[:, 0], data[:, 1], data[:, 2]
    D = np.column_stack([
        x*x, y*y, z*z,
        2*x*y, 2*x*z, 2*y*z,
        2*x, 2*y, 2*z,
        np.ones_like(x)
    ])
    
    # 最小二乘求解
    v = np.linalg.lstsq(D, np.ones_like(x), rcond=None)[0]
    
    # 从v中提取椭球参数
    # ... (具体推导略,网上有现成代码)
    
    return bias, scale_matrix
注意:椭球拟合对数据质量要求很高。如果传感器有振动或温度变化,拟合结果会不稳定。我建议在恒温环境下做,或者采集数据时确保传感器完全静止。

2.3 温度补偿策略

好,六面校准和椭球拟合都讲完了。但如果你以为这样就万事大吉,那就太天真了。实际飞行中,温度变化才是最大的敌人。

我记得有一次做无人机测试,早上校准好的参数,到了中午太阳一晒,加速度计输出直接漂了0.1g。姿态解算瞬间就乱了,飞机开始剧烈晃动。那次之后,我彻底明白了温度补偿的重要性。

加速度计的温度特性主要有两种:

  • 零偏温漂:温度变化时,零偏会跟着变。典型值在0.1~1 mg/°C之间。
  • 比例因子温漂:温度变化时,灵敏度也会变。这个影响相对小一些,但高精度应用不能忽略。

温度补偿的策略,我总结为三个层次:

  1. 查表法:在实验室里测出不同温度下的零偏和比例因子,做成一个查找表。飞行时根据当前温度查表补偿。这是最直接的方法,但需要提前做大量标定实验。
  2. 多项式拟合:用多项式拟合温度曲线。比如零偏随温度的变化可以用二次多项式表示:bias(T) = a0 + a1*T + a2*T²。这种方法比查表法更平滑,但需要确定多项式阶数。
  3. 在线估计:在飞行过程中,利用其他传感器(比如GPS、视觉)的信息,实时估计温度漂移。这是最先进的方法,但实现复杂,一般用于高端飞控。

我个人最常用的是查表法+线性插值。具体做法是:

// 温度补偿查表(示例数据)
typedef struct {
    float temp;   // 温度,单位°C
    float bias_x; // X轴零偏,单位g
    float bias_y;
    float bias_z;
} TempCompEntry;

TempCompEntry comp_table[] = {
    {-20, 0.02, -0.01, 0.03},
    {0,   0.01,  0.00, 0.02},
    {25,  0.00,  0.01, 0.01},
    {50, -0.01,  0.02, 0.00},
    {70, -0.02,  0.03, -0.01}
};

// 线性插值函数
float interpolate(float temp, TempCompEntry *table, int len, int axis) {
    // 找到温度区间
    for (int i = 0; i < len - 1; i++) {
        if (temp >= table[i].temp && temp <= table[i+1].temp) {
            float ratio = (temp - table[i].temp) / (table[i+1].temp - table[i].temp);
            float b0, b1;
            if (axis == 0) { b0 = table[i].bias_x; b1 = table[i+1].bias_x; }
            else if (axis == 1) { b0 = table[i].bias_y; b1 = table[i+1].bias_y; }
            else { b0 = table[i].bias_z; b1 = table[i+1].bias_z; }
            return b0 + ratio * (b1 - b0);
        }
    }
    // 超出范围则取边界值
    if (temp < table[0].temp) return table[0].bias_x;
    return table[len-1].bias_x;
}
避坑指南:我曾经犯过一个错误,只做了零偏的温度补偿,忽略了比例因子。结果在极端温度下,加速度计的输出幅值不对,导致姿态解算的增益出错。所以,如果要求高精度,比例因子的温度补偿也不能省。

最后说一句,温度补偿不是一劳永逸的。传感器会老化,温度特性也会变化。我建议每隔半年重新做一次温度标定,或者至少做一次六面校准来验证参数是否还准确。

好了,加速度计校准这部分就讲到这里。下一章咱们聊陀螺仪的校准,那个又是另一番天地了。