ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

低端MEMS IMU标定实战:基于imu_tk处理零偏与尺度误差

低端MEMS IMU标定实战:基于imu_tk处理零偏与尺度误差 先说一个很多刚入行SLAM或者做多传感器融合的朋友容易忽略的问题你以为买了一块“还凑合”的MEMS IMU比如MPU6050、ICM20602这类消费级/工业级芯片数据就能直接丢给VINS或者LIO-SAM用。结果跑出来的轨迹要么几十秒就漂到天上要么初始化永远不收敛。问题大概率出在你没有做标定。或者说你只是查了一下芯片手册把标称的零偏、尺度填进去了但芯片出厂值和实际值之间的差异远超你的想象。这块内容就是专门写给“手里只有低端MEMS IMU、没有转台、也没有温箱”的工程师和研究生看的。我会用imu_tk这套开源工具带你把加速计和陀螺仪的零偏、尺度因子、轴间非正交误差完整标定一遍并且把结果真正用进VINS或者自己的融合代码里。全程用我实际跑过的流程和踩过的坑来说话不需要精密设备也花不了太多时间。1. 为什么低端MEMS IMU必须要标定1.1 低端IMU的误差远比你想的大低端MEMS IMU最“坑”的地方在于芯片手册上写的参数都是理想条件下的统计值你手里那片芯片的实际参数完全是另一回事。以经典的MPU6050为例手册上陀螺零偏典型值可能是正负3度/秒但实际焊到板子上、通上电之后你采个静态数据算一下均值常常能到正负5甚至10度每秒。这个偏差如果不补偿单靠积分算姿态10秒钟就能偏出几十度。加速度计也一样。除了零偏还有更隐蔽的两个问题尺度因子误差和轴间非正交。尺度因子误差通俗说就是“芯片测出来的加速度变化量和真实变化量不成比例”可能偏大也可能偏小。轴间非正交误差则是MEMS内部三个轴的敏感方向并不是理论上完全垂直的制造过程中光刻、封装都会引入角度偏差这个偏差可以达到1度甚至更多。三个误差叠加在一起你看到的就是“静止放置时三轴输出模长不等于1g而且不同朝向误差都不一样”。所以我经常跟组里的新人说一句话低端IMU拿到手第一件事不是写驱动而是先标定。不标定就拿来融合后面的每一个环节都会被这个“脏数据”拉下水。1.2 不标定在VIO和Lidar SLAM里会出什么问题你可能觉得VIO不是有视觉约束嘛IMU不准也能被视觉拉住吧视觉确实能拉一部分但IMU误差过大会让预积分preintegration的结果在相邻帧之间就跑偏导致视觉和惯性约束对不上最典型的表现就是初始化反复失败、估计器发散或者即使跑起来了轨迹也总有明显的尺度漂移。做Lidar-SLAM也一样。比如LIO-SAM这类系统IMU用来做点云畸变补偿和帧间预测。如果陀螺零偏有一个稳定偏差雷达在扫描过程中按IMU姿态去校正点云每一帧都会引入系统性的点云扭曲你scan-to-map匹配做得再好地图也会一层层“糊”掉。这个现象做激光雷达和IMU联调的人应该都见过imu标定不靠谱地图边缘总是虚的。另外还有一个不太容易归因的问题初始化时估计器给出的重力向量、陀螺零偏初值明显不合理。很多系统在初始化阶段会在线估计IMU零偏但前提是输入误差不能太大。出厂误差太大非线性优化很容易掉进局部最优初始化就崩了。1.3 标定到底在解什么方程既然标定这么重要那imu_tk到底在解什么我们先建立数学模型。对加速度计imu_tk使用的模型可以简化成[ a_{meas} T_a K_a (a_{true} b_a n_a) ]其中(T_a) 是轴间非正交矩阵Misalignment Matrix3x3基本是单位矩阵加上很小的非对角元素(K_a) 是尺度因子矩阵Scale Factor3x3对角矩阵对角线接近1(b_a) 是加速度计零偏向量单位是 m/s^2 或 g(n_a) 是测量噪声。标定的目标就是从一系列已知参考静止时的重力向量中把这些未知矩阵和向量估计出来。陀螺仪的模型更复杂一点[ \omega_{meas} T_g K_g (\omega_{true} b_g) G_s a_{true} n_g ]对比加速度计多了一项 (G_s a_{true})这叫做 g 灵敏度g-sensitivity描述的是加速度对陀螺测量值的影响。低端MEMS里这个效应其实挺明显只是很多人没注意到。imu_tk做的事情就是通过特定的数据采集方法让这些参数变得“可观测”然后用非线性最小二乘把它们求出来。核心思路说穿了并不神秘加速度计标定靠重力作为天然参考陀螺仪标定靠“已知转动角度”作为参考。下一节我再具体展开。2. imu_tk选型与原理深挖2.1 为什么我推荐imu_tk而不是自己写市面上做IMU标定的工具其实不少比如Allan方差工具、Kalibr的imu-utils、码农自己写的最小二乘脚本但真正适合“低端MEMS 没有转台”场景的我首推imu_tk。原因有三个。第一它对设备要求低到极致。Kalibr那套标定虽然精度高但需要高精度的转台或者机械臂配合很多实验室根本没有。imu_tk只要求你手动把IMU摆到几个特定朝向然后静止采集数据就行不需要精确的物理角度信息。第二代码完整、流程闭环。imu_tk自带读取数据的工具、可视化工具还有ROS节点可以从rosbag直接读取IMU消息完成标定不用自己写一堆格式转换脚本。第三它在学术界和工业界都被验证过。这套工具出自苏黎世联邦理工ETH Zurich的ASL实验室就是维护Kalibr和OKVIS那个团队。虽然它不像Kalibr那么“有名”但做惯性导航的老工程师基本都跑过它踩坑的解决方案也相对成熟。2.2 加速度计标定的数学原理加速度计标定的物理基础很简单静止状态下加速度计只感受到一个重力向量模长固定为1g。也就是说不管你把IMU摆成什么姿势测量值的模长理论上都应该等于1g。利用这个约束你摆放多个不同的朝向就能反推出误差参数。数学上看当我们忽略噪声项后每个静止位置都有这样的约束[ | T_a K_a^{-1} (a_{meas} - b_a) |^2 g^2 ]对于一个未知参数集合 ({T_a, K_a, b_a})我们需要让所有静止样本都满足这个模长约束。imu_tk里把它转换成一个非线性最小二乘问题目标函数可以写成[ J(\theta) \sum_{i} \left( | T_a K_a^{-1} (a_{meas,i} - b_a) |^2 - g^2 \right)^2 ]然后用Ceres Solver来优化求解。这里有个很重要的直觉如果你只用一个姿势静止方程是欠定的解不出来。所以必须用多个姿态imu_tk官方推荐的“六面法”就是这个原理后面我会详细讲。我个人在用imu_tk时的经验六面之外可以再加几个对角朝向多采几组数据不会浪费时间反而能让尺度因子和轴间角的估计稳定很多。尤其是芯片装在PCB上之后贴片应力引起的非正交误差比你想象的大。2.3 陀螺仪标定的数学原理陀螺仪标定比加速度计麻烦一些因为没有“天然重力”可以直接当参考。地球自转角速度大约15度/小时低端MEMS的噪声完全盖过了这个信号所以你不可能像光纤陀螺那样直接对地球自转求零偏。imu_tk的陀螺标定思路是“已知角度对比法”。大致流程是将IMU静止一段时间然后绕某一个轴旋转一个已知角度比如90度或180度再静止一段时间。静止期间陀螺输出的均值就是当前角速度零偏。而在旋转期间对陀螺读数做积分能得到旋转角度。把积分角度与已知旋转角度对比就能估计出陀螺的尺度因子和轴间不对准。具体到模型陀螺仪校准时的观测值是角速度 (\omega_{meas})我们已知两个静止时刻之间的相对旋转 (\Delta R_{ref})。把陀螺积分得到的旋转 (\Delta R_{gyro}) 与参考值求误差优化目标就是让这个误差最小。同时旋转之前的静止段用于估计零偏 (b_g)旋转之后的静止段用于验证积分结果。这里有个操作要点旋转过程要尽量匀速、缓慢避免过大的角加速度。低端MEMS的陀螺对角加速度非常敏感暴力甩动不仅会引入很大的积分误差还可能让量程饱和。我第一次做的时候没注意用手快速拨了一下板子标定结果直接飞了。2.4 imu_tk不能帮你做什么用imu_tk标完后有一件事你必须清醒它标的是“确定性的误差”比如零偏、尺度、轴间角。但MEMS IMU还有一大类“随机误差”比如角度随机游走Angle Random Walk和零偏不稳定性Bias Instability这些东西随时间是统计变化的imu_tk不负责处理。随机误差需要靠Allan方差分析来刻画。很多做VINS的人会忽略这一步直接用imu_tk的标定结果填参数结果预积分的协方差设置完全不合理。我建议你把Allan方差也跑一遍把角度随机游走和零偏不稳定性的量级测出来这在后面调VINS的imu噪声参数时会特别有用。另外温度漂移是低端MEMS的一个大坑。imu_tk假设标定过程中温度恒定但实际使用中芯片温度会变化零偏和尺度都会跟着漂。这个imu_tk管不了需要你去做温度补偿实验或者在算法层面把温度项加进观测模型。我的态度是低端MEMS不要指望标定一次一劳永逸至少要在不同温度下多标几次看看零偏的变化范围心里有个底。3. 标定前的准备与数据采集实操3.1 硬件准备与采集环境先列一下我在实操中用的硬件清单方便你对照IMU模块一块确保能通过串口、SPI或者I2C稳定输出原始数据。我用过的有MPU6050、ICM20602、BMI088都跑通了一个稳定的供电环境。强烈建议不要用电池直接供电尤其是那种电量不足的锂电池电压波动会直接反映在IMU噪声上如果IMU是单独小模块备一个水平泡或者简单的直角块方便摆出比较正的六面如果要采ROS bag还需要一个能跑到100Hz以上的串口转USB适配器CH340和CP2102都可以CP2102更稳一些。采集环境上你最好找一张稳固的桌面把IMU放在减震垫或者泡沫棉上。有人觉得静止就是随便一放这里我想强调低端MEMS对微振动很敏感哪怕有人在你旁边走动地面传来的微小震动也会被当成加速度记录下来。条件允许的话关门、关空调、人别到处走。另外一个容易被忽略的细节是IMU的固定方式。如果你的IMU是单独板子背后有排针建议把排针焊牢或者用热熔胶固定在底座上。千万不要用那种软绵绵的杜邦线悬空接采集过程中线一弹整个朝向就变了数据直接废掉。3.2 安装imu_tk和依赖imu_tk的安装说简单也简单说坑也有坑。最省事的路径是这样的。先装依赖。imu_tk依赖Eigen3、Ceres Solver、glog、gflags这些库。在Ubuntu 18.04或者20.04上我推荐用apt直接装大部分sudo apt-get install libeigen3-dev libgoogle-glog-dev libgflags-devCeres Solver比较关键。Ubuntu源里的版本可能偏老但多数情况够用。如果不放心可以去Ceres官网编译最新版。官方教程里的命令大概是这样sudo apt-get install libceres-dev编译imu_tk本体git clone https://github.com/ethz-asl/imu_tk.git cd imu_tk mkdir build cd build cmake .. make -j$(nproc)如果你只用命令行工具编译完之后在build/bin目录下会有test_imu_calib。如果你想直接处理ROS bag还需要test_imu_calib_ros_node这就需要额外的ROS依赖比如cv_bridge、sensor_msgs建议先装好ROS再编译。这里提醒一个我踩过挺久的坑如果系统里同时装了多个版本的Eigen或者CeresCMake可能会抓到不匹配的那个编译期或者运行期出现莫名其妙的段错误。解决办法是把3rdparty目录下的Eigen强制加上或者在CMakeLists里手工指定EIGEN3_INCLUDE_DIR指向你要用的版本。3.3 采集ROS bag的正确姿势如果用ROS节点做标定数据采集可以直接录bag。我习惯的做法是写一个launch文件把IMU的topic重映射成/imu/data然后手动录制。录制之前先确认几件事IMU的发布频率我建议至少100Hz。频率太低会导致积分分辨率不够直接影响陀螺标定精度输出坐标系。imu_tk默认按x-y-z顺序处理如果你的IMU驱动里做过坐标变换务必在数据上标注清楚后面读结果时要对应起来量程设置。加速度计量程设成正负4g就够了别设太小导致旋转时饱和陀螺量程设成正负250度/秒或500度/秒都行。采bag的时候我一般按这样一个时间段分配水平静止120秒依次摆到六个面每个面静止30到60秒再补两三个对角朝向各静止30秒然后做陀螺标定数据采集这个单独录一段避免和加速计混在一起处理。关于为什么要静止那么久零偏估计需要长时间平均来压低随机噪声静止时间越长零偏估计越准。30秒是底线60秒更稳。3.4 数据预处理和格式转换如果你不用ROS直接用命令行工具imu_tk也需要一个固定的文本格式来读数据每行六个数前三列是加速度单位可以是g或m/s^2但要一致后三列是角速度单位是度/秒或弧度/秒。如果你只有RAW格式最好写个小脚本转一下。我自己常用这样一个小的Python片段把CSV转成imu_tk需要的格式import csv import sys def convert_csv_to_imu_tk(input_csv, output_txt): with open(input_csv, r) as f_in, open(output_txt, w) as f_out: reader csv.reader(f_in) for row in reader: if len(row) 6: continue # 前3列是acc后3列是gyro单位统一成m/s^2和rad/s ax, ay, az float(row[0]), float(row[1]), float(row[2]) gx, gy, gz float(row[3]), float(row[4]), float(row[5]) f_out.write(f{ax:.9f} {ay:.9f} {az:.9f} {gx:.9f} {gy:.9f} {gz:.9f}\n) if __name__ __main__: convert_csv_to_imu_tk(sys.argv[1], sys.argv[2])如果你数据里有时间戳就丢掉那一列imu_tk不在意时间戳。注意一个问题单位不能混。我见过有人加速度计用mg陀螺用度每秒结果标定出来的尺度因子完全离谱。建议统一成 m/s^2 和 rad/s或者都在代码里按同一规则转换。4. 完整标定流程与结果解读4.1 加速度计标定步骤实操加速度计标定在imu_tk里是单独完成的。如果用命令行工具流程大致是加载数据文件选择静止段然后运行优化。拿ROS节点来说命令行形式类似rosrun imu_tk test_imu_calib_ros_node它会开始订阅/imu/data并实时分析。为了更可控我更推荐离线方式先录bag再用test_imu_calib加载文本数据。这样你可以反复调整静止段判定阈值不必重新采数据。静止段检测上imu_tk内部会用滑窗统计加速度计输出的方差低于阈值就认为是静止。阈值设太严静止段太少优化不稳定阈值设太松把微小晃动也算成静止数据污染。我的实践经验是加速度计静止方差阈值设在 0.01 m/s^2 左右窗口长度选 1 到 2 秒比较靠谱。标定完成后imu_tk会打印出零偏、尺度因子以及3x3的变换矩阵。这个矩阵的物理含义是“将原始读数映射到真实值”。举个例子打印出的acc_misalignment如果是1.0000 0.0087 -0.0123 -0.0065 0.9952 0.0151 0.0102 -0.0099 1.0032对角线旁边的0.01级别数值就代表轴间非正交误差。这个量级在低端MEMS里非常常见完全不可忽略。4.2 陀螺仪标定步骤实操陀螺仪的标定步骤比加速度计要更有“仪式感”因为数据采集方式决定了标定成败。imu_tk陀螺标定的核心操作序列我一般这样安排先把IMU摆在一个稳定位置静止60秒然后绕X轴旋转90度正方向旋转过程控制在2到3秒完成尽量匀速达到角度后静止30秒继续绕X轴转回原位再静止30秒换Y轴、Z轴重复上述操作每个轴来回两三趟。采集完数据运行imu_tk的陀螺标定它会自动识别静止段和运动段然后用积分角度估计误差参数。最终会输出陀螺的零偏、尺度因子和misalignment矩阵。这里有个细节我要重点提醒陀螺标定对“旋转后静止”的要求极高。如果转完角度后你没有稳住还在来回微调imu_tk有可能会把微调过程当成旋转段的一部分导致积分角度出错。我的习惯是旋转结束后立刻稳住板子宁可角度稍微过头一点也别来回摆动。反正imu_tk不要求精确的90度它会把实际旋转角度作为未知量一并估计。4.3 标定输出参数怎么看标定完成后imu_tk会打印类似下面的参数加速度计零偏acc_bias单位通常在m/s^2加速度计尺度acc_scale对角线三个数接近1加速度计misalignment3x3矩阵表示轴间非正交陀螺零偏gyro_bias单位通常是rad/s陀螺尺度gyro_scale三个对角线值陀螺misalignment3x3矩阵。拿到这些之后怎么验证合理性我常用的方法很简单用标定出的矩阵把原始数据重新补偿一遍然后静态拿着IMU看补偿后三轴重力模长是不是稳定在9.8附近或者说1g附近。如果某个朝向误差依然超过0.02g说明标定结果有问题或者采集时那个朝向的数据本身有瑕疵。另一个验证是零偏量级。对低端MEMS来说加速度计零偏如果标出来超过0.2 m/s^2说明芯片比较差但仍在合理范围如果标出来0.5以上建议先检查是不是数据单位弄错了。4.4 把标定结果用进VINS或LIO系统标定完不等于结束关键是把补偿参数用起来。在VINS-Mono里IMU原始数据一般是先经过驱动补偿再进入预处理线程。如果你用的是标准VINS代码可以在IMU回调函数里加入补偿步骤。以C为例我一般这样写Eigen::Vector3d acc_raw(acc_msg-linear_acceleration.x, acc_msg-linear_acceleration.y, acc_msg-linear_acceleration.z); Eigen::Vector3d gyr_raw(gyr_msg-angular_velocity.x, gyr_msg-angular_velocity.y, gyr_msg-angular_velocity.z); // 补偿加速度计 Eigen::Matrix3d Ta_inv acc_misalignment.inverse(); Eigen::Matrix3d Ka_inv acc_scale.asDiagonal().inverse(); Eigen::Vector3d acc_corrected Ta_inv * Ka_inv * (acc_raw - acc_bias); // 补偿陀螺仪 Eigen::Matrix3d Tg_inv gyr_misalignment.inverse(); Eigen::Matrix3d Kg_inv gyr_scale.asDiagonal().inverse(); Eigen::Vector3d gyr_corrected Tg_inv * Kg_inv * (gyr_raw - gyr_bias); // 替换进原消息注意一点VINS的IMU数据接口有的是加速度单位是m/s^2有的是g一定要确认好。再一个如果你的原始数据里陀螺用的是度每秒驱动里一般会转成弧度每秒标定参数要以转换后的单位为基准。在LIO-SAM这类系统里补偿逻辑一模一样只是IMU回调位置不同。把补偿后的数据送进预处理后续的姿态预测和畸变校正都会更稳。我在实际测试中标定前LIO-SAM在室内跑一圈点云地图糊得不成样子标定后明显清晰轨迹闭环误差也小了一个量级。5. 实战中踩过的坑与验证技巧5.1 数据采集阶段的坑很多标定失败的问题不是算法不行而是数据采集阶段就埋了雷。我把踩过的坑整理一下。第一个坑是“静止不够静”。我之前在一个通风良好的实验室里采数据空调出风口正好对着桌面整个桌面都在微微震动。IMU虽然看起来静止但加速度计方差一直居高不下imu_tk把好几个“伪静止段”都剔除了最后可用样本太少标定结果特别飘。解决办法很简单换个没风的地方或者用厚书本垫高IMU减少空气流动影响。第二个坑是USB供电不稳。用树莓派或者车载USB口给IMU供电时电压毛刺会导致IMU读数异常尤其是陀螺仪会出现明显的直流偏移突变。标定前最好用示波器看下电源纹波没有示波器的话可以先用电池或者线性稳压供电试试。第三个坑是旋转数据采集时手法太粗糙。不要用手直接捏着IMU旋转这样手指热量会影响芯片温度而且手抖会导致角加速度过大。比较靠谱的做法是把IMU固定在立方体亚克力块上再用手握住立方体旋转这样能大幅减小抖动。5.2 标定结果验证静态与动态标定完成后光看输出参数还不够我强烈建议做一套验证实验。先做静态验证IMU静止不动采集一分钟数据用标定参数补偿后看三轴合力的均值是否在1g附近误差小于0.01g同时看陀螺三轴的输出去偏后是否接近0且波动范围在噪声量级内。再做动态验证手拿IMU做几次简单的旋转比如绕Y轴转180度然后回到原位。用标定后的陀螺积分看姿态是否还能回到初始位置。如果存在明显的角度回退误差大概率是陀螺尺度因子还不准或者标定过程中数据本身有瑕疵。最后一个更全面的验证是联合验证把标定后的IMU数据喂进VINS-Mono或者LIO-SAM在同一个场景里跑两遍对比。标定后系统的初始化时间应该明显缩短行驶轨迹的漂移也小很多。我在做多传感器融合项目时通常以“初始化成功率和5分钟轨迹漂移量”作为标定效果的最终评判标准。5.3 排查思路速查表结合我几次帮同事排查标定问题的经验列一个简单的速查表方便你遇到问题时快速定位。现象可能原因排查思路标定后静止合力偏离1g较大采集数据中混入振动或六面位置不够正检查静止段检测结果补采六面并增加静止时长陀螺标定角度误差很大旋转段积分不准或不满足匀速要求重新采集旋转数据旋转更慢、更匀速静止段加长优化不收敛或结果跳变静止段太少或数据单位不统一增加静止位置统一单位为m/s^2和rad/s标定参数在VINS里效果变差坐标系定义不一致或补偿公式写错对照imu_tk输出矩阵的含义检查补偿顺序零偏标定值明显不合理供电不稳或芯片温度未稳定等芯片预热后再采数据检查供电质量如果你遇到的现象不在表里可以再回去翻一下bag数据把IMU原始波形画出来看一眼。很多问题看一眼时域波形就能猜个八九不离十。5.4 一个小技巧用Allan方差补充随机误差分析前面提到过imu_tk只管确定性误差随机误差要靠Allan方差。我在实际项目中标定完成之后还会顺手把静态数据跑一遍Allan方差分析重点看两个指标角度随机游走ARW和零偏不稳定性。这两个指标会直接影响VINS里噪声参数 (\sigma_g) 和零偏随机游走 (\sigma_{bg}) 的设置。如果你发现自己调VINS的IMU参数总是调到极限还不稳定不妨先测一下Allan方差用实测数据去填参数而不是从别人的配置里抄。我在这里分享一个从零开始的实践用imu_tk完成确定性标定用Allan方差给出随机误差两者叠加基本上就能把一颗低端MEMS IMU的“脾气”摸透了。之后再进VINS、LIO-SAM或者任何其他多传感器系统你的IMU数据才真正算得上“干净可用”。我在实际跑项目时体会最深的一点是标定这件事花一两个小时做一遍能帮你省下后面至少一个周的调参时间。很多人宁可把精力花在改代码上也不愿意回头处理最原始的传感器数据这其实是本末倒置了。数据质量是融合算法性能的天花板imu_tk就是帮你把这层天花板抬起来的最省力工具。
返回列表