传感器标定与预处理:加速度计、陀螺仪、磁力计标定方法
各位同学,今天我们来聊聊传感器标定。
说实话,飞控系统里最让我头疼的,往往不是控制算法本身,而是传感器数据到底靠不靠谱。你想想看,算法再漂亮,输入的数据是歪的,那输出肯定跑偏。我在项目里见过太多这种情况——飞机悬停时莫名其妙往一边飘,查了半天,最后发现是加速度计零偏没标好。
所以,传感器标定是飞控调试的第一步,也是基本功。今天我把加速度计、陀螺仪、磁力计这三样东西的标定方法,掰开了讲清楚。
为什么传感器需要标定?
说白了,任何传感器出厂时都有误差。温度漂移、安装偏差、器件本身的非线性,都会让读数偏离真实值。你拿一个静止的加速度计,理论上应该读到(0, 0, -g),但实际读出来可能是(0.05, -0.03, -9.72)。
这些误差如果不消除,飞控的姿态解算就会出问题。悬停时飞机歪着,航线飞行时轨迹偏掉,都是标定没做好的后果。
核心思路:标定的本质就是建立「传感器原始读数 → 真实物理量」的映射关系。通常用一个线性模型:
真实值 = 比例因子 × (原始值 - 零偏)
我们需要标定的,就是零偏和比例因子这两个参数。
加速度计标定
加速度计标定,我习惯用六位置法。为什么叫六位置?因为我们需要让加速度计的每个轴,分别朝上和朝下各采集一次数据。
具体步骤:
- 将飞控水平放置,Z轴朝上,采集100组数据取平均
- 将飞控翻转,Z轴朝下,采集100组数据取平均
- 同理,X轴朝上、X轴朝下、Y轴朝上、Y轴朝下,各采集一次
- 这样我们得到6组数据,每组包含三轴读数
有了这6组数据,就可以解算出每个轴的零偏和比例因子。公式其实不复杂:
// 以X轴为例
// 当X轴朝上时,理论值应为 +g
// 当X轴朝下时,理论值应为 -g
// 设原始读数为 raw_up 和 raw_down
// 则有:
// scale_x = 2g / (raw_up - raw_down)
// bias_x = (raw_up + raw_down) / 2
// 实际代码实现
float raw_up = 1645; // 采集到的原始值
float raw_down = 1523;
float g = 9.81;
float scale_x = 2 * g / (raw_up - raw_down);
float bias_x = (raw_up + raw_down) / 2;
// 标定后的值
float calibrated_x = (raw_x - bias_x) * scale_x;
我的经验:六位置法虽然简单,但有个坑——你必须保证每次放置时,飞控是绝对水平的。我曾经用水平仪垫着飞控做标定,结果垫块本身就不平,标出来的参数全是歪的。后来我改用三轴转台,精度一下子就上去了。
陀螺仪标定
陀螺仪标定比加速度计简单。为什么?因为陀螺仪测量的是角速度,静止时理论值应该是0。所以标定只需要采集静止数据,算出零偏就行。
具体做法:
- 将飞控静止放置,不要有任何振动
- 连续采集1000组陀螺仪数据
- 对每个轴分别求平均值,这个平均值就是零偏
代码实现也很直接:
// 陀螺仪零偏标定
float gyro_bias[3] = {0};
int sample_count = 1000;
for(int i = 0; i < sample_count; i++) {
gyro_bias[0] += raw_gyro_x[i];
gyro_bias[1] += raw_gyro_y[i];
gyro_bias[2] += raw_gyro_z[i];
}
gyro_bias[0] /= sample_count;
gyro_bias[1] /= sample_count;
gyro_bias[2] /= sample_count;
// 使用时减去零偏
float calibrated_gyro_x = raw_gyro_x - gyro_bias[0];
注意:陀螺仪零偏会随温度变化。我遇到过最头疼的情况——冬天标定好的参数,夏天飞起来就飘。所以工业级飞控通常会做温度补偿,或者每次上电后先静止几秒,自动重新标定零偏。
磁力计标定
磁力计标定是最麻烦的,也是坑最多的。为什么?因为地磁场很弱,很容易被周围铁磁物质干扰。你拿着手机转一圈,磁力计读数画出来应该是个球,但实际画出来往往是个椭球。
椭球拟合标定法:
我个人习惯用椭球拟合。原理很简单——理想情况下,磁力计在三维空间旋转时,读数应该落在一个球面上。但因为有硬铁干扰和软铁干扰,实际数据会变成一个椭球。
标定的目标,就是把这个椭球拉回球心在原点、半径为1的标准球。
采集数据的方法:
- 拿着飞控,在空中画「8」字
- 尽量让飞控的每个面都朝向各个方向
- 采集至少200组数据,覆盖整个球面
有了数据后,用最小二乘法拟合椭球参数。这里我直接给出常用的代码框架:
// 椭球拟合标定(简化版)
// 输入:raw_mag[3] 原始磁力计数据
// 输出:calibrated_mag[3] 标定后的数据
// 标定参数(由椭球拟合算法计算得到)
float hard_iron[3] = {45.2, -12.7, 23.1}; // 硬铁偏移
float soft_iron[3][3] = { // 软铁矩阵
{1.02, 0.01, -0.03},
{0.01, 0.98, 0.02},
{-0.03, 0.02, 1.01}
};
// 标定过程
float temp[3];
for(int i = 0; i < 3; i++) {
temp[i] = raw_mag[i] - hard_iron[i];
}
// 应用软铁校正
for(int i = 0; i < 3; i++) {
calibrated_mag[i] = 0;
for(int j = 0; j < 3; j++) {
calibrated_mag[i] += soft_iron[i][j] * temp[j];
}
}
避坑指南:我曾经在无人机上装了个碳纤维管,结果磁力计读数一直不对。查了半天才发现,碳纤维管里有一根细铁丝。所以做磁力计标定时,一定要确保周围没有铁磁物质。另外,电机通电后也会产生磁场干扰,标定时最好把电机断电。
标定流程总结
好了,三种传感器的标定方法都讲完了。我画了一张流程图,帮你理清整个标定流程:
标定后的数据预处理
标定完参数,还没完。数据还需要做预处理,才能喂给姿态解算算法。
常用的预处理步骤:
- 低通滤波:传感器数据都有高频噪声,用一阶低通滤波就能去掉大部分。我一般用截止频率20Hz。
- 异常值剔除:偶尔会出现跳变的数据点,直接扔掉或者用前一帧数据替代。
- 坐标系对齐:如果传感器安装方向与飞控坐标系不一致,需要做旋转矩阵变换。
// 一阶低通滤波示例
float lowpass_filter(float raw, float prev, float alpha) {
// alpha 越小,滤波越平滑,但延迟越大
// 一般取 0.1 ~ 0.3
return alpha * raw + (1 - alpha) * prev;
}
// 使用示例
float filtered_acc_x = lowpass_filter(raw_acc_x, prev_acc_x, 0.2);
我的习惯:标定参数我会保存到飞控的EEPROM里,每次上电自动加载。但我会留一个「重新标定」的指令,方便现场调试。另外,标定完一定要做验证——把飞控静止放好,看看加速度计读数是不是(0, 0, -g),陀螺仪是不是(0, 0, 0)。
好了,传感器标定这部分就讲到这里。内容不少,但都是实打实的干货。你回去做实验时,记得先把标定做好,后面的控制调试才能事半功倍。
公众号:蓝海资料掘金营,微信deep3321