深入GTSAM源码:IMU预积分因子在优化器中是如何计算残差和雅可比的?

张开发
2026/4/5 23:43:08 15 分钟阅读

分享文章

深入GTSAM源码:IMU预积分因子在优化器中是如何计算残差和雅可比的?
深入GTSAM源码IMU预积分因子在优化器中如何计算残差和雅可比在SLAM系统的开发过程中理解优化器内部工作机制往往比单纯调用API更为关键。当我们使用GTSAM这样的库时经常会遇到一个现象代码运行良好但一旦需要调试或性能优化就会因为对底层机制的不了解而陷入困境。特别是IMU预积分这种涉及复杂物理模型和数学推导的组件其优化过程更像是一个黑盒子。1. GTSAM优化框架的核心机制GTSAM的优化过程本质上是一个因子图Factor Graph的求解过程。在这个框架中每个IMU预积分因子都代表了一组约束条件告诉优化器这些状态变量之间应该满足这样的物理关系。但真正让优化器工作的是这些因子能够准确计算当前状态假设下的误差残差以及这个误差如何随状态变化而变化雅可比矩阵。1.1 从非线性优化到线性化系统当调用LevenbergMarquardtOptimizer.optimize()时GTSAM实际上在重复执行以下步骤在当前估计值处线性化所有因子构建线性系统求解线性系统得到状态更新应用更新并判断收敛这个过程中最关键的环节就是线性化而线性化的核心就是计算每个因子在当前状态下的残差和雅可比矩阵。对于IMU预积分因子ImuFactor这个过程涉及多个层次的调用// 伪代码展示调用链 optimizer.optimize() → graph.linearize() → factor.linearize() → factor.unwhitenedError() → preintegrated.computeErrorAndJacobians()1.2 并行线性化架构GTSAM采用了一种高效的并行线性化策略。在NonlinearFactorGraph::linearize()中可以看到// 使用TBB进行并行线性化 tbb::parallel_for(tbb::blocked_rangesize_t(0, size()), _LinearizeOneFactor(*this, linearizationPoint, *linearFG));这种设计使得IMU预积分因子的线性化可以与其他类型的因子如视觉因子、GPS因子等并行处理显著提升了大规模SLAM问题的求解效率。2. IMU预积分因子的数学本质IMU预积分技术的核心价值在于它将原本与全局状态相关的IMU积分转换为相对测量从而避免在每次优化迭代时重新积分所有IMU数据。这种转换带来了计算效率的大幅提升但也使得误差和雅可比的计算变得更为复杂。2.1 预积分量的物理意义在连续时间内IMU预积分量可以表示为$$ \Delta R_{ij} \prod_{ki}^{j-1} \text{Exp}((\tilde{\omega}_k - b_k^g)\Delta t) $$$$ \Delta v_{ij} \sum_{ki}^{j-1} \Delta R_{ik} (\tilde{a}_k - b_k^a)\Delta t $$$$ \Delta p_{ij} \sum_{ki}^{j-1} [\Delta v_{ik}\Delta t \frac{1}{2}\Delta R_{ik} (\tilde{a}_k - b_k^a)\Delta t^2] $$这些量描述了从时刻i到j的相对运动仅依赖于IMU测量值和偏置与全局状态无关。2.2 残差函数的构建在GTSAM中IMU因子的残差通常包含以下几部分姿态残差比较预积分旋转与状态变量旋转的差异速度残差比较预积分速度与状态变量速度的差异位置残差比较预积分位置与状态变量位置的差异偏置残差约束偏置变化的缓慢特性这些残差的计算实现在PreintegratedImuMeasurements::computeErrorAndJacobians()中是IMU因子最核心的函数之一。3. 残差与雅可比的计算实现深入到GTSAM源码我们可以追踪到IMU预积分因子计算残差和雅可比的具体实现路径。3.1 调用链的完整解析当优化器开始工作时完整的计算调用链如下优化器入口LevenbergMarquardtOptimizer::optimize()线性化因子图NonlinearFactorGraph::linearize()因子线性化NoiseModelFactor::linearize()计算原始误差ImuFactor::unwhitenedError()预积分计算PreintegratedImuMeasurements::computeErrorAndJacobians()其中最关键的一步是unwhitenedError函数它同时计算残差和雅可比矩阵Vector ImuFactor::unwhitenedError( const Values x, boost::optionalstd::vectorMatrix H) const { // 获取相关状态变量 Pose3 pose_i x.atPose3(key1()); Vector3 vel_i x.atVector3(key2()); Pose3 pose_j x.atPose3(key3()); Vector3 vel_j x.atVector3(key4()); imuBias::ConstantBias bias x.atimuBias::ConstantBias(key5()); // 调用预积分类计算误差和雅可比 return preintegrated_-computeErrorAndJacobians( pose_i, vel_i, pose_j, vel_j, bias, H); }3.2 computeErrorAndJacobians的实现细节computeErrorAndJacobians函数是真正的数学计算核心。其主要步骤包括偏置校正应用当前偏置估计校正预积分量误差计算计算姿态、速度、位置的预测值与实际值之间的差异雅可比计算计算误差对各状态变量的导数以下是该函数的简化逻辑Vector9 PreintegratedImuMeasurements::computeErrorAndJacobians( const Pose3 pose_i, const Vector3 vel_i, const Pose3 pose_j, const Vector3 vel_j, const imuBias::ConstantBias bias, OptionalJacobian15,15 H) const { // 1. 计算偏置校正后的预积分量 Vector9 biasCorrected biasCorrectedDelta(bias); // 2. 计算预测状态 NavState predicted predict(pose_i, vel_i, bias); // 3. 计算误差项 Vector3 errorR Logmap(predicted.attitude().between(pose_j.attitude())); Vector3 errorV vel_j - predicted.velocity(); Vector3 errorP pose_j.position() - predicted.position(); // 4. 计算雅可比矩阵如有需要 if (H) { // 这里会填充H矩阵各块 // 包括对pose_i, vel_i, pose_j, vel_j, bias的导数 } return (Vector(9) errorR, errorV, errorP).finished(); }3.3 雅可比矩阵的结构特点IMU预积分因子的雅可比矩阵具有特定的稀疏结构。对于一个典型的IMU因子连接两个连续时刻的状态变量其雅可比矩阵可以分块表示为变量块维度说明pose_i6×6误差对第i帧位姿的导数vel_i3×3误差对第i帧速度的导数pose_j6×6误差对第j帧位姿的导数vel_j3×3误差对第j帧速度的导数bias6×6误差对IMU偏置的导数这种稀疏性在大型SLAM问题中尤为重要GTSAM会利用这种结构特性来高效地求解线性系统。4. 噪声模型与白化过程在GTSAM中噪声模型不仅用于加权不同传感器数据还参与线性化过程中的白化操作这对IMU预积分因子的计算有重要影响。4.1 噪声模型的角色IMU预积分因子的噪声模型通常来自IMU测量噪声的传播。在PreintegratedImuMeasurements类中噪声协方差矩阵会随着预积分过程不断更新void PreintegratedImuMeasurements::integrateMeasurement( const Vector3 measuredAcc, const Vector3 measuredOmega, double dt) { // 更新预积分量 // ... // 更新噪声协方差矩阵 Matrix9 A; // 状态转移矩阵 Matrix93 B, C; // 噪声传播矩阵 update(measuredAcc, measuredOmega, dt, A, B, C); covariance_ A * covariance_ * A.transpose() B * noiseModelGyro_ * B.transpose() C * noiseModelAccel_ * C.transpose(); }4.2 白化系统的构建在NoiseModelFactor::linearize()中我们可以看到噪声模型如何参与构建线性化系统if (noiseModel_) noiseModel_-WhitenSystem(A, b);这一步将原始残差和雅可比矩阵转换为白化形式使得不同因子的误差能够按照其置信度合理加权。对于IMU预积分因子这意味着IMU测量的不确定性被正确纳入优化过程。5. 性能优化与实践建议理解IMU预积分因子在优化器中的计算方式后我们可以针对性地进行性能优化和调试。5.1 避免重复计算在自定义因子时一个常见错误是在unwhitenedError和linearize中重复计算相同的中间结果。GTSAM的ImuFactor通过将核心计算集中在computeErrorAndJacobians中来避免这个问题。5.2 利用雅可比稀疏性当实现自定义IMU因子时应该注意只计算非零的雅可比块使用OptionalJacobian模板避免不必要的计算利用Eigen的块操作来简化矩阵填充5.3 调试技巧调试IMU预积分因子时可以检查预积分量的数值合理性验证残差在静态情况下是否趋近于零比较数值微分与解析雅可比的一致性监控协方差矩阵的条件数// 示例数值验证雅可比矩阵 Matrix numericalH numericalDerivative11Vector9, NavState( boost::bind(computeError, _1, pose_j, vel_j, bias), NavState(pose_i, vel_i)); Matrix analyticH ...; // 从computeErrorAndJacobians获取 assert((numericalH - analyticH).norm() 1e-5);6. 与因子图优化的协同作用IMU预积分因子在完整SLAM系统中的作用不仅取决于其自身的实现还与它在因子图中的交互方式密切相关。6.1 多传感器融合中的角色在典型的紧耦合SLAM系统中IMU预积分因子与其他传感器因子共同构成优化问题视觉/激光因子提供绝对或相对位姿约束IMU预积分因子提供连续帧间的运动学约束偏置随机游走因子约束IMU偏置的变化这种组合有效地结合了不同传感器的优势高频IMU数据提供了良好的短期运动估计而视觉/激光数据则抑制了长期漂移。6.2 边缘化与滑动窗口在实际系统中为了控制计算复杂度通常会采用滑动窗口策略。这时IMU预积分因子的处理需要特别注意当边缘化掉旧状态时需要将IMU预积分量正确地转换为先验因子新的预积分应该从滑动窗口中最新的状态开始需要考虑被边缘化状态与保留状态之间的相关性GTSAM的Marginals类和ISAM2优化器提供了相关工具但正确使用它们需要对IMU预积分因子的数学性质有深入理解。7. 高级话题与前沿进展随着SLAM技术的发展IMU预积分因子的实现也在不断演进。以下是一些值得关注的方向7.1 基于流形的优化现代SLAM系统越来越重视流形结构的正确处理。对于IMU预积分旋转部分应使用SO(3)流形上的运算使用指数映射和对数映射处理旋转误差雅可比矩阵应考虑流形局部参数化GTSAM通过Pose3和Rot3类提供了这些功能但正确使用它们需要理解背后的数学原理。7.2 不同IMU模型的适配虽然大多数实现使用简单的IMU噪声模型但某些应用场景可能需要考虑IMU尺度因子和轴偏差使用更复杂的偏置模型处理IMU与相机/激光雷达之间的时间同步误差这些扩展通常需要修改预积分的核心计算逻辑并重新推导误差和雅可比的计算公式。7.3 与深度学习方法的结合近年来一些工作尝试将学习方法引入IMU处理使用神经网络学习IMU噪声特性端到端学习预积分量的计算学习优化过程中的权重分配这些方法通常需要在传统预积分框架基础上进行扩展可能影响因子在优化器中的计算方式。

更多文章