Ceres 入门教程---Ceres Solver新手攻略

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27251141/article/details/90409048

Ceres solver 是谷歌开发的一款用于非线性优化的库,在谷歌的开源激光雷达slam项目cartographer中被大量使用。Ceres官网上的文档非常详细地介绍了其具体使用方法,相比于另外一个在slam中被广泛使用的图优化库G2O,ceres的文档可谓相当丰富详细(没有对比就没有伤害,主要是G2O资料太少了,对比起来就显得ceres的很多),下面我就介绍下如何使用ceres库进行简单的非线性优化,给各位ceres的初学者一个低门槛的入门教程,能比较直观地对使用ceres库求解优化问题的过程有一个清晰的了解。

简介

Ceres可以解决有界约束的线性最小二乘问题的形式
min x 1 2 i ρ i ( f i ( x i 1 , . . . , x i k ) 2 ) s . t . l j x j u j { \min_x \frac{1}{2} \sum_i \rho_i(||f_i(x_{i_1},...,x_{i_k})||^2) \qquad\\ s.t. \quad l_j \leq x_j \leq u_j }
这种形式的问题出现在科学和工程的广泛领域——从统计学中的拟合曲线,到计算机视觉中的照片构建三维模型。在本章中,我们将学习如何使用ceres求解器求解。所有源码可以在这里下载:https://github.com/ceres-solver/ceres-solver

表达式: ρ i ( f i ( x i 1 , . . . , x i k ) 2 ) {\rho_i(||f_i(x_{i_1},...,x_{i_k})||^2) } 为残差块(ResidualBlock),这里 f ( ) {f(\cdot)} 为依赖参数块 [ x i 1 , . . . , x i k ] {[x_{i_1},...,x_{i_k}]} 的代价函数(CostFunction)。在大多数优化问题中,小群标量总是同时出现。例如,定义摄像机姿态的平移矢量的三个分量和四元数的四个分量。我们将这样一组小的标量称为参数块。当然,一个参数块可以只是一个参数。 l j {l_j} u j {u_j} 是参数块 x j {x_j} 上的界限。
ρ i {\rho_i} 是一个损失函数(LossFunction),损失函数是一个标量函数,用于减少异常值对非线性最小二乘问题解的影响。
特别的当 ρ i ( x ) = x {\rho_i(x)=x} ,即为恒等函数,如果 l j = {l_j=-\infty} l j = {l_j=-\infty} 我们得到了更熟悉的非线性最小二乘问题。
1 2 i f i ( x i 1 , . . . , x i k ) 2 { \frac{1}{2} \sum_i ||f_i(x_{i_1},...,x_{i_k})||^2 }

第一个例子(Hello World!)

首先,考虑寻找函数的最小值的问题
1 2 ( 10 x ) 2 {\frac{1}{2}(10-x)^2}
这是一个很小的问题。其最小值位于 x = 10 {x=10} 。但这是一个很好的说明Ceres问题基础的例子。
第一步是构造一个函数去估计函数: f ( x ) = 10 x {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;
   }
};

值得注意的一件重要事情是, operator() 是一个模板化的方法,它假定所有的输入和输出是某种类型T的使用模板调用允许CeresCostFunctor::operator< T >(),与T =double时剩余的价值是必要的,和一种特殊类型T =Jet,雅克比是必要的。在衍生品中,我们将更详细地讨论向谷神星提供衍生品的各种方法。
一旦我们有了计算残差的函数的方法,现在是时候构造一个非线性最小值了。平方问题,让Ceres来解决

int main(int argc, char** argv) {
  google::InitGoogleLogging(argv[0]);

  // The variable to solve for with its initial value.
  double initial_x = 5.0;
  double x = initial_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.linear_solver_type = ceres::DENSE_QR;
  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;
}

自动求导函数将CostFunctor作为输入,自动微分它,并给它一个代价函数接口。
编译运行 examples/helloworld.cc 得到:

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.33e-04    3.46e-03
   1  4.511598e-07    4.51e+01    9.50e-04   9.50e+00   1.00e+00  3.00e+04       1    5.00e-04    4.05e-03
   2  5.012552e-16    4.51e-07    3.17e-08   9.50e-04   1.00e+00  9.00e+04       1    1.60e-05    4.09e-03
Ceres Solver Report: Iterations: 2, Initial cost: 4.512500e+01, Final cost: 5.012552e-16, Termination: CONVERGENCE
x : 0.5 -> 10

猜你喜欢

转载自blog.csdn.net/qq_27251141/article/details/90409048