Ceres学习-3.Solver

使用Ceres求解非线性优化问题,一共分为三个部分:

  1. 第一部分:构建cost fuction,即代价函数,也就是寻优的目标式。参见《Ceres学习-1.CostFunction》https://www.cnblogs.com/vivian187/p/15393995.html
  2. 第二部分:通过代价函数构建待求解的优化问题。参见《Ceres学习-2.Problem》https://www.cnblogs.com/vivian187/p/15394000.html
  3. 第三部分:配置求解器参数并求解问题,这个步骤就是设置方程怎么求解、求解过程是否输出等,然后调用一下Solve方法。

这节就讲述第三个部分:

一个简单的应用例子

// 来自于ceres-solver-1.14.0/examples/helloworld.cc

// 第三部分: 配置并运行求解器

// Run the solver!
Solver::Options options;
options.minimizer_progress_to_stdout = true;
options.linear_solver_type = ceres::DENSE_QR;
Solver::Summary summary; // 优化信息
Solve(options, &problem, &summary);// 求解!!!

std::cout << summary.BriefReport() << "\n"; // 输出优化的简要信息

求解最小二乘问题

ceres::Solve函数是Ceres求解最小二乘问题的核心函数,函数原型如下:

// 来自于ceres-solver-1.14.0/include/ceres/solver.h

void Solve(const Solver::Options& options, Problem* problem, Solver::Summary* summary);

参数:

Solver::Options 求解选项。是Ceres求解的核心,包括消元顺序、分解方法、收敛精度等在内的求解器所有行为均由Solver::Options控制。
Problem         求解问题。参考《Ceres学习-2.Problem》
Solver::Summary 求解报告。用于存储求解过程中的相关信息,并不影响求解器性能

参数详解

Solver::Options

Solver::Options含有的参数种类繁多,API文档中对于每个参数的作用和意义都给出了详细的说明。由于在大多数情况下,绝大多数参数我们都会使用Ceres的默认设置。列举了一些可能会改变的参数:

  • linear_solver_type:信赖域方法中求解线性方程组所使用的求解器类型,默认为DENSE_QR,其他可选项如下:
    DENSE_QR:QR分解,用于小规模最小二乘问题求解;
    DENSE_NORMAL_CHOLESKY&SPARSE_NORMAL_CHOLESKY:Cholesky分解,用于具有稀疏性的大规模非线性最小二乘问题求解;
    CGNR:使用共轭梯度法求解稀疏方程;
    DENSE_SCHUR&SPARSE_SCHUR:SCHUR分解,用于BA问题求解;
    ITERATIVE_SCHUR:使用共轭梯度SCHUR求解BA问题;
  • min_linear_solver_iteration/max_linear_solver_iteration:线性求解器的最小/最大迭代次数,默认为0/500,一般不需要更改;

  • max_num_iterations:求解器的最大迭代次数;

  • num_threads:Ceres求解时使用的线程数

  • linear_solver_ordering:线性方程求解器的消元顺序,默认为NULL,即由Ceres自行决定消元顺序;在以BA为典型代表的,对消元顺序有特殊要求的应用中,可以通过成员函数reset设定消元顺序,稍后将详细说明;

linear_solver_ordering

Ceres消元顺序的设置由linear_solver_ordering的reset函数完成,该函数接受参数为ParameterBlockOrdering对象。该对象将所有待优化参数存储为带标记(ID)的组(Group),
ID小的Group在求解线性方程的过程中会被首先消去。因此,我们需要做的第一个工作是调用其成员函数AddElementToGroup将参数添加到对应ID的Group中,函数原型为:

bool ParameterBlockOrdering::AddElementToGroup(const double *element, const int group)

接收的元素为变量数组的指针;组ID为非负整数,最小为0,如果该Id对应的Group不存在,则Ceres会自动创建。下面我们来看一个BA中的例子:

ceres::ParameterBlockOrdering* ordering = new ceres::ParameterBlockOrdering();

    // set all points in ordering to 0
    for(int i = 0; i < num_points; i++){
        ordering->AddElementToGroup(points + i * point_block_size, 0);
    }
    // set all cameras in ordering to 1
    for(int i = 0; i < num_cameras; i++){
        ordering->AddElementToGroup(cameras + i * camera_block_size, 1);
    }

该例子中,所有路标点被分到了ID = 0组,而所有相机位姿被分到了ID = 1组,因此在线性方程组的求解中,所有路标点会变首先SCHUR消元。

接下来,我们就可以使用reset函数制定线性求解器的消元顺序了:

// set ordering in options
options->linear_solver_ordering.reset(ordering);

在实际应用中,对最终求解性能最大的就是线性方程求解器类型linear_solver_type和线程数,如果发现最后的求解精度或求解效率不能满足要求,应首先尝试更换这两个参数。

Solver::Summary

Solver::Summary包含了求解器本身和求解中各变量的信息,许多成员函数与Solver::Options一致,详细列表同样请参阅API文档,这里只给出另外两个常用的成员函数:

  • BriefReport():输出单行的简单总结;
  • FullReport():输出多行的完整总结。

实例Bundle Adjustment,参照下面两个网站

https://blog.csdn.net/weixin_43991178/article/details/100568128?utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-1.no_search_link 《最小二乘问题构建与求解》

https://www.cnblogs.com/vivian187/p/15331483.html 《5.Ceres官方教程-非线性最小二乘~Bundle Adjustment》

posted on 2021-10-11 16:51  JJ_S  阅读(1894)  评论(0编辑  收藏  举报