ceres库主要是用来优化问题,和深度学习思想差不多,迭代优化,逐渐逼近最优解。
回顾一下非线性最小二乘法
方程式AX=B,我们可根据其形式求解析解。如果该问题为线性,我们可对目标函数求导,零导数为零,可求得目标函数极值,并且其为全局最小值,则为目标函数的最优解。
但问题往往为非线性,由于函数复杂,无法写出其导数形式,我们不可能再通过导数找全局最优解,而是通过不断的迭代计算找到函数局部最小解,并且该局部最小解在误差允许的范围内,我们就可认为目标函数取得最优解,以上就是非线性最小二乘法的思想。
这可将求导问题转化为梯度下降问题:
基于以上思想,ceres库可通过优化迭代求解非线性最小二乘问题最优解。
ceres::Problem problem;
cost_function = new ceres::AutoDiffCostFunction(new LOSS(pose,alpha,a,d));
参数介绍:CostFunctor 自己编写的代价函数,
第一个六 表示要优化的残差参数数组大小 (residual 大小)
第二个六 表示 待优化参数数组大小 (即下文中X大小)
problem.AddResidualBlock(new ceres::AutoDiffCostFunction(new LOSS(pose,alpha,a,d)),nullptr,x.data());
(这里代价函数直接添加到添加代价函数中)
new LOSS(pose,alpha,a,d) 需要传入代价函数的参数值
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
:是否向终端输出优化过程信息
这里仅仅列了比较常用的求解器参数,博主还是小白,还需继续学习。
ceres::Solver::Summary summary; // 优化信息
ceres::Solve(options, &problem, &summary); // 开始优化
cout << summary.BriefReport() << endl; //输出迭代信息
求导函数有三种:解析求导、数值求导和自动求导
这里只介绍自动求导,其他两个还未接触
这是ceres库一个神奇的功能,可以通过比较基础的表达式求解出导数形式,我的理解是通过Jets实现的,采用模板类形式
struct LOSS {
LOSS(vector pose,vector alpha,vector a,vector d):_pose(pose),_alpha(alpha),_a(a),_d(d) {}
template
bool operator()(const T *theta, T *residual) const {
...
for (int i = 0; i < 6; i++) residual[i] =...
return true;
}
//const Mat TD;
const vector _alpha, _a, _d,_pose;
};
在结构体构造的重载运算符中,如果要使用loss的参数,首先要将其转化为Jet的格式,例如pose,传入的数据类型为vector
vector Pose(6);
for(int i=0;i<6;i++) Pose[i]=T(_pose);
使用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