3、加速度计标定:六面法标定流程,零偏与尺度因子计算,标定结果验证
加速度计标定,说白了就是让传感器学会说“人话”。
你想想看,一个裸的加速度计,它输出的原始值跟真实的重力加速度之间,差着零偏、尺度因子,还有安装误差。如果不标定,你算出来的姿态角全是歪的。我刚开始做飞控那会儿,就吃过这个亏——飞机悬停时总往一边飘,查了半天,最后发现是加速度计零偏没校准。
嗯,今天我们就来聊聊最经典的标定方法:六面法。
核心思路:利用重力加速度作为已知参考,把加速度计的六个面(±X、±Y、±Z)分别对准重力方向,采集数据,然后反算出零偏和尺度因子。
3.1 六面法标定流程
六面法这个名字听起来挺唬人,其实操作很简单。你需要一个水平台,或者至少一个你知道绝对水平的平面。我个人习惯用大理石平台,精度够。
具体步骤:
- 预热:上电后等 5 分钟,让传感器温度稳定。我见过有人冷启动直接标定,结果温度一变化,零偏全漂了。
- 水平校准:把 IMU 放在水平台上,记录 Z 轴向上时的数据。这就是第一个面。
- 翻转:依次将 IMU 的 +X、-X、+Y、-Y、-Z 面朝上,每个面保持静止,采集 200~500 个样本。
- 取均值:每个面的数据取平均,得到 6 个平均加速度值:
A_x+,A_x-,A_y+,A_y-,A_z+,A_z-。
我的小技巧:每个面采集时,别急着开始。等数据稳定了再录,大概等 2~3 秒。我曾经用抖动的手拿着 IMU 翻转,结果数据里全是高频噪声,算出来的尺度因子偏差了 2%。
3.2 零偏与尺度因子计算
好,数据到手了。现在我们来算零偏和尺度因子。
先明确一下模型。对于每个轴,加速度计的输出可以写成:
A_meas = S * A_true + B
其中:
A_meas是测量值(原始 ADC 读数或电压)A_true是真实加速度(单位 g)S是尺度因子(单位:LSB/g 或 V/g)B是零偏(单位:LSB 或 V)
对于六面法,我们利用重力加速度 g 作为已知参考。当某个轴朝上时,该轴的真实加速度为 +1g;朝下时为 -1g。其他两个轴理论上为 0g。
以 Z 轴为例:
- Z 轴朝上:
A_z+ = S_z * (+1) + B_z - Z 轴朝下:
A_z- = S_z * (-1) + B_z
解这个二元一次方程组:
S_z = (A_z+ - A_z-) / 2
B_z = (A_z+ + A_z-) / 2
同理,X 轴和 Y 轴也一样。就这么简单。
注意:这里假设了安装误差已经很小,或者我们暂时忽略交叉轴耦合。如果要求更高精度,需要引入 12 参数或 9 参数模型,但六面法只解决零偏和尺度因子。
下面是一个完整的 Python 计算示例:
import numpy as np
# 假设采集到的六面均值数据(单位:LSB)
data = {
'x+': 16450, 'x-': 15830,
'y+': 16520, 'y-': 15790,
'z+': 16980, 'z-': 15260
}
# 计算零偏和尺度因子
S_x = (data['x+'] - data['x-']) / 2
B_x = (data['x+'] + data['x-']) / 2
S_y = (data['y+'] - data['y-']) / 2
B_y = (data['y+'] + data['y-']) / 2
S_z = (data['z+'] - data['z-']) / 2
B_z = (data['z+'] + data['z-']) / 2
print(f"X: S={S_x:.2f}, B={B_x:.2f}")
print(f"Y: S={S_y:.2f}, B={B_y:.2f}")
print(f"Z: S={S_z:.2f}, B={B_z:.2f}")
输出结果:
X: S=310.00, B=16140.00
Y: S=365.00, B=16155.00
Z: S=860.00, B=16120.00
你看,Z 轴的尺度因子明显比 X/Y 大,这是因为 Z 轴量程通常不同。零偏都在 16100 左右,说明传感器一致性还不错。
3.3 标定结果验证
算完了,别急着收工。验证这一步,我建议你认真做。
验证方法:
- 回代检查:用标定后的参数重新计算每个面的加速度,看是否接近 ±1g 和 0g。
- 模长检查:在任意姿态下,加速度计输出的模长应该接近 1g。如果偏差超过 0.02g,说明标定有问题。
- 静态倾角检查:把 IMU 放在已知角度的斜面上,对比计算出的倾角与实际角度。
我一般会用模长检查作为快速验证。具体做法:
# 标定后的数据
def calibrate(raw_x, raw_y, raw_z):
x_cal = (raw_x - B_x) / S_x
y_cal = (raw_y - B_y) / S_y
z_cal = (raw_z - B_z) / S_z
return np.array([x_cal, y_cal, z_cal])
# 随机取一组静态数据
raw = np.array([16150, 16180, 16900])
cal = calibrate(*raw)
norm = np.linalg.norm(cal)
print(f"标定后模长: {norm:.4f} g") # 期望接近 1.0000
避坑指南:我曾经遇到过一次,模长检查总是 1.02g,怎么都降不下来。后来发现是水平台本身有 0.5° 的倾斜。记住,你的参考基准必须比传感器精度高一个数量级。
如果验证不通过,别急着改参数。先检查数据采集过程:
- 每个面是否真的静止了?
- 翻转时有没有引入冲击?
- 温度是否稳定?
嗯,六面法虽然简单,但做好也不容易。我个人习惯在每次飞控上电后,先跑一遍快速标定(只做零偏),每周做一次完整六面法。这样既能保证精度,又不会太耗时。
最后,给你一个参考表格,对比不同标定方法的适用场景:
| 方法 | 标定参数 | 所需设备 | 精度 | 耗时 |
|---|---|---|---|---|
| 六面法 | 零偏 + 尺度因子 | 水平台 | 中等(0.5% 以内) | 10 分钟 |
| 多位置法 | 零偏 + 尺度 + 安装误差 | 转台 | 高(0.1% 以内) | 30 分钟 |
| 椭球拟合法 | 零偏 + 尺度 + 交叉轴 | 任意旋转 | 中等(受运动影响) | 5 分钟 |
对于大多数飞控应用,六面法已经足够。如果你做的是高精度导航,那得上转台。但那是另一节课的内容了。