• 理解ceres


    ceres库主要是用来优化问题,和深度学习思想差不多,迭代优化,逐渐逼近最优解。

    回顾一下非线性最小二乘法

    1.非线性最小二乘法

    方程式AX=B,我们可根据其形式求解析解。如果该问题为线性,我们可对目标函数求导,零导数为零,可求得目标函数极值,并且其为全局最小值,则为目标函数的最优解。

    但问题往往为非线性,由于函数复杂,无法写出其导数形式,我们不可能再通过导数找全局最优解,而是通过不断的迭代计算找到函数局部最小解,并且该局部最小解在误差允许的范围内,我们就可认为目标函数取得最优解,以上就是非线性最小二乘法的思想。

    这可将求导问题转化为梯度下降问题:

    1. 给定某个初值x_{0}
    2. 对于第K次迭代,寻找一个增量\Delta x,使得\left \| f(x_{k}+\Delta x_{k}) \right \|_{2}^{2}达到极小值(局部最小)
    3. \Delta x足够小则停止迭代
    4. 否则 另x_{k+1} = x_{k}+\Delta x_{k},返回第二步

     基于以上思想,ceres库可通过优化迭代求解非线性最小二乘问题最优解。

    2.ceres 优化过程

    2.1 ceres优化使用流程

    2.1.1构建优化问题

    ceres::Problem problem;

    2.1.2创建代价函数

    1. cost_function = new ceres::AutoDiffCostFunction16,6>(new LOSS(pose,alpha,a,d));

    参数介绍:CostFunctor 自己编写的代价函数,

                      第一个六  表示要优化的残差参数数组大小 (residual 大小)

                      第二个六 表示 待优化参数数组大小 (即下文中X大小)

    2.1.3添加代价函数、损失函数

    problem.AddResidualBlock(new ceres::AutoDiffCostFunction6,6>(new LOSS(pose,alpha,a,d)),nullptr,x.data());

    (这里代价函数直接添加到添加代价函数中)

    new LOSS(pose,alpha,a,d)   需要传入代价函数的参数值

    2.1.4 配置求解器

    1. ceres::Solver::Options options;
    2. options.linear_solver_type = ceres::ITERATIVE_SCHUR;// 信赖域策略
    3. options.max_num_iterations = 10000; //迭代次数
    4. options.gradient_tolerance = 1e-3; //梯度阈值
    5. options.function_tolerance = 1e-4; //相邻两次迭代之间目标函数之差
    6. options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT; //优化策略
    7. options.update_state_every_iteration = true; //是否向终端输出优化过程
    • 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:线性求解器的最小/最大迭代次数

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

    • minimizer_progress_to_stdout:是否向终端输出优化过程信息

     这里仅仅列了比较常用的求解器参数,博主还是小白,还需继续学习。

    2.1.5 运行求解器

    1. ceres::Solver::Summary summary; // 优化信息
    2. ceres::Solve(options, &problem, &summary); // 开始优化
    3. cout << summary.BriefReport() << endl; //输出迭代信息

    2.2 求导函数

    求导函数有三种:解析求导、数值求导和自动求导

    这里只介绍自动求导,其他两个还未接触

    2.2.1 自动求导

    这是ceres库一个神奇的功能,可以通过比较基础的表达式求解出导数形式,我的理解是通过Jets实现的,采用模板类形式

    1. struct LOSS {
    2. LOSS(vector<double> pose,vector<double> alpha,vector<double> a,vector<double> d):_pose(pose),_alpha(alpha),_a(a),_d(d) {}
    3. template<typename T>
    4. bool operator()(const T *theta, T *residual) const {
    5. ...
    6. for (int i = 0; i < 6; i++) residual[i] =...
    7. return true;
    8. }
    9. //const Mat TD;
    10. const vector<double> _alpha, _a, _d,_pose;
    11. };

     在结构体构造的重载运算符中,如果要使用loss的参数,首先要将其转化为Jet的格式,例如pose,传入的数据类型为vector  那么要将其转化为vector 形式,转化的方法也非常简单。

    1. vector Pose(6);
    2. for(int i=0;i<6;i++) Pose[i]=T(_pose);

     2.3 为优化参数添加上下边界

    使用SetParameterUpperBound 与SetParamterLowerBound函数定义边界

    1. problem.SetParameterLowerBound(x.data(), index, lower_bound);
    2. problem.SetParameterUpperBound(x.data(), index, upper_bound);

     lower表示下界,upper表示上界 

    第一个参数x 表示优化参数数组,index表示数组中第index个元素,第三个参数表示边界

    未完待续。。。。。

    推荐 【官网

    参考 https://blog.csdn.net/hltt3838/article/details/109695164

  • 相关阅读:
    在Windows环境下将Tomcat发布成服务,并配置JVM参数
    python astra相机驱动问题
    应用程序会不会导致linux系统崩溃死机?
    (附源码)ssm日语学习系统 毕业设计 271621
    OpenFeign:声明式服务调用
    数据结构实战开发教程(五)再论智能指针、循环链表的实现、双向链表的实现、双向循环链表的实现、Linux内核链表剖析
    24 回文链表
    libOpenDRIVE
    JS-前端学习随笔-对于深、浅拷贝的理解
    G-BioSciences F(ab‘)2 片段制备试剂盒解决方案
  • 原文地址:https://blog.csdn.net/qq_41485193/article/details/126243533