SensorsCalibration 中的 Ceres 求解器实战指南:线性求解器选择、雅可比评估与 FullReport 性能诊断

发布时间:2026/10/15 2:29:05
SensorsCalibration 中的 Ceres 求解器实战指南:线性求解器选择、雅可比评估与 FullReport 性能诊断
自动驾驶计算机视觉【免费下载链接】SensorsCalibrationOpenCalib: A Multi-sensor Calibration Toolbox for Autonomous Driving项目地址https://gitcode.com/gh_mirrors/se/SensorsCalibration点击查看免费下载导读本文以 SensorsCalibration 仓库OpenCalib 多传感器标定工具箱内置的 Ceres Solver 文档《Solving FAQs》为主体讲解非线性最小二乘求解中最关键的三类问题——如何评估已求解问题的雅可比Jacobian、如何为问题选择合适的线性求解器以及如何用Solver::Summary::FullReport定位运行时瓶颈。文中以仓库内lidar2car激光雷达标定车辆外参模块中真实使用 Ceres 的里程计优化代码为佐证读完你可以独立完成 Ceres 问题的求解器选型、性能分析与调优。一、背景Ceres 在 SensorsCalibration 中的角色SensorsCalibrationOpenCalib是一个面向自动驾驶的多传感器标定工具箱覆盖相机内参标定、相机/激光雷达/雷达/IMU 之间的外参标定、环视相机标定等。在这些任务中几乎所有标定问题最终都被建模为非线性最小二乘优化问题而 Ceres Solver 正是仓库所依赖的核心优化引擎。本仓库在SensorX2car/lidar2car/下内置了完整的 Ceres 源码一份用于编译的副本位于 3rdparty/ceres对应的文档源码位于SensorX2car/lidar2car/ceres/docs/source/因此下述所有接口、选项和诊断方法都可以直接在仓库源码中追溯验证。本文聚焦的关联文档为SensorX2car/lidar2car/ceres/docs/source/solving_faqs.rst其内容涵盖了 Ceres 求解阶段的高频 FAQ。从源码结构看lidar2car的激光里程计模块fastloam是把 Ceres 用在实际标定管线中的典型例子见 odomEstimationClass.cppceres::LossFunction *loss_function new ceres::HuberLoss(0.1); ceres::Problem::Options problem_options; ceres::Problem problem(problem_options); problem.AddParameterBlock(parameters, 7, new PoseSE3Parameterization()); addEdgeCostFactor(downsampledEdgeCloud, laserCloudCornerMap, problem, loss_function); addSurfCostFactor(downsampledSurfCloud, laserCloudSurfMap, problem, loss_function); ceres::Solver::Options options; options.linear_solver_type ceres::DENSE_QR; options.max_num_iterations 4; options.minimizer_progress_to_stdout false; options.check_gradients false; options.gradient_check_relative_precision 1e-4; ceres::Solver::Summary summary; ceres::Solve(options, problem, summary);这段代码展示了 Ceres 求解的标准三步流程构造Problem→ 设置Solver::Options→ 调用ceres::Solve。其中options.linear_solver_type ceres::DENSE_QR正是本文第二部分要深入讨论的核心决策。二、如何评估已求解问题的雅可比矩阵Problem::Evaluate是在求解完成后评估雅可比的标准接口。对于已经Solve过的问题你可以调用该接口取出每个残差块对应的雅可比矩阵用于验证优化结果、做敏感性分析或检查梯度是否正确。在实际的lidar2car中雅可比并非全部交给 Ceres 数值微分而是由开发者手写解析雅可比见 lidarOptimization.cpp。例如线特征残差点到直线的距离的EdgeAnalyticCostFunction::Evaluatebool EdgeAnalyticCostFunction::Evaluate(double const *const *parameters, double *residuals, double **jacobians) const { Eigen::Mapconst Eigen::Quaterniond q_last_curr(parameters[0]); Eigen::Mapconst Eigen::Vector3d t_last_curr(parameters[0] 4); Eigen::Vector3d lp q_last_curr * curr_point t_last_curr; Eigen::Vector3d nu (lp - last_point_a).cross(lp - last_point_b); Eigen::Vector3d de last_point_a - last_point_b; double de_norm de.norm(); residuals[0] nu.norm() / de_norm; if (jacobians ! NULL jacobians[0] ! NULL) { // 基于 SE(3) 左扰动模型的解析雅可比 Eigen::Matrix3d skew_lp skew(lp); Eigen::Matrixdouble, 3, 6 dp_by_se3; dp_by_se3.block3,3(0,0) -skew_lp; (dp_by_se3.block3,3(0, 3)).setIdentity(); Eigen::MapEigen::Matrixdouble, 1, 7, Eigen::RowMajor J_se3(jacobians[0]); J_se3.setZero(); Eigen::Matrix3d skew_de skew(de); J_se3.block1,6(0,0) - nu.transpose() / nu.norm() * skew_de * dp_by_se3 / de_norm; } return true; }同文件中SurfNormAnalyticCostFunction点到平面距离残差同样采用手写解析雅可比而PoseSE3Parameterization则通过Plus/ComputeJacobian实现 SE(3) 流形上的局部参数化7 维位姿 6 维扰动。对实践的启示Ceres 官方 FAQ 建议用Problem::Evaluate检查雅可比而仓库代码则展示了另一种更彻底的验证手段——在Solver::Options中开启options.check_gradients true让 Ceres 把解析雅可比与数值雅可比逐项比对。odomEstimationClass.cpp中保留的gradient_check_relative_precision 1e-4即为此机制的相对精度阈值。两者的适用场景不同前者面向求解后的验证与提取后者面向求解前的调试。三、如何选择正确的线性求解器当使用TRUST_REGION信赖域最小化器时线性求解器linear solver的选择是求解质量和运行时间的决定性因素。LinearSolverType的完整枚举定义可在 include/ceres/types.h 中找到enum LinearSolverType { DENSE_NORMAL_CHOLESKY, // 稠密法方程 Cholesky基于 Eigen DENSE_QR, // 稠密法方程 QR基于 Eigen SPARSE_NORMAL_CHOLESKY, // 稀疏法方程 Cholesky需要 SuiteSparse 或 CXSparse DENSE_SCHUR, // 稠密 Schur 补基于 Eigen SPARSE_SCHUR, // 稀疏 Schur 补基于 CHOLMOD ITERATIVE_SCHUR, // 共轭梯度 Schur 补面向大规模问题 CGNR // 法方程上的共轭梯度 };3.1 官方推荐的选择路径Solver::Options::linear_solver_type的默认值定义在 include/ceres/solver.h当编译时定义了CERES_NO_SPARSE时默认为DENSE_QR否则默认为SPARSE_NORMAL_CHOLESKY。官方 FAQ 给出的决策路径如下问题规模与结构推荐线性求解器依赖条件小规模问题几百个参数或稠密问题DENSE_QR无一般稀疏问题雅可比矩阵有大量零元素SPARSE_NORMAL_CHOLESKY需要安装SuiteSparse或CXSparse相机数量在百台量级的 BABundle Adjustment问题DENSE_SCHUR无相机数量更大的 BA 问题稀疏 Schur 补/约化相机矩阵SPARSE_SCHUR需要编译 Ceres 时开启SuiteSparse、CXSparse或 Eigen 的稀疏线性代数库支持数千台相机及以上的大规模 BA 问题ITERATIVE_SCHUR需要配合预条件子使用决策原则归纳小问题用稠密几百个参数以内或雅可比本身稠密时DENSE_QR简单可靠是首选稀疏但规模适中用稀疏直接法雅可比中存在大量结构化的零如多传感器标定中常见的每帧只关联少数参数结构SPARSE_NORMAL_CHOLESKY是平衡选择BA 问题优先考虑 Schur 族求解器BA 问题天然具有相机-路标的二分图结构Schur 补技巧可以把相机参数消元出来DENSE_SCHUR适合中小规模、SPARSE_SCHUR适合更大规模超大规模或无稀疏库可用时用迭代法ITERATIVE_SCHUR用共轭梯度法迭代求解约化系统配合SCHUR_JACOBI预条件子可获得速度和精度俱佳的折中——这也是DENSE_SCHUR太慢、又没有 SuiteSparse场景下的官方推荐。3.2 预条件子与集群算法的进阶选择当选用ITERATIVE_SCHUR时预条件子preconditioner的选择同样关键相关枚举同样定义在 include/ceres/types.h其默认值为JACOBI见 solver.hSCHUR_JACOBISchur 补的块对角预条件子仅能与ITERATIVE_SCHUR搭配是官方首推的速度与精度平衡选项CLUSTER_JACOBI、CLUSTER_TRIDIAGONAL基于可见性聚类的预条件子利用场景可见性结构决定预条件子的稀疏模式需要安装 SuiteSparse。官方建议在SCHUR_JACOBI表现不佳时按CLUSTER_JACOBI→CLUSTER_TRIDIAGONAL的顺序依次尝试集群预条件子依赖聚类算法VisibilityClusteringType中官方建议优先使用SINGLE_LINKAGE单链接聚类其次再考虑CANONICAL_VIEWS注意 solver.h 中该选项的默认值为CANONICAL_VIEWS需要显式改回前者。此外还有一个官方提示的性能开关对于中小规模问题可将Solver::Options::use_explicit_schur_complement置为true显式构造 Schur 补矩阵通常能带来可观的性能提升。3.3 仓库中的真实选型DENSE_QR与稀疏库编译选项回到lidar2car的激光里程计其 Ceres 问题每轮只优化一个 7 维位姿参数块四元数 平移线/面残差块数量有限雅可比矩阵规模极小因此 odomEstimationClass.cpp 中选用DENSE_QR完全符合上述决策路径第 1 条——问题参数规模小稠密直接法最优。而仓库内置的 Ceres 构建脚本 3rdparty/ceres/CMakeLists.txt 默认开启了三个稀疏支持选项保证SPARSE_NORMAL_CHOLESKY/SPARSE_SCHUR等求解器可用option(SUITESPARSE Enable SuiteSparse. ON) option(CXSPARSE Enable CXSparse. ON) option(EIGENSPARSE Enable Eigen as a sparse linear algebra library. ON)这意味着当你在本仓库上构建自己的标定/优化问题、且问题规模达到需要稀疏求解器时SPARSE_SCHUR、SPARSE_NORMAL_CHOLESKY及CLUSTER_*预条件子均已具备运行条件无需额外安装第三方库。四、用Solver::Summary::FullReport诊断性能问题当 Ceres 求解出现运行时或收敛性问题时第一步永远是查看Solver::Summary::FullReport()的输出。Summary记录了求解的完整统计信息迭代历史、线性求解器实际使用情况、各阶段耗时、终止原因等FullReport()将其格式化为可读报告。4.1 完整报告示例解读官方 FAQ 以 Ceres 自带的 BA 示例bundle_adjuster为例运行命令为./bin/bundle_adjuster --input ../data/problem-16-22106-pre.txt其迭代历史第一段输出逐列显示迭代序号、cost、cost_change、|gradient|、|step|、tr_ratio信赖域比例、tr_radius信赖域半径、ls_iter线性求解器内迭代数、iter_time单次迭代耗时、total_time累计耗时iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time 0 4.185660e06 0.00e00 2.16e07 0.00e00 0.00e00 1.00e04 0 7.50e-02 3.58e-01 1 1.980525e05 3.99e06 5.34e06 2.40e03 9.60e-01 3.00e04 1 1.84e-01 5.42e-01 2 5.086543e04 1.47e05 2.11e06 1.01e03 8.22e-01 4.09e04 1 1.53e-01 6.95e-01 3 1.859667e04 3.23e04 2.87e05 2.64e02 9.85e-01 1.23e05 1 1.71e-01 8.66e-01 4 1.803857e04 5.58e02 2.69e04 8.66e01 9.93e-01 3.69e05 1 1.61e-01 1.03e00 5 1.803391e04 4.66e00 3.11e02 1.02e01 1.00e00 1.11e06 1 1.49e-01 1.18e00随后是汇总报告Solve Report关键字段如下Ceres Solver v1.12.0 Solve Report Parameter blocks 22122 Parameters 66462 Residual blocks 83718 Residual 167436 Minimizer TRUST_REGION Sparse linear algebra library SUITE_SPARSE Trust region strategy LEVENBERG_MARQUARDT Linear solver SPARSE_SCHUR SPARSE_SCHUR Threads 1 1 Linear solver threads 1 1 Linear solver ordering AUTOMATIC 22106, 16 Cost: Initial 4.185660e06 Final 1.803391e04 Minimizer iterations 5 Successful steps 5 Unsuccessful steps 0 Termination: NO_CONVERGENCE (Maximum number of iterations reached.)报告右侧的Given / Used两列如Linear solver: SPARSE_SCHUR / SPARSE_SCHUR很有价值当 Ceres 判定你指定的求解器不适用时会自动降级此时 Used 列会显示实际采用的求解器与 Given 列不一致提示你需要修正配置。4.2 从时间分解定位瓶颈运行时性能分析应聚焦以下耗时块Time (in seconds): Preprocessor 0.283 Residual evaluation 0.061 Jacobian evaluation 0.361 Linear solver 0.382 Minimizer 0.895 Postprocessor 0.002 Total 1.220以上例分析总耗时 1.2 秒中线性求解约 0.38 秒其余主要花在预处理Preprocessor和雅可比求值上。预处理阶段明显偏贵这是由报告中的这行暴露的Linear solver ordering AUTOMATIC 22106, 16它表明SPARSE_SCHUR正在使用自动求序AUTOMATIC ordering。自动求序需要对约化矩阵做符号分解来寻找好的消元顺序在部分问题上开销很大。bundle_adjuster提供了-orderinguser参数来改为手动给定排序改进后的时间分解为Time (in seconds): Preprocessor 0.051 Residual evaluation 0.053 Jacobian evaluation 0.344 Linear solver 0.372 Minimizer 0.854 Postprocessor 0.002 Total 0.935预处理时间从 0.283 秒降到 0.051 秒降幅超过 5.5 倍总耗时同步从 1.22 秒降到 0.935 秒。调优启示若Preprocessor占比异常高优先检查linear_solver_ordering是否为 AUTOMATIC并考虑手动提供参数分块顺序对 BA 问题即先相机后路标的排序若Linear solver占比高回头对照第三部分的选择路径评估当前线性求解器是否匹配问题规模与稀疏结构若Residual/Jacobian evaluation占比高则应审视代价函数实现本身如是否可以用SizedCostFunction或自动求导模板减少重复计算。五、总结一条贯穿标定任务的 Ceres 调优主线综合官方 FAQ 与仓库源码可以提炼出一条适用于 SensorsCalibration 各类标定/优化任务的调优主线先求解后验证用Problem::Evaluate提取雅可比验证结果调试期可开启check_gradients做解析/数值雅可比互检按规模选求解器小规模稠密问题如lidar2car单帧位姿优化用DENSE_QR大规模稀疏/BA 问题沿SPARSE_NORMAL_CHOLESKY→SPARSE_SCHUR→ITERATIVE_SCHUR路线升级并根据稀疏库可用性本仓库默认开启 SuiteSparse / CXSparse / EigenSparse做取舍迭代法配好预条件子ITERATIVE_SCHUR下优先SCHUR_JACOBI不满意再试CLUSTER_JACOBI/CLUSTER_TRIDIAGONAL聚类算法优先SINGLE_LINKAGE用FullReport说话任何性能问题先看报告的时间分解与 Given/Used 求解器列针对性处理自动排序、线程数等配置避免盲目调参。文中所涉核心源码与文档路径汇总如下供继续深入研读关联文档本文骨架SensorX2car/lidar2car/ceres/docs/source/solving_faqs.rst线性求解器枚举定义SensorX2car/lidar2car/3rdparty/ceres/include/ceres/types.h#L57-L92Solver::Options默认值与容差设置SensorX2car/lidar2car/3rdparty/ceres/include/ceres/solver.h稀疏库编译开关SensorX2car/lidar2car/3rdparty/ceres/CMakeLists.txt#L163-L179仓库内实际 Ceres 求解示例SensorX2car/lidar2car/src/fastloam/odomEstimationClass.cpp#L53-L70手写解析雅可比与 SE(3) 参数化示例SensorX2car/lidar2car/src/fastloam/lidarOptimization.cpp赞分享自动驾驶计算机视觉【免费下载链接】SensorsCalibrationOpenCalib: A Multi-sensor Calibration Toolbox for Autonomous Driving项目地址https://gitcode.com/gh_mirrors/se/SensorsCalibration点击查看免费下载相关推荐openMVG 中的 Ceres 求解器调优指南线性求解器选型、雅可比求值与性能诊断openMVG 中的 Ceres 求解器调优指南线性求解器选型、雅可比求值与性能诊断 本篇技术指南围绕 Ceres Solver 官方文档 solving_f计算机视觉科研SensorsCalibration 实践Ceres Solver FAQ 与调优技巧详解——从求导、参数化到线性求解器选型SensorsCalibration 实践Ceres Solver FAQ 与调优技巧详解——从求导、参数化到线性求解器选型 导读 本篇文章围绕 OpenCa自动驾驶计算机视觉Ceres Solver 非线性最小二乘求解实战指南信任域/线搜索算法与求解器配置全解析基于 SensorsCalibration 仓库Ceres Solver 非线性最小二乘求解实战指南信任域/线搜索算法与求解器配置全解析基于 SensorsCalibration 仓库 导读 本文围绕自动驾驶计算机视觉上一篇Redwood 后台任务实战使用 exec 命令与 Faktory 构建后台 Worker下一篇AI MA 全景报告深度解读战略收购目标、买家动机与市场整合趋势context-engineering-intro RAG 知识库示例文档创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考