Ceres Solver Document学习笔记

Posted Jichao_Peng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ceres Solver Document学习笔记相关的知识,希望对你有一定的参考价值。

Ceres Solver Document学习笔记

之前在学习Vins-Mono时,在Vins-Mono后端就是使用Ceres实现的滑窗优化,当时对Ceres的了解也仅仅是在简单使用的层面上,最近项目中有再次用到Ceres相关的内容,因此我把Ceres Solver的Document扫了一遍,这篇博客是相关的笔记。我个人觉得Ceres比较重要的优势主要有如下几点:

  1. Ceres中提供了自动求导的功能,在优化算法推到的过程中,最麻烦的也就是求雅克比的过程,而Ceres Solver提供的自动求导功能直接为用户避免了这个麻烦,因此在工程中使用Ceres后除非需要优化系统性能才会采用手动求导雅克比;
  2. Ceres中提供了局部参数化的功能接口,也就是LocalParameteriztion对象,这对于求解旋转变换相关问题有极大的帮助;

其他的诸如计算速度,求解器丰富等就不多提及了,下面开始展开细节

1. 基本概念

我看大多数博客都会用如下例子:
min ⁡ x 1 2 ∑ i ρ i ( ∥ f i ( x i 1 , … , x i k ) ∥ 2 ) \\min _{\\mathbf{x}} \\frac{1}{2} \\sum_{i} \\rho_{i}\\left(\\left\\|f_{i}\\left(x_{i_{1}}, \\ldots, x_{i_{k}}\\right)\\right\\|^{2}\\right) xmin21iρi(fi(xi1,,xik)2)  s.t.  l j ≤ x j ≤ u j \\text { s.t. } \\quad l_{j} \\leq x_{j} \\leq u_{j}  s.t. ljxjuj其中,公式中的各个部分对应的概念是:
ρ i ( ∥ f i ( x i 1 , … , x i k ) ∥ 2 ) \\rho_{i}\\left(\\left\\|f_{i}\\left(x_{i_{1}}, \\ldots, x_{i_{k}}\\right)\\right\\|^{2}\\right) ρi(fi(xi1,,xik)2)被称为ResidualBlock,也就是残差;
{ x i 1 , … , x i k } \\left\\{x_{i_{1}}, \\ldots, x_{i_{k}}\\right\\} {xi1,,xik}被成为ParameterBlock,也就是输入参数;
f i ( ⋅ ) f_{i}(\\cdot) fi()被称为CostFunction,也就是代价函数,是根据输入参数直接计算残差的模块;
ρ i ( ⋅ ) \\rho_{i}(\\cdot) ρi()被称为LossFunction,也就是损失函数或者鲁棒核函数,通常用来减少输入参数中外点影响的模块;
以上都是Ceres中的定义,在其他不同的工具或者任务中定义可能不相同,Ceres所做的也就是根据输入参数计算损失,并通过凸优化的方法将损失降到局部最小,关于凸优化中的基本原理就不在此补充,下面主要就Ceres各个模块的用法进行介绍。

2. 基本方法

2.1 CostFunction

CostFunction负责计算残差和雅克比矩阵,CostFuntion类代码如下:

class CostFunction {
 public:
  virtual bool Evaluate(double const* const* parameters,
                        double* residuals,
                        double** jacobians) = 0;
  const vector<int32>& parameter_block_sizes();
  int num_residuals() const;

 protected:
  vector<int32>* mutable_parameter_block_sizes();
  void set_num_residuals(int num_residuals);
};

Evaluate成员函数负责根据输入的paramters计算residuals和jacobians,如果jacobians为nullptr,通常我们只需要在Evaluate函数中实现residuals的计算,jacobians后面可以通过Ceres提供的自动求导(数值求导)替代,否则,我们还需要在Evaluate函数中实现jacobians的计算,jacobians是一个大小为num_residuals * parameter_block_sizes_的行主数组,具体计算公式为: jacobians [ i ] [ r ∗  parameter block sizes [ i ] + c ] = ∂ residual [ r ] ∂ parameters [ i ] [ c ] \\text{jacobians}[i] [r * \\text{ parameter block sizes}[i] + c] =\\frac{\\partial \\text {residual}[r]}{\\partial \\text {parameters}[i][c]} jacobians[i][r parameter block sizes[i]+c]=parameters[i][c]residual[r]在CostFunction中用成员变量CostFunction::parameter_block_sizes_和CostFunction::num_residuals_分别用于记录输入parameters和residuals的尺寸,这两个信息可以通过继承该类后使用访问其设置,或者通过Problem::AddResidualBlock()函数添加。

此外,如果paramters大小和residuals大小在编译时是已知的,我们就可以使用SizeCostFunction,该函数可以将paramters大小和residuals大小设置为模板参数,用户只需要在使用的时候给模板参数赋值就可以,如下:

