尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ESKF:IMU与GNSS融合定位的误差状态卡尔曼滤波原理与实践

ESKF:IMU与GNSS融合定位的误差状态卡尔曼滤波原理与实践 1. 项目概述为什么我们需要ESKF在自动驾驶、机器人定位导航这些领域我们经常听到一个词叫“传感器融合”。说白了就是让机器人知道自己到底在哪儿、头朝哪边、速度多快。这事儿听着简单做起来可太难了。你想想一个机器人身上可能装着惯性测量单元IMU、全球导航卫星系统GNSS比如GPS、激光雷达Lidar、摄像头……每个传感器都有自己的脾气和毛病。IMU是个“短跑健将”它通过测量角速度和比力加速度减去重力能非常快地推算出姿态和位置变化但它的毛病是会“漂移”误差会随着时间累积跑得越久偏得越离谱。GNSS比如GPS则像个“路标”它能直接告诉你一个绝对的地理坐标精度高不漂移但它的更新频率慢通常1-10Hz而且在城市峡谷、隧道里信号一丢直接就“失明”了。所以一个很自然的想法就是把IMU和GNSS结合起来用IMU的高频数据在两次GNSS定位之间做“插值”保持定位的连续性再用GNSS的绝对位置信息时不时地“拉”IMU一把纠正它的累积漂移。这个“结合”的过程就是传感器融合的核心。而误差状态卡尔曼滤波器Error State Kalman Filter, ESKF就是实现这种融合的“数学引擎”里目前公认最优雅、最鲁棒的一种。它不像传统的卡尔曼滤波器直接去估计机器人的“全身状态”位置、速度、姿态等而是去估计这些状态的“误差”。这个巧妙的转换让ESKF在处理姿态尤其是用四元数表示的三维旋转这种非线性、有约束的状态时变得异常高效和稳定。简单说ESKF是让IMU和GNSS这对“欢喜冤家”高效协作、取长补短的关键数学模型。接下来我们就一层层剥开它的外壳看看这个引擎到底是怎么工作的。2. ESKF的核心思想与数学模型总览要理解ESKF我们得先看看它要解决什么问题以及它为什么选择“误差”作为估计对象。2.1 状态定义真值、名义值与误差值在ESKF的框架里我们把机器人的状态分成了三部分真值True Statex_t。这是机器人真实的状态但我们永远无法直接获得它是我们估计的目标。名义状态Nominal Statex。这是我们通过IMU的测量数据纯粹由运动学方程“积分”推算出来的状态。它不包含任何误差修正所以会随着IMU的漂移而越来越偏离真值。你可以把它想象成一辆没有GPS校正、只靠内部里程计跑的车跑久了肯定有偏差。误差状态Error Stateδx。这就是真值和名义状态之间的差值δx x_t ⊖ x。这里的“⊖”是一种广义的减法对于位置、速度这种向量就是普通减法对于姿态四元数则是四元数的乘法逆运算。ESKF的核心就是不去直接估计庞大的真值x_t而是去估计这个相对较小的误差δx。为什么这么做是聪明的主要有两大好处线性化友好误差δx通常很小因为名义状态x已经通过IMU积分给出了一个不错的预测在小误差的假设下复杂的非线性系统可以被很好地线性化。这使得卡尔曼滤波的更新步骤最复杂的部分可以在一个简单的线性空间中进行计算量小且数值稳定。姿态处理优雅机器人姿态通常用四元数表示而四元数本身有单位范数的约束||q||1。直接对四元数进行加减和协方差运算会破坏这个约束。而误差状态δθ姿态误差通常用一个三维的旋转向量或等效的角轴来表示它生活在无约束的三维空间中可以自由地进行卡尔曼滤波中的加、减和协方差更新完美避开了约束问题。2.2 ESKF的完整数学模型框架一个完整的ESKF包含三个核心方程预测Propagation和更新Update。下面我们结合IMU和GNSS的具体场景给出完整的数学描述。首先定义我们的状态向量。通常一个用于融合IMU和GNSS的15维误差状态向量定义为δx [δp^T, δv^T, δθ^T, δb_a^T, δb_g^T]^T其中δp三维位置误差。δv三维速度误差。δθ三维姿态误差旋转向量。δb_a三维加速度计零偏误差。δb_g三维陀螺仪零偏误差。对应的名义状态x和真值x_t也包含位置p、速度v、姿态四元数q、加速度计零偏b_a、陀螺仪零偏b_g。2.2.1 预测步骤IMU驱动下的误差状态动力学预测步骤由IMU的高频数据驱动。我们有两件事要做更新名义状态使用IMU的原始测量值含噪声对名义状态进行积分。ω_meas ω_true b_g n_g // 陀螺仪测量值 真值 零偏 噪声 a_meas a_true b_a n_a // 加速度计测量值 真值 零偏 噪声名义状态的运动学方程连续时间为p_dot v v_dot R(q) * (a_meas - b_a) g // R(q)是将载体坐标系下的加速度转到世界坐标系g是重力矢量 q_dot 0.5 * q ⊗ [0; (ω_meas - b_g)] // ⊗是四元数乘法 b_a_dot 0 // 通常建模为零偏为随机游走 b_g_dot 0在实际代码中我们使用数值积分如龙格-库塔法来离散地更新名义状态。更新误差状态的协方差矩阵这是预测步骤的核心。我们需要推导误差状态δx是如何随时间演化的即误差状态方程。 通过对真值、名义状态和误差的定义关系进行微分并在误差δx很小的假设下进行一阶线性化我们可以得到误差状态的连续时间线性动力学方程δx_dot F_c * δx G_c * n其中F_c是误差状态转移矩阵15x15。它包含了姿态旋转矩阵R、测量的加速度、地球自转等项反映了各种误差如速度误差、姿态误差之间是如何耦合、传播的。G_c是噪声驱动矩阵。n是IMU的噪声向量[n_a; n_g; n_ba; n_bg]包括加速度计白噪声、陀螺仪白噪声以及零偏的随机游走噪声。将这个连续时间方程离散化采样周期为Δt得到离散时间的误差状态方程δx_k F_k * δx_{k-1} G_k * n_k其中F_k ≈ I F_c * ΔtG_k是离散化的噪声矩阵。有了这个我们就可以按照卡尔曼滤波的预测公式更新误差状态的协方差矩阵PP_k|k-1 F_k * P_{k-1|k-1} * F_k^T G_k * Q_k * G_k^T这里的Q_k是离散时间的过程噪声协方差矩阵它由IMU噪声特性n_a,n_g等的强度决定。这里就回答了热词中的一个关键问题“imu静止初始化得到的测量方差和eskf中的过程噪声中q之间关系”。在静止初始化时我们通过采集一段静止的IMU数据可以统计出加速度计和陀螺仪输出的方差。这个方差主要反映了IMU的测量白噪声n_a和n_g的强度。而过程噪声矩阵Q正是基于这些噪声的功率谱密度或方差以及零偏随机游走的参数计算出来的。因此静止初始化得到的测量方差是标定和设置ESKF中过程噪声Q矩阵的重要依据。如果Q设置得比实际噪声大滤波器会过于信任IMU修正力度弱如果Q设置得小滤波器会过于信任观测如GNSS导致输出抖动。2.2.2 更新步骤GNSS观测带来的修正当GNSS接收机提供一个新的位置和/或速度观测值时更新步骤被触发。GNSS观测值z例如经纬高或ECEF坐标与我们的状态x_t之间存在一个观测模型z h(x_t) r其中r是GNSS的观测噪声协方差为R。在ESKF中我们不是直接用真值x_t而是用名义状态x和误差状态δx来表示这个关系。由于δx很小我们可以将观测方程在名义状态x处进行一阶线性化z ≈ h(x) H * δx r其中H ∂h/∂x_t |_{x_tx}是观测矩阵在名义状态处的雅可比矩阵。由此我们可以定义观测残差Innovationyy z - h(x)这个残差y就包含了GNSS观测值与当前名义状态预测值之间的差异这个差异理论上应该主要由误差状态δx和观测噪声r引起。接下来就是标准的卡尔曼滤波更新流程但操作对象是误差状态δx及其协方差P计算卡尔曼增益KS H * P_k|k-1 * H^T R // 残差协方差 K P_k|k-1 * H^T * S^{-1}更新误差状态估计δx_k|k K * y注意这里更新的是误差状态的估计值δx。更新误差状态协方差P_k|k (I - K * H) * P_k|k-1最关键的一步注入Injection与重置Reset更新完成后我们得到了估计出的误差δx_k|k。这个误差需要被“注入”到名义状态中以修正名义状态使其更接近真值p - p δp v - v δv q - q ⊗ QuaternionExp(δθ/2) // 将旋转向量δθ转换为四元数增量再与原始四元数相乘 b_a - b_a δb_a b_g - b_g δb_g注入之后名义状态x被更新了。由于误差已经被吸收我们应将误差状态δx重置为零因为此时名义状态已经是最佳估计误差的理论均值应为零。同时误差状态的协方差矩阵P也需要进行相应的变换以反映这个重置操作。这一步是ESKF区别于传统滤波器的标志性操作确保了误差状态始终围绕零值附近波动维持了小误差的假设。注意观测矩阵H的推导需要特别注意坐标系。GNSS天线相位中心的位置p_gnss与IMU中心的位置p_imu通常不重合它们之间有一个杆臂l在载体坐标系下。因此观测方程是h(x_t) p_imu_t R(q_t) * l求雅可比时需要包含对姿态q的导数这又会引入杆臂l的影响。忽略杆臂补偿会导致系统性误差尤其在机器人旋转时。3. ESKF实现中的核心细节与实操要点理论模型搭建好了但要把它变成稳定运行的代码中间有大量的“魔鬼细节”。这部分就是教科书上往往一笔带过但实际工程中却能卡你几天甚至几周的地方。3.1 姿态参数化与误差表示这是ESKF的精华所在也是新手最容易懵圈的地方。名义状态姿态使用四元数q表示。它在积分q_dot 0.5 * q ⊗ ω和旋转向量R(q) * a时非常高效且无奇点。误差状态姿态使用三维旋转向量δθ或等效的角轴表示。其物理意义是名义姿态q绕着一个单位轴u旋转一个微小角度δθδθ δθ * u就得到了真值姿态q_t。数学上表示为q_t ≈ q ⊗ [1; δθ/2]一阶近似。雅可比矩阵中的姿态项在计算误差状态转移矩阵F和观测矩阵H时凡是对姿态求导的地方最终都会转化为对三维误差旋转向量δθ的导数。这里会频繁用到叉积矩阵(·)^∧和旋转矩阵的导数。例如速度方程v_dot R(q) * a g对姿态误差δθ的导数结果是-R(q) * (a)^∧。这个(a)^∧就是把加速度向量a转换成的反对称矩阵。不理解这个F矩阵和H矩阵就写不对。实操心得在代码中为四元数和旋转向量或李代数实现完善的运算库是第一步。推荐使用Eigen库C或类似的数学库。务必验证你的四元数乘法、旋转矩阵转换、指数映射Exp(δθ)和对数映射Log(q)函数的正确性。一个简单的验证方法是随机生成一个旋转用你的函数进行“四元数 - 旋转向量 - 四元数”的转换看结果是否与原始四元数在允许误差内一致。3.2 传感器时间同步与延迟处理IMU频率通常100-500Hz和GNSS频率1-10Hz不同且GNSS数据从接收、解算到被程序获取可能存在几十到上百毫秒的延迟。粗暴地使用最新GNSS数据更新当前状态会导致严重的误差。时间戳必须为每一个IMU数据和GNSS数据打上精确的硬件时间戳例如PPS脉冲同步的时间。缓冲区与插值常见的做法是维护一个IMU数据的历史缓冲区。当收到一个带有延迟t_delay的GNSS观测值时我们不是用它来更新“现在”的状态而是将整个ESKF状态回溯Retrodict到GNSS观测发生的那个历史时刻t_k - t_delay在那个时刻进行卡尔曼滤波更新然后再将状态前向传播Forward Predict回当前时间。这个过程需要利用缓冲的IMU数据重新积分。更工程化的做法是采用反向传播或迭代优化的思想但这在滤波器框架内实现较复杂。一个简化的实用方法是如果延迟固定且较小如100ms以内可以接受一定的性能损失直接将GNSS观测与最近的历史状态进行对齐更新。3.3 初始化静对齐与零偏标定ESKF在开始滤波前需要一个良好的初始状态。静止初始化将设备静止放置数十秒。姿态初始化静对齐假设设备静止那么加速度计测到的唯一外力就是重力。通过测量到的平均比力向量f_avg可以计算出初始俯仰角pitch和横滚角rollroll atan2(-f_y, -f_z),pitch asin(f_x / g)。航向角yaw无法通过加速度计确定可以初始化为0或使用磁力计/初始GNSS航向。零偏初始化计算静止期间陀螺仪输出的平均值作为初始陀螺零偏b_g。计算加速度计输出的平均值减去重力矢量在机体坐标系下的投影可以得到初始加速度计零偏b_a。噪声参数初始化如前所述计算静止数据序列的方差用于设置过程噪声Q。观测噪声R通常由GNSS接收机给出的精度指标如HDOP、VDOP决定。位置和速度初始化直接使用第一个有效的GNSS观测值作为初始位置。初始速度可以设为0或由最初几个GNSS位置差分得到。3.4 异常值处理与自适应滤波GNSS信号并非总是可靠。多径效应、信号遮挡会导致观测出现野值。卡方检验Chi-square test这是最常用的方法。在更新前计算观测残差y和其协方差S构造马氏距离d y^T * S^{-1} * y。理论上d应服从卡方分布。如果d超过某个阈值例如对应95%置信区间的值则认为当前观测是异常值将其拒绝不进行本次更新。自适应噪声调整更高级的策略是监测残差序列。如果连续多次残差都偏大可能不是单次野值而是GNSS整体精度下降如进入城市峡谷此时可以自适应地增大观测噪声协方差R让滤波器更信任IMU。4. 从理论到代码一个简化的ESKF融合流程让我们用一个高度简化的伪代码流程将上述所有环节串联起来。假设我们已经有了完善的数学运算函数四元数、矩阵等。// 1. 初始化 ErrorState eskf; eskf.nominal_state.p first_gnss.position; eskf.nominal_state.v Vector3d::Zero(); eskf.nominal_state.q init_attitude_from_gravity(imu_static_data); eskf.nominal_state.b_a calc_acc_bias(imu_static_data); eskf.nominal_state.b_g calc_gyro_bias(imu_static_data); eskf.error_state.setZero(); // 误差状态初始为0 eskf.P initial_covariance_matrix; // 初始协方差位置速度姿态不确定性大零偏不确定性小 // 2. 主循环 while (running) { // 2.1 IMU数据到达高频 if (new_imu_arrived) { // a. 预测步骤更新名义状态数值积分 double dt imu.timestamp - last_imu_time; eskf.predict_nominal_state(imu.acc, imu.gyro, dt); // b. 预测步骤更新误差状态协方差P MatrixXd F compute_discrete_F(eskf.nominal_state, imu.acc, imu.gyro, dt); MatrixXd G compute_discrete_G(eskf.nominal_state, dt); eskf.P F * eskf.P * F.transpose() G * Q * G.transpose(); last_imu_time imu.timestamp; } // 2.2 GNSS数据到达低频 if (new_gnss_arrived gnss.is_valid) { // a. 计算观测残差 y z - h(x) Vector3d pos_pred eskf.nominal_state.p eskf.nominal_state.q.toRotationMatrix() * lever_arm; // 杆臂补偿 Vector3d y gnss.position - pos_pred; // b. 计算观测矩阵 H dh/d(δx) MatrixXd H compute_observation_matrix(eskf.nominal_state, lever_arm); // c. 卡方检验可选 MatrixXd S H * eskf.P * H.transpose() R; double mahalanobis_dist y.transpose() * S.inverse() * y; if (mahalanobis_dist CHI2_THRESHOLD) { LOG(WARNING) GNSS outlier rejected!; continue; // 跳过本次更新 } // d. 卡尔曼增益和更新 MatrixXd K eskf.P * H.transpose() * S.inverse(); VectorXd delta_x K * y; // 这是误差状态的更新量 δx // e. 注入用δx修正名义状态 eskf.nominal_state.p delta_x.segment3(0); // 位置 eskf.nominal_state.v delta_x.segment3(3); // 速度 // 姿态修正q q ⊗ Exp(δθ/2) Vector3d dtheta delta_x.segment3(6); Quaterniond dq deltaQuaternion(dtheta); eskf.nominal_state.q (eskf.nominal_state.q * dq).normalized(); eskf.nominal_state.b_a delta_x.segment3(9); // 加速度零偏 eskf.nominal_state.b_g delta_x.segment3(12); // 陀螺零偏 // f. 重置误差状态置零并更新协方差P eskf.error_state.setZero(); MatrixXd I MatrixXd::Identity(eskf.P.rows(), eskf.P.cols()); eskf.P (I - K * H) * eskf.P; // 简化的协方差更新严格来说重置后P需要变换 // 更严格的公式P (I - K*H) * P * (I - K*H).transpose() K * R * K.transpose(); // 或者使用Joseph form更新以保持数值对称正定性。 } // 3. 获取当前最优估计名义状态即为注入修正后的状态 current_pose eskf.nominal_state.p; current_orientation eskf.nominal_state.q; }注意上述伪代码省略了大量细节如四元数运算、F和H矩阵的具体计算、协方差重置的严格公式、时间同步处理等。但它清晰地勾勒出了ESKF“预测-更新-注入-重置”的核心循环。5. 常见问题、调试技巧与性能优化即使数学模型和代码流程都清楚了在实际部署中还是会遇到各种奇怪的问题。下面是一些典型的“坑”和排查思路。5.1 滤波器发散或不稳定症状位置、速度估计值开始指数级增长或剧烈振荡协方差矩阵P的对角线元素方差变得异常大或出现负值。排查清单检查噪声参数Q和R这是最常见的原因。Q过程噪声太小滤波器过于信任IMU模型GNSS修正不进去Q太大滤波器过于信任GNSSIMU的高频特性被抑制在GNSS中断时容易漂移。R观测噪声设置不当同理。调试黄金法则信任哪个传感器就把哪个传感器的噪声参数设小。通常先用理论值或标定值再微调。检查F和H矩阵的雅可比计算一个符号错误就可能导致滤波器不稳定。强烈建议使用数值微分进行验证。对于F矩阵可以给某个误差状态一个微小扰动δ分别用运动学方程积分名义状态和扰动后的状态计算数值差分与你自己推导的F矩阵对应列进行比较。检查时间戳和延迟严重的时间不同步会导致更新发生在错误的状态上引入巨大误差。确保所有传感器数据都有精确、同步的时间戳。检查数值稳定性协方差矩阵P必须保持对称正定。在代码中每次更新P后可以强制将其对称化P (P P.transpose()) / 2.0。使用双精度浮点数。在计算卡尔曼增益K时对残差协方差矩阵S进行求逆前检查其条件数避免病态矩阵。检查初始化糟糕的初始姿态特别是航向或过大的初始协方差P可能导致滤波器需要很长时间收敛甚至一开始就发散。5.2 定位输出有系统性偏差症状滤波器稳定但定位结果与真实轨迹存在固定的偏移。排查清单杆臂补偿这是最容易被忽略的误差源务必准确测量GNSS天线相位中心相对于IMU中心的杆臂向量l在载体坐标系下并在观测模型h(x)中正确补偿。补偿错误会导致在转弯时产生周期性位置误差。传感器标定IMU的尺度因子、非正交性误差、g敏感性等未标定。高质量的融合需要事先对IMU进行内参标定热词中的“imu内参和外参标定”。外参标定主要指IMU与相机、激光雷达之间的相对位姿对于纯IMU-GNSS融合最重要的是IMU与GNSS天线之间的杆臂。观测模型误差你是否使用了正确的坐标系GNSS输出通常是WGS-84经纬高或ECEF坐标而你的状态可能是在局部ENU坐标系中。需要正确的坐标转换。另外GNSS天线相位中心与天线底座也有偏移高精度应用中需考虑。未建模的系统误差例如车辆在行驶中IMU并非处于严格的匀速直线运动存在因悬挂和轮胎形变导致的微小振动这些高频运动在IMU积分时会被平滑但可能引入低频偏差。更复杂的模型可能需要考虑这些因素。5.3 性能优化建议稀疏性利用F和H矩阵通常是稀疏的很多零元素。手动推导时就能发现规律。在代码中使用Eigen的稀疏矩阵模块或手动进行分块矩阵运算可以极大提升计算效率这对于资源受限的嵌入式平台尤为重要。异步更新IMU预测步骤频率很高但并非每次预测后都需要进行完整的协方差P更新。可以以稍低的频率如IMU频率的1/10更新P中间只积分名义状态这能节省大量计算量而精度损失很小。考虑姿态运动学中的科氏力对于高速运动的载体如飞机在将比力从机体坐标系转换到惯性坐标系时需要考虑地球自转和载体速度引起的科氏加速度和向心加速度。这需要在速度微分方程v_dot R*a g中增加额外的项。对于地面低速机器人通常可以忽略。与预积分结合在视觉惯性里程计VIO中IMU预积分热词中的“imu预积分”是一个重要概念。它可以将多个IMU测量值累积成一个相对运动约束从而与视觉关键帧同步避免重复积分。ESKF的预测步骤本质上也是一种积分其思想可以与预积分结合在优化框架中发挥更大作用。调试ESKF是一个系统工程。最好的方法是循序渐进先在仿真环境中如MATLAB/Simulink使用已知轨迹和添加了噪声的仿真IMU/GNSS数据验证你的算法和代码确保基础功能正确。然后再上实车/实物数据用真值系统如高精度RTKINS组合导航系统做参考对比分析误差来源。记录下每次更新的残差y、协方差S和卡尔曼增益K的范数绘制成图是分析滤波器行为的强大工具。当你看到滤波器在GNSS信号良好时增益变小更信任预测在GNSS信号丢失时增益变大更依赖IMU但协方差逐渐增长就说明它正在智能地工作。
返回列表