4、加速度计校准实践:数据采集流程设计、Python实现最小二乘参数估计、校准结果验证方法、温漂补偿策略

好,咱们直接进入正题。加速度计校准这件事,说白了就是让传感器说人话。你从芯片读出来的原始数据,跟真实的物理加速度之间,隔着一层误差。这层误差怎么去掉?就是今天要聊的。

4.1 数据采集流程设计

数据采集是校准的第一步,也是最容易翻车的一步。我见过不少工程师,算法写得飞起,结果数据采得稀烂,最后校准出来还不如不校。

核心原则:让加速度计在已知姿态下静止,记录数据。

具体怎么做?我个人习惯用六位置法。就是把传感器分别朝上、朝下、朝左、朝右、朝前、朝后,每个位置静止采集个几十秒。

六位置法采集步骤:

  1. 将IMU水平放置,Z轴朝上(感受重力+g),采集30秒
  2. 翻转180度,Z轴朝下(感受重力-g),采集30秒
  3. 将IMU侧立,X轴朝上,采集30秒
  4. 翻转180度,X轴朝下,采集30秒
  5. 将IMU侧立,Y轴朝上,采集30秒
  6. 翻转180度,Y轴朝下,采集30秒

这里有个坑,我曾经吃过亏——采集时一定要保证IMU完全静止。你手抖一下,数据就废了。建议用夹具固定,或者至少把IMU放在水平桌面上。

小技巧:每个位置采集前,先等个2-3秒,让传感器稳定下来。别一放上去就开始采,那会儿数据还在震荡。

4.2 Python实现最小二乘参数估计

数据采完了,接下来就是数学活了。加速度计的误差模型,说白了就是一个线性模型:

a_measured = S * a_true + b + noise

其中S是比例因子矩阵,b是零偏。我们想求的就是S和b。

最小二乘法怎么用?你想想看,我们已知每个位置的理论加速度(比如Z轴朝上时,理论值是[0, 0, g]),又测到了实际值。这不就是解一个超定方程组吗?

直接上代码:

import numpy as np

def calibrate_accel(measured_data, reference_data):
    """
    加速度计最小二乘校准
    measured_data: 测量值,形状 (n, 3)
    reference_data: 参考值,形状 (n, 3)
    """
    n = measured_data.shape[0]
    
    # 构造矩阵 A 和向量 b
    A = np.zeros((3*n, 7))
    b = np.zeros(3*n)
    
    for i in range(n):
        mx, my, mz = measured_data[i]
        rx, ry, rz = reference_data[i]
        
        # 第 i 个测量点的 X 轴方程
        A[3*i, 0] = mx
        A[3*i, 1] = my
        A[3*i, 2] = mz
        A[3*i, 3] = 1
        b[3*i] = rx
        
        # Y 轴方程
        A[3*i+1, 0] = 0
        A[3*i+1, 1] = mx
        A[3*i+1, 2] = 0
        A[3*i+1, 3] = 0
        A[3*i+1, 4] = my
        A[3*i+1, 5] = mz
        A[3*i+1, 6] = 1
        b[3*i+1] = ry
        
        # Z 轴方程
        A[3*i+2, 0] = 0
        A[3*i+2, 1] = 0
        A[3*i+2, 2] = mx
        A[3*i+2, 3] = 0
        A[3*i+2, 4] = 0
        A[3*i+2, 5] = my
        A[3*i+2, 6] = 1
        b[3*i+2] = rz
    
    # 最小二乘求解
    x, residuals, rank, s = np.linalg.lstsq(A, b, rcond=None)
    
    # 提取参数
    Sxx, Sxy, Sxz, bx = x[0], x[1], x[2], x[3]
    Syx, Syy, Syz, by = 0, x[4], 0, x[5]
    Szx, Szy, Szz, bz = 0, 0, x[6], x[7]
    
    S = np.array([[Sxx, Sxy, Sxz],
                  [Syx, Syy, Syz],
                  [Szx, Szy, Szz]])
    b = np.array([bx, by, bz])
    
    return S, b

# 使用示例
# S, b = calibrate_accel(measured, reference)
# 校准后的数据:a_calibrated = np.linalg.inv(S) @ (a_raw - b)

嗯,这里要注意一点。我刚开始做的时候,直接把所有参数都放开去拟合,结果发现矩阵奇异。后来才意识到,加速度计的交叉轴耦合其实很小,没必要全放开。一般只拟合对角线比例因子和零偏就够了。

4.3 校准结果验证方法

参数算出来了,怎么知道校得好不好?不能光看残差小就觉得万事大吉。

我常用的验证方法有三个:

  1. 模长检验法:静止时,加速度计输出的模长应该等于g。把校准后的数据算一下模长,看看是不是稳定在9.8左右。
  2. 重复性检验:同一个位置,多采几次数据,看看校准后的结果是否一致。
  3. 角度检验:把IMU转个已知角度,看看校准后的输出跟理论值差多少。

验证代码示例:

def validate_calibration(raw_data, S, b, g=9.8):
    # 校准
    calibrated = np.linalg.inv(S) @ (raw_data - b).T
    calibrated = calibrated.T
    
    # 计算模长
    norm = np.linalg.norm(calibrated, axis=1)
    
    # 统计
    mean_norm = np.mean(norm)
    std_norm = np.std(norm)
    error = np.abs(mean_norm - g)
    
    print(f"平均模长: {mean_norm:.4f} m/s²")
    print(f"模长标准差: {std_norm:.4f} m/s²")
    print(f"与g的偏差: {error:.4f} m/s²")
    
    if error < 0.05:
        print("校准效果良好")
    else:
        print("校准效果一般,建议检查数据")
    
    return mean_norm, std_norm, error

我记得有一次,模长检验通过了,但角度检验一塌糊涂。后来发现是数据采集时IMU没放正,参考值给错了。所以说,验证不能只做一项。

4.4 温漂补偿策略

温度漂移是加速度计校准里最头疼的问题。你室温下校得好好的,拿到户外一用,数据又飘了。

为什么会这样?因为MEMS传感器的零偏和比例因子都跟温度有关。说白了,就是温度变了,误差模型也跟着变。

温漂补偿的常见做法有两种:

方法 原理 适用场景
查表法 在不同温度下标定,建立温度-参数查找表 温度变化范围大,精度要求高
多项式拟合 用多项式拟合零偏随温度的变化曲线 温度变化规律明显,计算资源有限

我个人更推荐查表法,虽然前期工作量大了点,但效果最稳定。具体做法是:

  1. 把IMU放进温箱,从-40°C到85°C,每10°C一个点
  2. 每个温度点做一次六位置法校准
  3. 记录每个温度下的零偏和比例因子
  4. 运行时根据当前温度,插值查表得到补偿参数

注意:温箱实验一定要给足稳定时间。我曾经为了赶进度,温度到了就立刻采数据,结果数据全是乱的。每个温度点至少稳定15分钟再开始采集。

如果你没有温箱条件,也可以用一种简化方法——只补偿零偏的温度漂移。因为实际应用中,零偏受温度影响最大,比例因子的变化相对较小。你可以让IMU自然升温(比如从冷启动到稳定工作),记录温度变化过程中的零偏变化,然后拟合一条曲线。

嗯,关于加速度计校准,今天就聊这么多。下一章咱们讲陀螺仪的校准,那又是另一番天地了。