1. 基本介绍
Ceres Solver是用来解决非线性最小二乘问题的库。
非线性最小二乘问题用公式表达如下:
其中是依赖于参数块的代价函数。
参数块就是要优化的东西。
and 是参数块 的上下界。
是损失函数。作用是减少异常点对解的影响,可理解为根据不同值带入不同权重计算的函数。
表达式记为残差块。
比如,当 ,上下界取:
and ,我们得到熟悉的非线性最小二乘问题的形式如下:
2. 入门小例子
为了简化问题:我们取只有一个参数和只有一个代价函数的情况,求使得代价函数最小时的参数的值。
2.1 定义代价函数
Ceres中是通过定义一个结构体或者类的形式来定义代价函数。比如$f(x)=10-x$
定义如下:
struct CostFunctor {
template <typename T>
bool operator()(const T* const x, T* residual) const {
residual[0] = T(10.0) - x[0];
return true;
}
};
这里用到了模板,是为了后面用自动求导,可理解这里的T
为double
。
定义代价函数实现函数operator()
,这里的参数顺序是参数块在前,残差块在最后。
2.2 构建问题,求解问题,输出结果报告
构建一个问题
Problem problem;
输入已经定义的代价函数
CostFunction* cost_function = new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
problem.AddResidualBlock(cost_function, NULL, &x);
把残差块输入我们构建的问题,使用AddResidualBlock()
函数,AddResidualBlock()
的第1个参数代价函数指针,类型为CostFunction*
,第2个参数是损失函数指针,空缺NULL
大致表示,从第3个参数开始表示参数块,这里只有一个参数块x
,如果有更多,可以接着往后写。
非线性最小二乘的优化过程涉及到对目标函数求导,这里我们使用自动求导类,所以使用模板类AutoDiffCostFunction
,它的第1个模板参数是我们定义的代价函数类名称,第2个模板参数是残差块中的残差项的数量,第3个模板参数是第1个参数块中的参数数量,这里我们只有一个x
,所以是1。
定义求解器的选项参数和求解器的结果报告
Solver::Options options;
options.minimizer_progress_to_stdout = true;
Solver::Summary summary;
Options
是用来设置一些求解器的选项参数,控制求解过程。Summary
是用来表示求解器的结果报告。
求解并输出结果
Solve(options, &problem, &summary);
std::cout << summary.BriefReport() << "\n";
有了构建好的问题,求解器选项,结果报告,就可以用Solve
去进行求解。
summary.BriefReport()
用来输出求解过程和求解结果
2.3 完整代码
// A simple example of using the Ceres minimizer.
//
// Minimize 0.5 (10 - x)^2 using jacobian matrix computed using
// automatic differentiation.
#include "ceres/ceres.h"
using ceres::AutoDiffCostFunction;
using ceres::CostFunction;
using ceres::Problem;
using ceres::Solver;
using ceres::Solve;
// A templated cost functor that implements the residual r = 10 -
// x. The method operator() is templated so that we can then use an
// automatic differentiation wrapper around it to generate its
// derivatives.
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) {
// The variable to solve for with its initial value. It will be
// mutated in place by the solver.
double x = 0.5;
const double initial_x = x;
// Build the problem.
Problem problem;
// Set up the only cost function (also known as residual). This uses
// auto-differentiation to obtain the derivative (jacobian).
CostFunction* cost_function =
new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
problem.AddResidualBlock(cost_function, NULL, &x);
// Run the solver!
Solver::Options options;
options.minimizer_progress_to_stdout = true;
Solver::Summary summary;
Solve(options, &problem, &summary);
std::cout << summary.BriefReport() << "\n";
std::cout << "x : " << initial_x
<< " -> " << x << "\n";
return 0;
}
运行结果如下:
iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time
0 4.512500e+01 0.00e+00 9.50e+00 0.00e+00 0.00e+00 1.00e+04 0 5.48e-05 1.16e-03
1 4.511598e-07 4.51e+01 9.50e-04 9.50e+00 1.00e+00 3.00e+04 1 4.56e-04 2.07e-03
2 5.012552e-16 4.51e-07 3.17e-08 9.50e-04 1.00e+00 9.00e+04 1 2.82e-05 2.12e-03
Ceres Solver Report: Iterations: 3, Initial cost: 4.512500e+01, Final cost: 5.012552e-16, Termination: CONVERGENCE
x : 0.5 -> 10
网友评论