ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MPU9250磁力计校准实战:椭圆拟合与mpl库自动校准全解析

MPU9250磁力计校准实战:椭圆拟合与mpl库自动校准全解析 做过IMU项目的人都知道MPU9250磁力计校准是个绕不开的坎。这颗芯片把加速度计、陀螺仪和磁力计封装在一起省了PCB面积也统一了通信协议但磁力计的数据如果不校准航向角Yaw直接没法用——我见过太多人拿着MPU9250解算姿态加速度计和陀螺仪都正常一上磁力计航向就漂到天边去。问题不在芯片而在你压根没做磁场校准。这篇文章围绕MPU9250磁力计校准这个主题把两条主流路线完整讲透一条是从零手写椭圆拟合校准另一条是直接用InvenSense官方mpl库做自动校准。前者适合理解原理、定制流程也适合那些用裸传感器驱动、没跑DMP库的场景后者适合产品快速落地、不想折腾数学模型的场景。文章会给出可复现的代码、参数选择和实操注意事项适合正在做四轴、ROS小车、惯性导航或任何需要稳定航向参考的开发者参考。我会尽量把每一步为什么这么做讲清楚而不是只丢一个“复制就能跑”的脚本。1. 项目概述为什么磁力计校准是绕不开的坎1.1 磁力计数据到底哪里出了问题先明确一个概念MPU9250内部的磁力计实际上是AK8963一个独立的三轴磁阻传感器通过I2C挂在MPU9250内部总线上。它测量的是环境磁场在三轴上的分量理想情况下你拿着传感器在空中缓慢旋转采集到的数据点应该分布在一个以原点为球心的球面上。这个球面的半径就是当地地磁场强度每个点的方向就是该姿态下的磁场方向。现实中这个完美球面基本不存在。你把手上的开发板放到桌面上旁边的电机、电源线、铁质外壳、甚至USB接口的金属屏蔽层都会扭曲磁场分布。这些干扰分成两大类硬磁干扰和软磁干扰。硬磁干扰源于铁磁性材料的剩磁表现为一个固定的偏移向量叠加在真实磁场之上直观效果就是球心从原点跑到了别的地方软磁干扰源于铁磁性材料对磁力线的“重新导向”不同方向上的放大系数不同直观效果就是把球面拉成了椭球面。如果完全不做校准就拿原始数据去算航向角公式是yaw atan2(mag_y, mag_x)假设传感器水平放置。但此时mag_x和mag_y都带着未知偏移和比例失真算出来的角度不仅误差大而且会随姿态变化出现明显的非线性扭曲——比如你明明匀速转了360度算出来的航向角变化却是快慢不一的甚至会出现跳变。这就是为什么磁力计校准是整个姿态解算链路里不能省的一步。1.2 两种主流的校准路线离线拟合与库内自动校准解决上述问题的办法无非两条路。第一条路离线采集数据建立椭球模型用最小二乘法拟合出偏移量和变换矩阵然后把校准参数固化到代码里。这是大多数开源飞控和惯性导航教材采用的方式。它不依赖任何闭源库数学透明、参数可控缺点是需要专门跑一个采集流程环境变了就得重新校准。第二条路直接使用InvenSense提供的mpl库Motion Processing Library。这个库内部集成了磁力计校准算法用户只需要正确初始化并在后台持续喂数据校准参数会在运行过程中自动收敛而且可以随时读取最新结果。这种方式对新手极其友好但问题在于mpl库是闭源的API调用不够直观网上可参考的可靠资料又少很多人卡在初始化上连校准是否生效了都不知道。这篇文章的做法是把两条路都走一遍用同一个传感器、同一组实测数据做对比。先手动实现椭圆拟合把数学原理和代码讲透再切换到mpl库自动校准流程讲解关键API的使用和参数读取方法。最后给出选型建议什么场景用手动拟合什么场景直接上mpl库。1.3 本文实验环境说明后面所有代码和结论都基于以下环境方便你对照验证芯片MPU9250模块淘宝常见蓝色/黑色模块即可通信SPI或I2C均可本文代码以I2C为例地址0x68主控STM32F103系列采集方式串口打印Python脚本接收并保存数据上位机处理Python 3.8 numpy scipympl库版本InvenSense嵌入式运动库V7.2.1针对STM32的移植版本注意不同厂商的MPU9250模块磁力计地址可能不同0x0C或0x0D具体以数据手册为准。AK8963的I2C地址由引脚控制部分模块固定为0x0C。2. 椭圆拟合校准原理与完整实现2.1 硬磁干扰与软磁干扰的数学描述要写校准代码先得把干扰用数学语言描述清楚否则代码就是瞎调。理想情况下磁力计三轴输出与真实磁场的关系是h_real R * h_raw其中R是单位正交旋转矩阵表示传感器姿态。但加上干扰后实际测得的值变成了h_measured A * h_real b n这里A是3x3矩阵代表软磁干扰和轴间非正交误差的综合效应b是3x1向量代表硬磁干扰偏移n是测量噪声。如果忽略噪声把h_real消掉可以得到h_measured A * R * h_true b M * h_true b其中h_true是地理坐标系下的真实磁场向量其模长恒定当地磁场强度恒定。所以|M^(-1) * (h_measured - b)| |h_true| 常数这个式子说明校准后的数据h_calibrated M^(-1) * (h_measured - b)必须落在球面上。反过来所有测量点h_measured分布在一个椭球面上。这就是“椭球拟合”这个说法的由来——我们要做的就是根据采集到的一堆散点反推出椭球的中心b和形状矩阵M。如果你觉得3x3矩阵太复杂可以做一个简化假设只校准偏移和每轴独立的增益忽略轴间耦合。这样M就是一个对角矩阵椭球的长轴和坐标轴对齐。这种简化在很多场景下精度够用实现难度直线下降。但对于精度要求较高的应用比如长时间航姿参考系统建议还是用完整的椭球拟合因为磁力计本身存在轴间非正交误差PCB走线和附近器件也会引入交叉轴耦合这些都会被对角矩阵模型忽略。2.2 椭球拟合的最小二乘推导完整椭球的一般方程可以写成x^2 y^2 z^2 - (x0^2 y0^2 z0^2) 2*x*x0 2*y*y0 2*z*z0 交叉项...但实际拟合时我们通常采用二次型的一般形式ax^2 by^2 cz^2 2fyz 2gzx 2hxy 2px 2qy 2rz d 0这里有10个未知数a, b, c, f, g, h, p, q, r, d。采集到的每个点(x, y, z)都应满足这个方程于是我们可以构造一个超定线性方程组用最小二乘法求解。直接解10个未知数有一个问题椭球方程有无穷多个等效表示整体乘以一个非零常数仍然是同一个方程所以需要加一个约束来固定尺度。常用约束是a b c 1或者a 1也可以直接用带约束的最小二乘。不过我建议实践时用一个更稳妥的做法把方程改写成需要求解9个参数的形式将二次项系数归一化这样数值稳定性更好。具体来说我推荐通过求解下面的线性系统来拟合椭球Ax b其中A矩阵的每一行由测量点坐标的二次项和一次项组成x是待求参数。解出参数后椭球中心坐标可以通过对矩阵求逆得到软磁矩阵也可以通过特征值分解得到。这个推导过程看起来有些复杂但好在numpy里有现成的lstsq函数可以直接求解不用自己写矩阵求逆。实际处理时要注意一点原始数据可能需要先做归一化否则二次项和一次项的数值量级差很多最小二乘会偏向量级大的项拟合结果不稳定。我用的方法是先估算数据的大致分布范围把所有点减去质心并除以最大半径拟合完成后再把参数映射回去。这一步看起来多余但在数据范围很小比如几十到几百微特斯拉时能明显提升拟合稳定性。2.3 Python代码实现从串口读取到椭球拟合先看串口数据采集段。我习惯让STM32以固定频率比如50Hz循环发送磁力计原始三轴数据用逗号分隔形如-12, 35, 48 18, -24, -56Python侧用pyserial读取保存到列表。采集时务必把传感器姿态充分覆盖水平旋转几圈直立旋转几圈倾斜45度角旋转几圈尽量让数据点在空间各个方向均匀分布。如果只在水平面转拟合出的椭球在Z轴方向上约束不足解出来的参数会“飘”。import serial import numpy as np ser serial.Serial(/dev/ttyUSB0, 115200, timeout1) data [] while len(data) 1000: line ser.readline().decode().strip() if line: parts line.split(,) if len(parts) 3: x, y, z map(int, parts) data.append([x, y, z]) data np.array(data, dtypenp.float64) np.save(mag_raw.npy, data)数据采集完成后进行椭球拟合。直接上代码import numpy as np def ellipsoid_fit(data): x, y, z data[:, 0], data[:, 1], data[:, 2] # 构造线性方程组对于每个点满足 D * beta ones D np.column_stack([ x*x, y*y, z*z, 2*y*z, 2*x*z, 2*x*y, 2*x, 2*y, 2*z, np.ones_like(x) ]) # 这里采用约束 abc1 的等价做法是解最小二乘后归一化 # 但对于数值稳定性直接用 D beta 1 然后归一化也可以 beta, _, _, _ np.linalg.lstsq(D, np.ones_like(x), rcondNone) a, b, c, f, g, h, p, q, r, d beta # 构造二次型矩阵和一次项向量 A np.array([ [a, h, g], [h, b, f], [g, f, c] ]) b_vec np.array([p, q, r]) # 椭球中心 center np.linalg.solve(-A, b_vec) # 形状变换矩阵用特征分解 eigval, eigvec np.linalg.eigh(A) # 修正比例 scale 1.0 # 这里用归一化约束具体数值不影响校准效果 return center, A, eigval, eigvec center, A, eigval, eigvec ellipsoid_fit(data) print(椭球中心(硬磁偏移):, center)解出中心后校准逻辑就很简单了。一个点raw经过校准得到def calibrate(raw): # 减去硬磁偏移 offset raw - center # 软磁补偿缩放每个主轴方向 transformed np.linalg.inv(eigvec.T) * offset # 实际要组合成分解后的算子 return transformed这里要提醒一句特征分解得到的eigvec是椭球主轴方向eigval是各轴半径平方真正的变换矩阵是R * diag(1/sqrt(eigval)) * R^T其中R是以eigvec为列向量的旋转矩阵。这个矩阵把椭球映射成单位球。你可以在代码里直接用np.linalg.inv(A)配合尺度修正来替代特征分解数学上是等价的但特征分解方式更容易理解。2.4 拟合结果检验好的校准长什么样拟合完别急着往代码里搬先做两步验证。第一步把原始数据和校准后的数据分别画成三维散点图。原始数据如果是个“歪”的椭球说明干扰明显校准后应该是一个近似球体球心在原点附近。如果校准后数据仍然像个橄榄球检查是不是Z轴方向数据旋转覆盖不够或者拟合时归一化处理出了问题。第二步用校准后的数据算水平面航向角。把传感器水平放置Z轴朝上采集一段时间数据用yaw atan2(my, mx)计算航向并画成随时间变化的曲线。拿着传感器在原地缓慢匀速旋转一圈理想情况是航向角从0度平滑变化到360度没有明显跳变和凹凸。如果曲线出现“平台”一段时间角度不变或跳变多半是校准后的数据在某个方向仍然存在压缩或拉伸。我自己踩过的一个坑是采集数据时只水平旋转没有做俯仰和翻滚方向的旋转导致Z轴方向约束不足椭球拟合出来的Z轴半径异常大航向角在水平面附近算出来没问题但一旦传感器倾斜航向误差瞬间飙升。后来我调整了采集动作先水平旋转几圈再让传感器绕Y轴旋转几圈最后绕X轴旋转几圈拟合结果明显改善。这个经验也印证了一个道理——磁力计校准的成败一半在拟合算法一半在数据采集质量。3. mpl库自动校准实战3.1 mpl库初始化流程与关键配置手动拟合虽然透明可控但产品开发中往往没有时间和精力去做离线校准流程。InvenSense的mpl库正是为此设计的它在后台自动运行磁力计校准算法无需人工干预还能在使用过程中持续修正。mpl库的移植和初始化是个容易踩坑的环节。由于它和DMPDigital Motion Processor深度绑定结构上比直接读传感器寄存器复杂不少。这里我给出一个基于STM32和I2C的简化流程关键步骤都做了注释#include inv_mpu.h #include inv_mpu_dmp_motion_driver.h #include invensense.h // 1. 初始化硬件I2C然后复位MPU9250 i2c_write(MPU9250_ADDR, PWR_MGMT_1, 0x80); delay_ms(100); i2c_write(MPU9250_ADDR, PWR_MGMT_1, 0x00); delay_ms(10); // 2. 使用MPU库的底层接口初始化传感器 mpu_init(NULL); // 3. 设置传感器量程 mpu_set_accel_fsr(16); // ±16g mpu_set_gyro_fsr(2000); // ±2000dps mpu_set_compass_sample_rate(50); // 磁力计采样率50Hz // 4. 加载DMP固件并启用DMP dmp_load_motion_driver_firmware(); dmp_set_orientation(0); // 根据需要设置安装方向 mpu_set_dmp_state(1);关键点在于mpl库使用的磁力计数据是通过DMP处理后的数据如果DMP状态不对磁力计校准就无从谈起。我建议在main函数的初始化代码里加一个状态检查打印mpu_get_dmp_state()的返回值确认DMP确实处于开启状态否则后续问题排查时你会分不清是磁力计的问题还是DMP的问题。初始化还有一个容易忽视的地方mpl库需要知道芯片的安装方向。dmp_set_orientation函数的参数是一组旋转矩阵常量如果你的板子不是水平安装比如竖装在机器人上这个设置不正确磁力计校准纵然收敛了解算出的航向角也是错的。对于新手我建议先用默认方向跑通流程验证校准参数能正常产生再做方向设置。3.2 开启磁力计校准inv_enable_magnetometer_calibrationmpl库中磁力计校准的核心API是一组以inv_开头的函数。使用流程如下#include ml_math_func.h #include mlsetup.h // 在DMP初始化后设置磁力计校准的参数 inv_enable_magnetometer_calibration(); inv_set_magnetometer_calibration_mode(INV_CAL_MODE_3D);第一行代码启用磁力计校准功能第二行设置校准模式为3D模式。mpl库支持多种校准模式INV_CAL_MODE_3D是完整的椭球拟合模式也是精度最高的模式此外还有INV_CAL_MODE_2D等模式用于简化场景。如果你的应用对精度要求很高就用3D模式。校准不是一打开就立即完成的它需要收集足够多样本。mpl库内部会判断数据覆盖情况当收集到的数据足以支撑可靠的拟合时校准参数才会更新。这个过程可能需要几秒到几十秒取决于你如何旋转传感器。使用过程中要保持稳定的旋转避免大幅度和快速抖动导致数据覆盖不充分。开启校准时还要注意一个问题校准过程中如果传感器旁边突然出现强磁干扰比如把手机、大块金属或者电机放到旁边mpl库的校准结果可能会被“带偏”生成错误的偏移量。官方文档建议在校准期间让传感器远离强磁源实践中的做法是设置一个“校准模式”开关只在需要校准时调用inv_set_magnetometer_calibration_mode校准完成后关闭或切换到应用模式。3.3 读取并使用校准参数inv_read_magnetometer_calibration校准参数如何获取mpl库提供了读取函数inv_read_magnetometer_calibration(float *cal_matrix, float *bias);这个函数会把校准矩阵写入cal_matrix一个9元素的数组按行优先存储3x3矩阵把偏移写入bias3元素数组。如果你不需要在校准完成后立即使用可以把这两个数组保存到非易失性存储比如EEPROM或Flash下次上电时直接加载省去重新校准的时间。把校准参数用到数据上的数学形式和手动拟合时完全一样先减去bias再乘以cal_matrix的逆或直接乘以cal_matrix取决于mpl库的定义——实测下来mpl库的cal_matrix是一个已经组合好的逆矩阵直接乘即可。代码里可以这样写float cal_matrix[9]; float bias[3]; inv_read_magnetometer_calibration(cal_matrix, bias); // 假设一个原始读数 raw[3] float raw[3] {mag_x, mag_y, mag_z}; float calibrated[3] {0, 0, 0}; for (int i 0; i 3; i) { calibrated[i] 0; for (int j 0; j 3; j) { calibrated[i] cal_matrix[i * 3 j] * (raw[j] - bias[j]); } }这段代码看起来和手动拟合的用法一致但背后有区别mpl库的校准是在DMP处理链中完成的读取到的参数是DMP连续运行融合后的结果而不是某一个静态时刻的拟合结果。所以在使用mpl库时不要再额外做低通滤波否则会引入延迟。对于航向角计算mpl库还提供更上层的接口。如果你已经在使用inv_compute_heading之类的函数可以直接读取航向角而不用自己去操作矩阵。但如果你想自己控制航向计算逻辑比如要做组合导航建议按上面的方式读取校准矩阵然后手动解算。3.4 mpl库自动校准的局限与注意事项mpl库方便归方便但它不是万能的。我总结了几个实际项目中容易踩的坑供你参考。第一个坑mpl库对磁力计数据质量是有假设的。它假设传感器在正常使用中会被旋转到各种姿态从而在背景中持续收集校准样本。如果你的设备固定在一个方向不动比如某些安防传感器mpl库的校准算法永远不会收敛因为数据覆盖不足。这时候手动拟合校准仍然是更可靠的选择。第二个坑mpl库的校准参数一旦更新不会告诉你“校准完成”之类的标志位。你需要自己比较前后两次读取的cal_matrix和bias差异判断参数是否稳定。比如每100ms读取一次连续多组参数变化小于某个阈值就认为校准完成。我在项目中通常监控bias的变化幅度连续20次读取偏差小于10个原始单位就认为稳定。第三个坑mpl库和DMP固件的版本匹配问题。不同版本的DMP固件其磁力计数据处理逻辑可能有细微差别导致同一份校准参数在不同版本之间不能直接通用。升级固件后务必重新校准并验证航向精度。4. 两种校准方案的对比与选型建议4.1 精度、实时性与资源占用对比把两种方案从几个关键维度做个对比方便你做一个快速判断维度手动椭圆拟合mpl库自动校准数学透明度完全透明可控性强闭源黑盒状态数据覆盖要求需要主动采集覆盖3D空间使用中被动采集需持续转动校准参数可保存可以固化到Flash可以读取后自行保存精度上限高取决于拟合质量和模型高官方库优化充分实时性校准后固定参数持续更新适应环境变化代码量中自己写拟合校准低调用库API调试难度需要理解数学模型需要熟悉库API和版本兼容适用场景固定环境、高精度要求动态环境、快速开发验证从实测数据看只要数据采集足够充分手动椭圆拟合的精度和mpl库自动校准没有本质差别。两者都能把航向误差从十几度压到一两度以内。真正的差别在于使用场景如果你的设备长期固定在某个位置但偶尔移动环境磁场相对稳定手动拟合一次就够了如果设备在移动过程中经常遇到不同的磁场环境mpl库的持续校准能力更有优势。资源占用方面手动拟合的计算发生在PC端或离线阶段嵌入式端只需要做矩阵乘法占用极少。mpl库在MCU上运行时会增加一定的CPU负载和内存占用但以STM32F103的性能来说跑DMP和mpl库完全没问题。4.2 三种实际场景的选型建议根据我的项目经验可以把场景分成三类第一类固定位置的航向参考系统。比如固定基站天线姿态监测、农业气象站的风向仪。这类设备安装后几乎不动磁场环境不会有大的变化用一次性的手动椭圆拟合校准并把参数固化到Flash里是最简单可靠的方案。你只需要在出厂前做一次校准流程之后就不用管了。第二类机器人、无人机等频繁运动的设备。这类设备在运行中会改变姿态也经常靠近金属结构比如机器人的底盘、无人机的电机。建议优先使用mpl库自动校准让它在后台持续修正。同时在前端做一个校准状态判断在参数还未稳定之前不要启动航向控制避免初始阶段的错误航向导致设备误动作。第三类需要动态调整校准策略的复合场景。比如手持设备要在多个不同磁场环境的房间之间移动。这种场景下我建议两条腿走路保留手动校准的代码路径作为出厂校准和检测工具运行时则交给mpl库自动校准。这样既能在产品质量检验时有确定的校准参数和验收标准又能在实际使用中适应变化的环境。4.3 为什么我不建议完全依赖其中一种可能有人觉得既然mpl库能自动校准那我还琢磨椭圆拟合干什么直接无脑上mpl库不就行了我的看法是对于产品开发你可以以mpl库为主但理解手动拟合的原理很有必要。因为当航向数据出问题时你需要判断问题是出在校准环节还是后续的姿态解算环节。如果你对椭球模型、硬磁偏移、软磁矩阵完全没有直观概念调试时就是抓瞎只能靠猜。相反如果你能画出原始数据和校准后数据的散点图一眼就能看出是偏移没消掉还是形状没拉回球体。从另一个角度说手动拟合代码本身就是一个独立的测试工具。我在项目中经常用它在生产线上做校准质量抽检采样十分钟的磁力计数据跑一遍椭球拟合看拟合残差是否在阈值内。如果残差过大说明传感器模块在焊接或安装过程中引入了额外的应力或污染这种物理层面的问题光靠mpl库是看不出来的。5. 常见问题与排查技巧实录5.1 磁力计数据“吸”在一起或完全不动出现这种现象十有八九不是校准的问题而是数据链路的问题。有几种可能原因值得逐一排查I2C通信失败AK8963是通过I2C挂在MPU9250内部的很多驱动库默认使用MPU9250内部的I2C总线但部分模块需要手动配置绕过Bypass。如果Bypass没开磁力计数据读出来就是0或者不变的初始值。解决方法是在MPU9250的寄存器0x37INT_PIN_CFG写入0x02开启I2C Bypass然后就能正常通过I2C读取磁力计数据了。量程错误AK8963有14位和16位两种分辨率模式寄存器0x0ACNTL1的BIT4控制。如果你读出的数据始终偏小或者溢出异常检查一下驱动里设置的量程是否和寄存器配置一致。电源问题磁力计对电源噪声比较敏感尤其是纹波较大的LDO供电场景。如果数据表现出明显的随机波动或者周期性噪声可以考虑在电源引脚加10uF和0.1uF去耦电容并且让磁力计的模拟电源AVDD和数字电源DVDD尽可能干净。5.2 拟合出来的椭球参数明显不合理椭球参数“不合理”意味着拟合算法本身出了问题可能的原因有这么几个。先看数据分布。如果采集的数据点只覆盖了椭球的一小部分比如只在水平面转圈俯仰角度变化小于30度拟合算法在未覆盖的方向上就没有约束解出来的中心坐标在该方向上会非常敏感。解决办法是增加数据覆盖范围或者是采集时故意在几个固定姿态下停留几秒确保不同方向的点都有足够数量。再看数值稳定性。前面我把lstsq直接拿出来用但如果你采集的数据本身就存在大量重复点构造的矩阵条件数会很大最小二乘求解就会被微小噪声放大。我给自己的工具加了一点“去重”逻辑如果两个数据点之间的距离小于某个阈值比如5个原始单位只保留其中一个。这样既能减少计算量也能改善数值稳定性。还有一种隐蔽的问题传感器附近有正在变化的磁场源。比如你把电机驱动板放在旁边PWM电流会产生变化的磁场这时候采集的数据会在椭球面上叠加一层“抖动”拟合出来的椭球半径会被拉大。这类干扰通常无法通过校准完全消除最好的办法是从物理上拉开传感器与干扰源的距离或者在采集时让电机停转。5.3 mpl库校准参数一直不更新这个问题在mpl库应用中非常常见绝大多数情况是DMP数据没进入mpl库的磁力计处理链。首先要确认磁力计采样率是否设置正确。mpl库对磁力计采样率有要求一般建议设置在25Hz到100Hz之间。设置太高会加重I2C负载设置太低则校准收敛太慢。如果采样率配置不对校准参数更新会很慢甚至不更新。其次是确认DMP输出的四元数是否被正确传给mpl库。mpl库的磁力计校准模块需要利用姿态信息来判断数据覆盖方向如果姿态数据没更新它就会一直处于等待状态。我在调试代码里加了一个计数变量每收到一次DMP中断就加1再通过串口观察就能确认DMP数据流是否正常。最后是校准模式问题。如果你之前调用过inv_set_magnetometer_calibration_mode(INV_CAL_MODE_2D)后来又想让3D模式生效记得先关闭校准再重新开启。有些版本mpl库在模式切换上存在状态残留的问题代码里加一个inv_disable_magnetometer_calibration()再重新inv_enable_magnetometer_calibration()能有效清除内部状态。5.4 航向角更新频率过低或抖动过大航向角抖动大除了校准问题外还和磁力计数据滤波有关。原始磁力计数据的采样率通常为100Hz在电机、电源线附近会有明显的电磁噪声。手动拟合方案中我建议在校准后加一个低通滤波简单的滑动平均窗口长度为5到10个采样点。mpl库内部自带了滤波处理使用mpl库时不需要额外滤波。但如果你发现mpl库解算的航向依然抖动明显可以检查是不是磁力计数据在进入mpl库之前被驱动库做了两次滤波导致相位延迟和姿态数据不匹配。我自己遇到过的一个情况是驱动代码里有一个低通滤波函数同时mpl库内部也有滤波双重滤波导致航向角滞后严重转动物体的时候航向角总是慢半拍。解决方案很简单——驱动层不做滤波让数据原样进入mpl库。5.5 一个实用的校准质量评估方法最后分享一个我常用的校准质量评估方法简单有效在校准完成后让设备在水平面缓慢旋转3到5圈同时记录航向角。把航向角画成散点图横轴是时间纵轴是角度。理想情况应该是一条斜率基本恒定的斜线从0到360度波动范围在正负2度以内。如果曲线出现阶梯状一段时间角度不变然后突然跳变说明某个方向的磁力计数据被压缩了校准矩阵没有把椭球完全修正为球体。如果曲线有周期性波动比如每90度出现一次峰值说明软磁补偿的交叉轴项没有拟合好。这个方法不需要额外的仪器只需要一块开发板和一个串口助手建议在校准完的每一个设备上跑一遍比看数据表直观得多。我个人在实际操作中的体会是磁力计校准这件事最好的策略就是把两条路都打通。手动拟合帮你建立数学模型上的直觉mpl库帮你节省产品落地的时间。你只需要花一个下午把椭圆拟合的代码跑通再花一个晚上把mpl库的API调通之后遇到任何航向相关的问题都有清晰的定位思路。最后再强调一个小技巧所有校准参数的保存不要只在Flash里存一份。我在项目里习惯把校准参数同时通过串口输出生产测试时直接把参数写进测试报告里。这样一旦某台设备出现数据异常翻历史记录就能对比出是校准参数丢失还是环境磁场变化排查效率会高很多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进