4. 六位置标定法(加速度计)
加速度计的标定,说白了就是找到它的「真实面」。
我刚开始做MEMS的时候,总觉得芯片出厂前不是已经校准过了吗?后来发现,温度、焊接应力、PCB安装角度,都会让加速度计的输出偏离理想值。你想想看,一个手机里的加速度计,如果零偏误差有50mg,那倾斜角度算出来能差好几度——这谁受得了?
六位置法,就是目前最经典、最实用的加速度计标定方法。它不需要转台,不需要高精度设备,只需要一个水平的平台,加上你的耐心。
4.1 六位置法的原理
加速度计的数学模型,其实很简单:
Ax_meas = Sx * (Ax_true + Bx)
Ay_meas = Sy * (Ay_true + By)
Az_meas = Sz * (Az_true + Bz)
其中:
- Ax_meas:加速度计实际输出的原始值
- Sx:X轴的灵敏度(比例因子)
- Bx:X轴的零偏(bias)
- Ax_true:真实的加速度值
理想情况下,Sx=1,Bx=0。但现实嘛……
六位置法的核心思想是:利用重力加速度这个已知的「标准源」。地球重力加速度g,大小是9.8 m/s²,方向永远指向地心。我们把加速度计的六个敏感轴分别对准重力方向,就能得到六个方程,解出六个未知数。
哪六个位置?
- 位置1:X轴朝上(感受+g)
- 位置2:X轴朝下(感受-g)
- 位置3:Y轴朝上(感受+g)
- 位置4:Y轴朝下(感受-g)
- 位置5:Z轴朝上(感受+g)
- 位置6:Z轴朝下(感受-g)
每个位置,我们记录加速度计三个轴的输出。这样就有6组数据,每组3个轴,总共18个测量值。但我们要解的只有6个参数(3个零偏 + 3个比例因子),所以这是个超定方程组——用最小二乘法来拟合最合适。
关键假设:六位置法假设加速度计的轴间非正交误差可以忽略,或者已经预先补偿。如果轴间误差较大,需要用更复杂的九位置法或十二位置法。
4.2 数据采集步骤
数据采集这块,我踩过不少坑。有一次在实验室里,我拿着加速度计模块手动翻转,结果手抖得不行,数据噪声大得离谱。后来学乖了——用夹具固定,每次翻转后等3秒再采集。
具体步骤:
- 准备一个水平平台:用水平仪校准,误差控制在0.1°以内。
- 固定传感器模块:用夹具或双面胶,确保模块不会松动。
- 上电预热:我一般预热5分钟,让芯片温度稳定下来。
- 位置1(X轴朝上):将模块按图示方向放置,等待2秒,采集100个样本取平均。
- 位置2(X轴朝下):翻转180°,同样等待2秒,采集100个样本取平均。
- 位置3~6:依次完成Y轴和Z轴的朝上/朝下位置。
我的小技巧:每个位置采集时,不要只采一次。我习惯采3次,每次间隔1秒,然后取中位数。这样可以剔除偶然的粗大误差。
数据记录格式建议如下:
| 位置 | Ax (LSB) | Ay (LSB) | Az (LSB) |
|---|---|---|---|
| X朝上 | 16384 | 12 | -8 |
| X朝下 | -16376 | 10 | -5 |
| Y朝上 | 15 | 16382 | -3 |
| Y朝下 | 13 | -16378 | -6 |
| Z朝上 | 8 | -5 | 16380 |
| Z朝下 | 11 | -7 | -16374 |
注意:这里的数值是ADC原始输出,单位是LSB。如果你的加速度计量程是±2g,分辨率是16位,那么1g对应的理论值是16384 LSB。
4.3 最小二乘法拟合
好了,数据到手了。接下来怎么算?
我们以X轴为例。在位置1(X朝上),X轴感受到的真实加速度是+1g,所以:
Ax_meas1 = Sx * (1g + Bx)
在位置2(X朝下),X轴感受到的真实加速度是-1g:
Ax_meas2 = Sx * (-1g + Bx)
两个方程相减,就能消掉Bx:
Ax_meas1 - Ax_meas2 = Sx * 2g
Sx = (Ax_meas1 - Ax_meas2) / (2g)
两个方程相加,消掉Sx:
Ax_meas1 + Ax_meas2 = Sx * 2 * Bx
Bx = (Ax_meas1 + Ax_meas2) / (2 * Sx)
嗯,这是最简化的解法。但实际中,我更喜欢用最小二乘法,因为它能同时处理所有数据,而且对噪声更鲁棒。
最小二乘法的思路是这样的:我们建立一个线性方程组,把六个位置的测量数据都放进去,然后求解使得残差平方和最小的参数。
对于每个位置i,我们有:
Ax_meas_i = Sx * (Ax_true_i + Bx)
Ay_meas_i = Sy * (Ay_true_i + By)
Az_meas_i = Sz * (Az_true_i + Bz)
写成矩阵形式:
[Ax_meas_1] [Ax_true_1 1 0 0 0 0] [Sx]
[Ay_meas_1] = [0 0 Ay_true_1 1 0 0] * [Bx]
[Az_meas_1] [0 0 0 0 Az_true_1 1] [Sy]
... [By]
[Sz]
[Bz]
用Python实现的话,代码很简洁:
import numpy as np
# 测量数据(LSB)
meas = np.array([
[16384, 12, -8], # X朝上
[-16376, 10, -5], # X朝下
[15, 16382, -3], # Y朝上
[13, -16378, -6], # Y朝下
[8, -5, 16380], # Z朝上
[11, -7, -16374] # Z朝下
])
# 真实加速度(g)
true_acc = np.array([
[1, 0, 0], # X朝上
[-1, 0, 0], # X朝下
[0, 1, 0], # Y朝上
[0, -1, 0], # Y朝下
[0, 0, 1], # Z朝上
[0, 0, -1] # Z朝下
])
# 构建设计矩阵
A = np.zeros((18, 6))
b = np.zeros(18)
for i in range(6):
for j in range(3):
row = i * 3 + j
A[row, j*2] = true_acc[i, j] # 比例因子
A[row, j*2+1] = 1 # 零偏
b[row] = meas[i, j]
# 最小二乘求解
params, residuals, rank, s = np.linalg.lstsq(A, b, rcond=None)
Sx, Bx, Sy, By, Sz, Bz = params
print(f"Sx={Sx:.4f}, Bx={Bx:.2f}")
print(f"Sy={Sy:.4f}, By={By:.2f}")
print(f"Sz={Sz:.4f}, Bz={Bz:.2f}")
注意:这里的真实加速度值用的是g作为单位。如果你的加速度计输出已经是m/s²,那就要把1g换成9.8。我习惯统一用g,最后再换算。
4.4 参数提取与验证
参数算出来了,怎么知道对不对?
我的做法是:用标定后的参数去补偿原始数据,然后看补偿后的模长是不是接近1g。
def calibrate(ax, ay, az, Sx, Bx, Sy, By, Sz, Bz):
ax_cal = (ax - Bx) / Sx
ay_cal = (ay - By) / Sy
az_cal = (az - Bz) / Sz
return ax_cal, ay_cal, az_cal
# 验证:用任意位置的原始数据
ax_raw, ay_raw, az_raw = 16384, 12, -8
ax_cal, ay_cal, az_cal = calibrate(ax_raw, ay_raw, az_raw, Sx, Bx, Sy, By, Sz, Bz)
norm = np.sqrt(ax_cal**2 + ay_cal**2 + az_cal**2)
print(f"补偿后模长: {norm:.4f} g") # 应该接近1.0
如果模长偏差在±0.01g以内,说明标定效果不错。我曾经遇到过模长偏差0.05g的情况,查了半天发现是平台没放平——后来用水平仪重新校准,偏差就降到0.003g了。
经验之谈:六位置法标定后的加速度计,零偏误差可以做到5mg以内,比例因子误差0.1%以内。如果你的结果差很多,先检查数据采集过程,再检查平台水平度。
最后,我把整个六位置法的流程画了张图,方便你理解:
这张图把整个流程串起来了。你跟着走一遍,基本不会出错。
最后提醒一句:六位置法虽然简单,但精度上限取决于你的平台水平度和数据采集质量。如果追求更高精度,可以考虑用温控箱做温度补偿,或者用九位置法补偿轴间误差。不过那是后话了。