ceres使用

  • Post author:
  • Post category:其他


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创建代价函数


cost_function = new ceres::AutoDiffCostFunction<LOSS,16,6>(new LOSS(pose,alpha,a,d));

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

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

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

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

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

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

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

2.1.4 配置求解器

ceres::Solver::Options options;
options.linear_solver_type = ceres::ITERATIVE_SCHUR;// 信赖域策略
options.max_num_iterations = 10000;  //迭代次数
options.gradient_tolerance = 1e-3; //梯度阈值
options.function_tolerance = 1e-4;  //相邻两次迭代之间目标函数之差
options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;  //优化策略
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 运行求解器

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

2.2 求导函数

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

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

2.2.1 自动求导

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

struct LOSS {
	LOSS(vector<double> pose,vector<double> alpha,vector<double> a,vector<double> d):_pose(pose),_alpha(alpha),_a(a),_d(d) {}
	template<typename T>
	bool operator()(const T *theta, T *residual) const {

		...
		for (int i = 0; i < 6; i++) residual[i] =...
		return true;
	}
	//const Mat TD;
	const vector<double> _alpha, _a, _d,_pose;
};

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

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

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

使用SetParameterUpperBound 与SetParamterLowerBound函数定义边界

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

lower表示下界,upper表示上界

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

未完待续。。。。。

推荐 【

官网

参考

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



版权声明:本文为qq_41485193原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。