ceres solver 01 入门小例子

1. 基本介绍

Ceres Solver是用来解决非线性最小二乘问题的库。

非线性最小二乘问题用公式表达如下:

其中是依赖于参数块的代价函数。
参数块就是要优化的东西。

and 是参数块 的上下界。

是损失函数。作用是减少异常点对解的影响,可理解为根据不同值带入不同权重计算的函数。

表达式记为残差块。

比如,当 ,上下界取: and ,我们得到熟悉的非线性最小二乘问题的形式如下:

2. 入门小例子

为了简化问题:我们取只有一个参数和只有一个代价函数的情况,求使得代价函数最小时的参数的值。

2.1 定义代价函数

Ceres中是通过定义一个结构体或者类的形式来定义代价函数。比如$f(x)=10-x$定义如下:

struct CostFunctor {
  template  
  bool operator()(const T* const x, T* residual) const {
    residual[0] = T(10.0) - x[0];
    return true;
  }
};

这里用到了模板,是为了后面用自动求导,可理解这里的Tdouble
定义代价函数实现函数operator(),这里的参数顺序是参数块在前,残差块在最后。

2.2 构建问题,求解问题,输出结果报告

构建一个问题

Problem problem;

输入已经定义的代价函数

CostFunction* cost_function = new AutoDiffCostFunction(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  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(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

3. 参考

http://ceres-solver.org/nnls_tutorial.html#introduction

你可能感兴趣的:(ceres solver 01 入门小例子)