template<int kNumResiduals, int... Ns>
class SizedCostFunction : public CostFunction {
 public:
  virtual bool Evaluate(double const* const* parameters,
                        double* residuals,
                        double** jacobians) const = 0;
};

2.2 AutoDiffCostFunction

该方法就是Ceres中提供的自动求导的方法,如果需要使用自动微分函数,必须在CostFunction中定义模板符号函数operator(),该函数根据paramters计算residuals,如下所示:

class MyScalarCostFunctor {
  MyScalarCostFunctor(double k): k_(k) {}

  template <typename T>
  bool operator()(const T* const x , const T* const y, T* e) const {
    e[0] = k_ - x[0] * y[0] - x[1] * y[1];
    return true;
  }

 private:
  double k_;
};

然后带自动微分功能的CostFunction构造函数如下:

CostFunction* cost_function
    = new AutoDiffCostFunction<MyScalarCostFunctor, 1, 2, 2>(
        new MyScalarCostFunctor(1.0));              ^  ^  ^
                                                    |  |  |
                        Dimension of residual ------+  |  |
                        Dimension of x ----------------+  |
                        Dimension of y -------------------+

默认情况下,当AutoDiffCostFunction销毁时,其对应的CostFunction也会销毁,如果不希望存在这种从属关系,可以在实例化AutoDiffCostFunction时,传入参数DO_NOT_TAKE_OWNERSHIP,如下代码所示:

MyScalarCostFunctor functor(1.0)
CostFunction* cost_function
    = new AutoDiffCostFunction<MyScalarCostFunctor, 1, 2, 2>(
        &functor, DO_NOT_TAKE_OWNERSHIP);

AutoDiffCostFunction同时支持在运行时确定残差维度,此时需要在实例化时传入参数DYNAMIC,代码如下所示:

CostFunction* cost_function
    = new AutoDiffCostFunction<MyScalarCostFunctor, DYNAMIC, 2, 2>(
        new CostFunctorWithDynamicNumResiduals(1.0),   ^     ^  ^
        runtime_number_of_residuals); <----+           |     |  |
                                           |           |     |  |
                                           |           |     |  |
          Actual number of residuals ------+           |     |  |
          Indicate dynamic number of residuals --------+     |  |
          Dimension of x ------------------------------------+  |
          Dimension of y ---------------------------------------+

AutoDIffCostFunction在编译时就需要确定参数维度,但是在一些使用场景中需要在运行时才确定,那么这时就需要使用DynamicAutoDiffCostFunction,具体使用方法如下:

template <typename CostFunctor, int Stride = 4>
class DynamicAutoDiffCostFunction : public CostFunction {
};

struct MyCostFunctor {
  template<typename T>
  bool operator()(T const* const* parameters, T* residuals) const {
  }
}

DynamicAutoDiffCostFunction<MyCostFunctor, 4>* cost_function =
  new DynamicAutoDiffCostFunction<MyCostFunctor, 4>(
    new MyCostFunctor());
cost_function->AddParameterBlock(5);
cost_function->AddParameterBlock(10);
cost_function->SetNumResiduals(21);

2.3 NumericDiffCostFuntion

NumericDiffCostFunction作用和AutoDiffCostFunction作用是相同的,二者唯一的区别是NumericDiffCostFunction中不再使用模板类型,而是使用double类型代替模板类型。谷歌推荐类型为AutoDiffCostFunction,C++模板的使用使得自动求导效率更高,而数值的差花费更多,容易出现数字错误,导致收敛速度变慢。

2.4 LossFunction

LossFunction就是上文提到的代价计算公式中的鲁棒核函数 ρ i ( ⋅ ) \\rho_{i}(\\cdot) ρi()部分,用于减少输入异常值对结果的影响,通常鲁棒核函数 ρ i ( ⋅ ) \\rho_{i}(\\cdot) ρi()要求满足 ρ ( 0 ) = 0 \\rho(0)=0 ρ(0)=0 ρ ′ ( 0 ) = 1 \\rho^{\\prime}(0)=1 ρ(0)=1 ρ ′ ( s ) < 1 \\rho^{\\prime}(s)<1 ρ(s)<1 ρ ′ ′ ( s ) < 0 \\rho^{\\prime \\prime}(s)<0 ρ(s)<0具体地,在Ceres中提供了如下核函数:
TrivialLoss ρ ( s ) = s \\rho(s)=s ρ(s)=sHuberLoss ρ ( s ) = { s s ≤ 1 2 s − 1 s > 1 \\rho(s)=\\left\\{\\begin{array}{ll} s & s \\leq 1 \\\\ 2 \\sqrt{s}-1 & s>1 \\end{array}\\right. ρ(s)={s2s ceres solver 03 三种求导方式

[ceres-solver] From QuaternionParameterization to LocalParameterization

linux ceres-solver怎么编译

Ceres-solver安装(win10+vs2015)

ceres-solver无脑搞定协方差小技巧

Ceres学习