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


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

Ceres Solver是一个开源的C++库,用于解决非线性最小二乘问题。它可以用于各种应用领域,如计算机视觉、机器人、三维重建等。以下是Ceres Solver的简单使用步骤: 1. 安装Ceres Solver:可以从官网下载源代码,然后编译安装。 2. 定义问题:首先需要定义一个问题对象,该对象包含要优化的参数、残差函数和权重等信息。 3. 定义残差函数:残差函数是实际值和理论值之间的差异,需要实现该函数并传递给问题对象。 4. 设置求解选项:设置求解器的选项,例如最大迭代次数、收敛阈值等。 5. 求解问题:调用Ceres Solver的求解器函数来求解问题。 下面是一个简单的示例代码,演示如何使用Ceres Solver来解决一个非线性最小二乘问题: ```c++ #include "ceres/ceres.h" struct CostFunctor { template <typename T> bool operator()(const T* const x, T* residual) const { residual[0] = T(10.0) - x[0]; return true; } }; int main(int argc, char** argv) { google::InitGoogleLogging(argv[0]); // 初始化问题 ceres::Problem problem; // 添加参数 double x = 0.5; problem.AddResidualBlock( new ceres::AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor), nullptr, &x); // 设置求解选项 ceres::Solver::Options options; options.max_num_iterations = 100; options.linear_solver_type = ceres::DENSE_QR; options.minimizer_progress_to_stdout = true; // 求解问题 ceres::Solver::Summary summary; ceres::Solve(options, &problem, &summary); std::cout << summary.FullReport() << std::endl; std::cout << "x : " << x << std::endl; return 0; } ``` 在这个例子中,我们定义了一个CostFunctor结构体,它包含一个operator()函数,该函数计算实际值和理论值之间的差异。在main函数中,我们首先初始化了一个问题对象,然后添加了一个参数x和一个残差函数。接下来,我们设置了求解选项,并调用了Ceres Solver的求解器函数来求解问题。最后,输出结果并返回0。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值