ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

LSM303AGR电子罗盘开发:磁校准与倾斜补偿实战

LSM303AGR电子罗盘开发:磁校准与倾斜补偿实战 去年做一款智能指环的导航功能时我接手了一个很头疼的需求在只有一颗LGA封装的LSM303AGR、功耗预算不到1mA的前提下把电子罗盘的方位精度做到正负3度以内。LSM303AGR是ST推出的低功耗6轴组合传感器集成了3轴磁力计和3轴加速度计专门面向便携设备。我身边不少同行一听“罗盘”就说“不就是读磁力计寄存器再算个atan2吗”但真跑起来才发现数据噪声、硬铁干扰、倾角补偿、板级布局随便哪一环不处理好输出角度都能飘到怀疑人生。这篇文章就把我从选型到量产调试的完整过程记录下来重点是磁校准和倾斜补偿这两块最容易翻车的部分适合正在做电子罗盘、可穿戴导航或者姿态参考系统的工程师参考。1. 为什么在罗盘项目里选LSM303AGR参数之外的选型逻辑1.1 一颗芯片同时搞定加速度计和磁力计电子罗盘和普通磁力计项目的最大区别在于罗盘应用不能只读磁场强度还必须知道设备当前的姿态。手持设备但凡有倾斜磁力计的X、Y、Z三轴投影就会发生变化如果不做倾斜补偿输出的方位角在设备横滚或俯仰时会产生很大的偏差。常见方案是磁力计加加速度计两颗芯片组合使用但这样既占PCB面积又增加系统复杂度。LSM303AGR把三轴磁力计和三轴加速度计集成在一个2mm x 2mm、厚度0.7mm的LGA封装里12个引脚布板时当作一颗普通传感器处理就行。加速度计可以同时用于计步、抬手亮屏、跌落检测等功能一颗芯片承担多个传感器融合任务这对小体积可穿戴设备来说是很有吸引力的。我在智能指环项目里选择它最直接的原因就是板子空间实在有限单独放两颗传感器是件很奢侈的事。另外LSM303AGR使用数字接口I2C或SPI输出数据不需要外部ADC信号链路全部在芯片内部处理抗干扰性比模拟输出的磁阻传感器好不少。磁力计数据以LSB形式直接输出省掉了后续模拟调理电路的设计工作这在研发周期紧张的时候能节省不少时间。1.2 功耗、量程与接口电池设备关心的三个数字对于可穿戴设备来说功耗往往比精度更敏感。LSM303AGR的磁力计在低功耗模式下典型工作电流在微安级别加速度计在低功耗模式下也只是个位数微安相比许多独立磁力计芯片动不动几百微安的电流这种功耗水平对电池设备的续航来说是本质差别。我实测在50Hz磁力计输出、100Hz加速度计输出的配置下整颗芯片的平均电流大约在百微安以内比板上的蓝牙射频功耗低一到两个数量级基本可以忽略不计。量程方面LSM303AGR磁力计支持正负50高斯的满量程而实际地磁场强度在地表只有0.25到0.65高斯左右。看到这个数字你可能觉得奇怪量程给这么大不是浪费吗实际上这是有意设计的因为设备内部的磁干扰源比如扬声器磁铁、充电线圈、铁镍合金外壳的磁场强度远大于地磁场量程不足会导致数据削顶饱和罗盘直接就废了。而且磁力计在50高斯量程下灵敏度仍有1.5毫高斯每LSB地磁水平分量大约300到500毫高斯换算过来能产生200到300多个LSB的输出变化足够支撑较高精度的角度计算。接口方面我印象最深的是BDU位也就是块数据更新机制。磁力计数据在更新过程中如果被单片机读取可能出现高低字节分别是前后两帧的“拼接数据”罗盘角度会偶发性跳变。打开BDU之后寄存器数据会锁存到稳定后再更新这个位一定要在初始化代码里设置好。1.3 板级布局的坑比选型更影响精度这一点必须要单独拿出来说。同样一颗LSM303AGR在A厂家方案板上精度正负2度换到B厂家板上直接变成正负10度差的就是布局。磁力计测的是磁场矢量而PCB上任何流过电流的走线都会产生磁场任何铁磁性材料都会改变磁场分布。除了把传感器放在板边远离电流环路以外还要注意扬声器、马达、电池和充电线圈这些大磁源。扬声器和马达内部的永磁体是硬铁干扰会让磁场数据产生巨大的固定偏置充电线圈在工作时产生的是动态干扰会让数据出现周期性波动。我的实际经验是传感器距离这些磁源至少要留1厘米以上的安全距离如果不能避开就要在结构上做磁屏蔽隔离。另外传感器正下方不要布置大电流走线尤其是电源和地回路。地平面的涡流效应虽然比走线小但大功率输出时仍然可能扰动磁力计。还有一点容易被忽略PCBA分板时如果使用金属支撑柱或者钢片加固这些钢材会被地球磁场磁化形成额外的固定干扰需要在结构设计阶段就评估进去。2. 电子罗盘到底在算什么从地磁场到方位角的一条链路2.1 磁力计输出的三个数代表什么很多初学者会把磁力计想象成一个“指向地球北极的指南针”这是不对的。三轴磁力计测量的是它所在位置的磁感应强度矢量在地表无干扰的环境下这个矢量就是地磁场向量。地磁场在不同纬度有不同的倾斜角在赤道附近磁场方向接近水平在极地附近磁场方向接近垂直向下。磁力计输出的X、Y、Z三个LSB值就是这个向量在传感器坐标系里的投影。电子罗盘的定位原理其实是利用地磁场水平分量。地磁场向量可以分解为水平分量和垂直分量其中的水平分量方向就是磁北方向。所以罗盘算法的本质就是把传感器坐标系中的磁场向量转换成水平坐标系中的水平分量然后再通过atan2函数求出该分量与设备前向轴的夹角这个夹角就是相对磁北的方位角。我在调试时习惯先把原始磁数据转成高斯单位打印出来然后观察水平旋转一圈时X、Y是否呈现正弦和余弦变化。如果波形干净且相位正确说明基础链路没问题如果波形有突变、有偏置就要往下查干扰源或者校准问题。2.2 加速度计为什么必不可少有人可能会问如果我保证设备水平放置只用磁力计的X、Y不就行了吗理论上可以但工程上很难做到。设备在手里使用时不可能一直水平每次倾斜都会导致磁场向量在传感器坐标系里的分布发生变化。举个具体例子设备绕Y轴俯仰30度时原本在水平面上的X轴分量部分转移到了Z轴如果仍然只取X、Y算角度得到的方位角可能有10度以上的偏差。角度越陡偏差越大到了接近垂直状态下水平分量几乎全部落在Z轴上罗盘输出基本完全失效。加速度计的作用就是测量重力向量从而估计设备的横滚角和俯仰角再把磁力计测到的向量反旋转到水平坐标系中还原出真实的水平分量。这就是倾斜补偿也叫“姿态校正”。在LSM303AGR上加速度计和磁力计同步采样后经过校准可以在倾斜至正负60度范围内保持较高精度。2.3 从原始数据到方位角的三步走完整流程分三步第一步用加速度计数据计算横滚角roll和俯仰角pitch第二步用旋转矩阵将磁力计向量投影回水平面得到水平磁分量Xh和Yh第三步用atan2(Yh, Xh)计算方位角。这三步中第一步是基础第二步是核心第三步是收尾。每一步的误差都会逐级累积。需要特别注意的是第三步算出来的方位角是相对磁北的角度不是相对地理北极的要得到真正意义上的地理方位还需要叠加当地磁偏角。不同经纬度的磁偏角差别很大应用端如果做的是固定区域产品可以直接在固件里写死一个修正值如果是全球漫游产品就需要查表或者从定位系统获取。方位角的数学定义还涉及角度范围处理。atan2的输出范围是-180度到180度罗盘惯例是0度指向北、90度指向东、顺时针递增也就是0到360度。换算时如果得到负值就加360度。这个逻辑虽然简单但很容易写错调试时每次都在固定方向偏转180度的话十有八九就是这步的问题。3. 磁校准决定罗盘能不能用的关键工程步骤3.1 硬铁干扰和软铁干扰怎么区分在做磁校准之前先要搞清楚干扰来自哪里。磁干扰分两大类硬铁干扰和软铁干扰。硬铁干扰主要来自设备内部的永磁体和被磁化的铁磁性材料它会在磁场数据上叠加一个固定偏置表现为零点偏移数据分布是一个偏心球。软铁干扰来自设备附近的铁磁性材料对磁力线的重定向它会让不同方向的灵敏度不一样数据分布变成椭圆或椭球。LSM303AGR在正负50高斯的量程下内部磁源的固定偏置在原始数据上非常显著。我测试过一颗靠近微型扬声器的板子磁力计零点被拉偏了几百毫高斯相当于把真正的磁场信号完全淹没。如果不做校准整个系统根本没有可用性判断基准。其实大多数消费级电子罗盘项目解决硬铁偏移后就基本能用了因为软铁干扰通常比硬铁弱一到两个数量级。但如果你对精度要求比较高或者板子结构里确实有较多铁磁性结构件就要把软铁校正也纳入流程。软铁校正在算法上要解一个包含9个参数甚至更多的椭球拟合问题在单片机上做浮点运算也不是不行但要考虑代码体积和CPU负载。3.2 最大最小值校准法的完整操作对大多数电子罗盘项目最简单实用的校准方法是最大最小值法。原理很简单让设备在空间中尽可能多地变换姿态采集磁场数据记录每个轴的输出最大最小值然后按下式求出校准值。offset_x (max_x min_x) / 2scale_x (max_x - min_x) / 2校准后的数据为x_cal (x_raw - offset_x) / scale_x这个方法的合理前提是设备在校准过程中经历了所有空间方向数据包的包络刚好包围真实磁场球体。实际使用中我会让操作员拿着设备画“∞”字同时让设备绕Z轴旋转至少两圈再刻意把设备竖起来转几圈。我在测试时发现如果只在水平面转圈Z轴的限值没有被覆盖到校准后竖直方向依然有较大误差。最终校准效果可以用一个指标快速判断在各姿态下校准后数据的模长是否接近常数。如果模长波动在正负5%以内说明校准质量可以接受。我习惯把模长波动作为一个测试项写入产线验证流程。3.3 校准结果怎么验证校准做完不是直接就能用我一般会做两个验证实验。第一个是水平旋转验证把设备平放让Z轴垂直向上绕Z轴匀速旋转一圈记录输出方位角。理想情况下角度随旋转角度线性变化最大误差可以直接读出来。如果发现角度曲线呈波浪状且周期是360度说明有硬铁干扰没有校正干净回去检查offset是否算错如果周期是180度的波浪多半是软铁干扰或者scale没校准好。第二个是多姿态抽样验证让设备在不同倾斜姿态下分别瞄准已知方位角比如正北方向对比输出。这一步主要验证倾斜补偿是否有效。如果平着精度不错、倾斜后误差变大很多问题通常出在加速度计的校准上比如加速度计零偏、标度因子误差或安装角度偏差。还有一个很容易犯的错误是校准参数和实际工作环境的磁场不同。校准应该在最终装机状态、最终工作环境里进行。如果在研发调试台上校准完再装进金属外壳整机里使用出厂后精度肯定受影响。因为金属机壳本身会改变磁场分布相当于换了环境就要重新校准。4. 倾斜补偿算法拆解公式到代码的距离4.1 横滚角和俯仰角从哪来加速度计测量的是比力静置状态下测到的就是重力加速度向量重力方向始终竖直向下。利用加速度计输出Ax、Ay、Az可以估计设备相对于水平面的倾斜角。横滚角roll是设备绕前向轴通常定义为X轴转动的角度俯仰角pitch是设备绕侧向轴通常定义为Y轴转动的角度。标准计算公式如下roll atan2(Ay, sqrt(Ax * Ax Az * Az))pitch atan2(-Ax, sqrt(Ay * Ay Az * Az))这里之所以用atan2和平方根的组合而不是直接asin是为了避免角度接近90度时的奇异性问题也在正负角度范围内保持连续性。实际工程里还要注意一点设备有线性运动时加速度计测到的就不是单纯的重力向量了急加速状态下计算出来的角度会有较大误差。对于步行或慢速转动的场景问题不大但如果放在车上加速度扰动可能导致导航角度抖动这时候需要用陀螺仪做姿态融合来弥补。4.2 把磁向量拉回水平面有了roll和pitch接下来就是把磁力计测到的向量从传感器坐标系转换到水平坐标系。转换方法是先绕X轴反旋转roll再绕Y轴反旋转pitch等价于把设备“放平”。旋转后水平坐标系中的两个分量公式如下Xh x * cos(pitch) y * sin(roll) * sin(pitch) z * cos(roll) * sin(pitch)Yh y * cos(roll) - z * sin(roll)这两个公式看着唬人拆开看就是两次旋转变换的矩阵乘法。第一次用roll把Y、Z轴上的分量对齐到水平面第二次用pitch把X轴和Z轴的残余分量再对齐一次。之所以是反旋转而不是正旋转是因为我们要把已经倾斜的测量值映射回“设备水平”的假设坐标里。公式里用的x、y、z应该是经过磁校准后的数据不是原始读数。如果输入的是未校准的原始值姿态再准确也白搭输出角度会带着很大的固定偏差。有些资料里公式的符号约定不同取决于坐标轴方向和roll、pitch正方向的定义。工程上最安全的做法是把旋转矩阵按自己实际定义的坐标轴重推一遍或者用仿真数据验证不要直接从网上下载一段代码就跑。4.3 方位角换算与修正水平磁分量Xh和Yh求出后方位角就是heading atan2(Yh, Xh)这个角度是设备前向轴与磁北方向之间的夹角加上磁偏角修正后就变成地理方位角heading_true heading declination磁偏角因地域而异同一城市每年也在缓慢变化。我通常的做法是在固件里提供一个配置项支持通过外部命令写入当地磁偏角避免为不同销售区域维护多套固件。对于需要全球漫游的产品可以从定位模块获取经纬度后查地磁模型表在应用层叠加修正。angle heading_true;if (angle 0) angle 360.0;这里的0度对应正北90度对应正东顺时针递增。4.4 一个可以直接跑的C代码骨架以LSM303AGR为例初始化I2C接口并配置芯片后主循环里按顺序读取磁力计和加速度计数据然后调用计算函数。以下是我在项目里用过的简化代码骨架去掉了平台相关的I2C读写函数。typedef struct { float mx, my, mz; /* 磁力计原始数据已转成高斯 */ float ax, ay, az; /* 加速度计原始数据已转成g */ } sensor_data_t; typedef struct { float offset[3]; float scale[3]; } mag_cal_t; float roll_deg, pitch_deg, heading_deg; void calculate_heading(sensor_data_t *s, mag_cal_t *cal, float declination) { /* 磁力计校准 */ float mx (s-mx - cal-offset[0]) / cal-scale[0]; float my (s-my - cal-offset[1]) / cal-scale[1]; float mz (s-mz - cal-offset[2]) / cal-scale[2]; /* 用加速度计计算横滚和俯仰角单位弧度 */ float roll atan2f(s-ay, sqrtf(s-ax * s-ax s-az * s-az)); float pitch atan2f(-s-ax, sqrtf(s-ay * s-ay s-az * s-az)); float cosr cosf(roll); float sinr sinf(roll); float cosp cosf(pitch); float sinp sinf(pitch); /* 倾斜补偿得到水平面磁场分量 */ float xh mx * cosp my * sinr * sinp mz * cosr * sinp; float yh my * cosr - mz * sinr; /* 计算方位角并转换到0~360度范围 */ float heading atan2f(yh, xh) * 180.0f / M_PI; heading declination; if (heading 0) heading 360.0f; if (heading 360.0f) heading - 360.0f; heading_deg heading; roll_deg roll * 180.0f / M_PI; pitch_deg pitch * 180.0f / M_PI; }需要注意的是这段代码里我直接用了float运算在带FPU的MCU上没问题但在一些低端Cortex-M0平台上大量三角运算会比较吃力。如果CPU紧张可以预先算好正弦余弦值做成查找表或者降低方位角的更新频率比如10Hz足够人眼使用。5. 实测调优让罗盘从“有指向”到“稳得住”5.1 数据平滑的两种做法传感器原始数据总会有噪声磁力计在环境有工频干扰或者电源纹波较大时输出的跳动可能会很明显。我见过不少人一上来就套一个深度滑动平均滤波结果角度是稳定了但动态响应慢得跟藕似的转一下手要等好几秒钟才跟上。对罗盘应用我推荐在角度输出前做一阶低通滤波也就是互补滤波的思路out alpha * new_sample (1 - alpha) * last_out。alpha的取值取决于采样率和期望的响应速度。在100Hz采样率下alpha取0.2到0.3通常能在稳定性和动态响应之间取得较好平衡。加速度计数据如果噪声偏大可以先做轻度的均值滤波或低通滤波再做角度计算。但要注意滤波会引入相位延迟加速度计延迟又会造成倾斜补偿误差所以滤波强度不要过度。我通常只对磁力计数据做滑动平均窗口为4到8个样本的滤波加速度计保持原始数据或者窗口为2到4个样本这样对动态性能影响最小。5.2 采样率和输出频率的匹配LSM303AGR的磁力计支持选择输出数据率ODR加速度计也可以独立配置。我建议磁力计和加速度计配置成相近的ODR比如都是50Hz或100Hz这样两组数据在时间上更匹配。如果加速度计更新频率明显高于磁力计倾斜补偿时拿到的两个数据不是同一时刻的状态运动过程中会产生额外误差。读取策略上我习惯通过状态寄存器或中断引脚等待新数据就绪后再读取而不是盲目地以固定周期读。否则可能连续读到多帧旧数据也可能跳过中间帧造成角度输出不均匀。有时看着角度轻微来回抖动其实就是读写时序没对齐造成的。如果应用端只需要20Hz的输出频率传感器的ODR设成50Hz多次读取取平均后再算角度这样比把ODR直接设成20Hz更能抑制噪声因为磁力计本身存在随机噪声过采样平均可以提升有效分辨率。5.3 抓了几个真实现象漂移、抖动、大角度翻转调试过程中我记录了几个高频问题。第一个是静态漂移。设备放在桌上不动方位角缓慢变化或者突然跳几度。这通常是磁干扰源在工作最常见的是旁边手机充电或者带磁吸的支架。排除外部干扰后需要确认是不是数据读取偶尔错位导致的毛刺用BDU位和就绪检测可以解决。第二个是转动抖动。在水平旋转过程中角度输出有明显的抖动尤其在不同方位上抖动幅度不同。我用示波器观察过原始数据一个方位噪声大转过90度后噪声小这基本就是软铁干扰造成的因为磁力线在铁磁物体周围被扭曲等效灵敏度随方向变化。此时单纯的最大值最小值校准已经不够必须做椭球拟合或者从硬件上削弱干扰源。第三个是大角度翻转误差。设备倾斜超过正负60度后再计算方位角误差明显变大。原因是加速度计在接近垂直时对倾角的估计误差增大旋转矩阵放大效应也变强。如果产品使用场景经常处于大角度状态建议引入陀螺仪做姿态航向参考系统而不是单纯靠加速度计做倾斜补偿。6. 固件细节从裸机能跑通到量产不翻车6.1 别忘了BDU位和寄存器自动递增初始化代码里有一类细节特别容易漏数据就绪标志、块的更新模式、寄存器地址自动递增。如果漏了BDU位低速I2C下读取磁力计高低字节时数据更新可能刚好发生在两个字节读取之间导致某一个轴的数值突然跳变。这个故障复现起来很诡异因为不是每次都会出现频率不高时很容易被当成外部干扰。寄存器地址自动递增也是提升稳定性的一招。连续读取加速度计或磁力计的多字节数据时正确配置地址递增位可以一次突发读取整组数据避免分多次读导致的时间窗口错位也减少I2C总线高频切换带来的开销。启动顺序同样有讲究。磁力计内部有上电自检和偏置建立过程寄存器复位后不会立即输出稳定数据。我在实验中发现刚初始化完马上读数据前几十次读取的几个轴数据会缓慢爬升到稳定值如果直接拿去做校准会把一段非稳态数据混入校准集。因此在初始化后加一个延时等稳定后再开始正常采样。6.2 上电后的偏置稳定时间别跳过ST的磁力计在手册里明确提到了一个软启动和偏置稳定时间。很多工程师容易把它忽略因为芯片上电后确实能立刻读到数据看起来“正常工作”但数据里藏了偏置漂移。我在开发阶段做过一次对比实验上电后第1帧数据比稳定后的读数低了将近2%持续100毫秒后才慢慢归位。对于要求不高的方向指示这2%可能无所谓但对精度敏感的罗盘来说这会直接转化为固定角度误差。处理方式很简单系统启动时执行一次延时比如等待50到100毫秒或者通过状态寄存器判断芯片是否就绪。如果是电池供电的休眠唤醒场景每次从睡眠状态退出也要考虑这个问题可以在唤醒后做一次丢弃前N帧的处理。还有一个与电源相关的注意点磁力计对电源噪声比较敏感供电电压跌落或纹波过大会直接体现在数据噪声上。我用万用表和示波器对比过把VDD纹波从50mV降到20mV磁力计数据标准差大约能改善三分之一。所以硬件上尽量给传感器单独加一个小电容滤波固件里也可以通过过采样平均来弥补部分噪声。6.3 量产端的自检与校准策略实验室里怎么调都不如产线能落地。量产电子罗盘产品每个设备都面临元器件贴装偏差、板子局部应力和结构件磁化的差异所以出厂校准是必须的。常见做法是产线用高精度转台让产品绕Z轴旋转360度取各角度的磁力计数据计算offset和scale然后把校准参数写入芯片寄存器或设备的非易失存储区。但实际产线进度紧张很多设备根本不会专门跑转台校准。我的替代方案是“自由空间简易校准”产线员工手持设备画“∞”字20秒算法端实时评估包络覆盖程度覆盖达标后自动计算校准参数并写入。虽然精度比转台略差一点但对大多数穿戴设备来说完全够用。用户使用一段时间后设备会因温度变化、结构轻微形变而出现校准参数漂移所以应用层最好保留一个“重新校准”入口。很多手机导航APP都有类似功能会提示用户摇头晃脑画八字。做固件时可以在检测到罗盘误差持续偏大比如连续多次与GPS航向差异超过10度时主动提示用户重新校准。这个处理既简单又直接能大幅减少售后问题。最后一个量产关键点老化测试和首批成品时一定要用磁干扰检测过的工装台做验证。我遇到过产线的金属工装夹具带磁导致同一批次部分产品校准后数据偏大后来把夹具换成无磁材料才解决。如果产线上有金属传送带或者磁性治具校准前务必确认它们不会出现在设备周围的工作半径内。
返回列表