Ceres(4):Modeling Non-linear Least Squares APIs(1)

来源:互联网 发布:淘宝企业开店费用 编辑:程序博客网 时间:2024/06/07 14:25

转载地址:http://ceres-solver.org/nnls_modeling.html

classCostFunction(详见cost_function.h)

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);};
类的private member std::vector<int32> parameter_block_sizes_,保存的是要求的参数的个数,和每个参数的维数;

int num_residuals_;保存的是残差的维数; parameters和parameter_block_sizes_大小相同,且: 
  //   parameters_[i] = double[parameter_block_sizes_[i]]

jacobians 保存的是残差的每一维对每个参数的每个维的偏导。jacobians是一个大小为parameter_block_sizes_的 double* 指针数组,

每个指针指向的是残差的每一维对相应参数块的每个维的偏导,即jacobians[i]是一个数组,大小:num_residuals_* parameter_block_sizes_[i],

依次保存的是:

residuals[0]对parameter_block_sizes_[i][0],对parameter_block_sizes_[i][1],...parameter_block_sizes_[i][n]的偏导,

然后是residuals[1]对parameter_block_sizes_[i][0],对parameter_block_sizes_[i][1],...parameter_block_sizes_[i][n]的偏导,...

依次重复,直到residuals[num_residuals_-1]。

该类必须通过类的函数设置要求的参数的个数,和每个参数的维数,以及残差的维数,太麻烦。

classSizedCostFunction

template<int kNumResiduals,         int N0 = 0, int N1 = 0, int N2 = 0, int N3 = 0, int N4 = 0,         int N5 = 0, int N6 = 0, int N7 = 0, int N8 = 0, int N9 = 0>class SizedCostFunction : public CostFunction { public:  virtual bool Evaluate(double const* const* parameters,                        double* residuals,                        double** jacobians) const = 0;};
当参数的个数及每个参数的维数知道,残差的维数也知道时,在建模时,可以通过该类的模板参数指明残差的维数以及,每个参数的维数,这样仅对虚函数Evaluate

进行定义就可以了,比 CostFunction类简单,但是它还是要计算乏味且容易出错的jacobians,要用Analytic Derivatives时,就得自己写一个类,

public 继承自SizedCostFunction,自己写Evaluate,jacobians

classAutoDiffCostFunction

template <typename CostFunctor,       int kNumResiduals,  // Number of residuals, or ceres::DYNAMIC.       int N0,       // Number of parameters in block 0.       int N1 = 0,   // Number of parameters in block 1.       int N2 = 0,   // Number of parameters in block 2.       int N3 = 0,   // Number of parameters in block 3.       int N4 = 0,   // Number of parameters in block 4.       int N5 = 0,   // Number of parameters in block 5.       int N6 = 0,   // Number of parameters in block 6.       int N7 = 0,   // Number of parameters in block 7.       int N8 = 0,   // Number of parameters in block 8.       int N9 = 0>   // Number of parameters in block 9.class AutoDiffCostFunction : publicSizedCostFunction<kNumResiduals, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9> { public:  explicit AutoDiffCostFunction(CostFunctor* functor);  // Ignore the template parameter kNumResiduals and use  // num_residuals instead.  AutoDiffCostFunction(CostFunctor* functor, int num_residuals);  //这个构造函数是针对ceres::DYNAMIC的,运行时指定残差的维数};
AutoDiffCostFunction不需要用户计算jacobians,但是用户得定义一个模板类的仿函数。从上面的定义可以看出,它最多能有10个参数。

The function must write the computed value(残差) in the last argument (the only non-const one,其他形参都是const T* const) 

and return true to indicate success.

For example, consider a scalar error e=kxy,where both x and y are two-dimensional vector parameters and k is a constant. 

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] = T(k_) - x[0] * y[0] - x[1] * y[1];    return true;  } private:  double k_;};

AutoDiffCostFunction also supports cost functions with a runtime-determined number of residuals. For example:


classDynamicAutoDiffCostFunction

该API支持:可变的参数个数和每个参数的维数可变

class MyCostFunctor {     template<typename T>     bool operator()(T const* const* parameters, T* residuals) const {       // Use parameters[i] to access the i'th parameter block.     }   }

Since the sizing of the parameters is done at runtime, you must also specify the sizes after creating the dynamic autodiff cost function. For example:

DynamicAutoDiffCostFunction<MyCostFunctor, 4>* cost_function =  new DynamicAutoDiffCostFunction<MyCostFunctor, 4>(    new MyCostFunctor());cost_function->AddParameterBlock(5);   //第一参数是5维cost_function->AddParameterBlock(10);   //第二个是10维cost_function->SetNumResiduals(21);

classNumericDiffCostFunction

In some cases, its not possible to define a templated cost functor, for example when the evaluation of the residual involves a call to a library function that you do not have control over. In such a situation, numerical differentiation can be used.

template <typename CostFunctor,          NumericDiffMethodType method = CENTRAL,          int kNumResiduals,  // Number of residuals, or ceres::DYNAMIC.          int N0,       // Number of parameters in block 0.          int N1 = 0,   // Number of parameters in block 1.          int N2 = 0,   // Number of parameters in block 2.          int N3 = 0,   // Number of parameters in block 3.          int N4 = 0,   // Number of parameters in block 4.          int N5 = 0,   // Number of parameters in block 5.          int N6 = 0,   // Number of parameters in block 6.          int N7 = 0,   // Number of parameters in block 7.          int N8 = 0,   // Number of parameters in block 8.          int N9 = 0>   // Number of parameters in block 9.class NumericDiffCostFunction : publicSizedCostFunction<kNumResiduals, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9> {};
To get a numerically differentiated CostFunction, you must define a class with a operator() (a functor) that computes the residuals. The functor must write the computed value(残差) in the last argument (the only non-const one,其他参数为const型) and return true to indicate success.

class ScalarFunctor { public:  bool operator()(const double* const x1,                  const double* const x2,                  double* residuals) const;}
可以看出,它是针对非模板仿函数,跟AutoDiffCostFunction一样,最多有10个参数,残差是最后一个参数。

对于e=kxywhere both x and y are two-dimensional vector parameters and k is a constant. 

class MyScalarCostFunctor {  MyScalarCostFunctor(double k): k_(k) {}  bool operator()(const double* const x,                  const double* const y,                  double* residuals) const {    residuals[0] = k_ - x[0] * y[0] - x[1] * y[1];    return true;  } private:  double k_;};




Numeric Differentiation & LocalParameterization

有些参数,它的各个维数不是独立的,互相之间是关系的,比如一个参数是四元数。

Numeric differentiation in Ceres is performed by perturbing the individual coordinates of the parameter blocks that a cost functor depends on.In doing so, we assume that the parameter blocks live in an Euclidean space and ignore the structure of manifold that they live. As a result some of the perturbations may not lie on the manifold corresponding to the parameter block.

For example consider a four dimensional parameter block that is interpreted as a unit Quaternion. Perturbing the coordinates of this parameter block will violate the unit norm property of the parameter block.

解决办法:Fixing this problem requires that NumericDiffCostFunction be aware of the LocalParameterization associated with each parameter block and only generate perturbations in the local tangent space of each parameter block.Further, in most cases it is relatively straightforward to project a point off the manifold back onto the manifold before using it in the functor。没太理解??

NumericDiffCostFunction 也可以用costFunction类作为参数

To get a numerically differentiated cost function, define a subclass of CostFunction such that theCostFunction::Evaluate() function ignores the jacobians parameter.

CostFunction* cost_function    = new NumericDiffCostFunction<MyCostFunction, CENTRAL, 1, 4, 8>(        new MyCostFunction(...), TAKE_OWNERSHIP);
classDynamicNumericDiffCostFunction

Like NumericDiffCostFunction the user must define a functor, but the signature of the functor differs slightly. The expected interface for the cost functors is:

struct MyCostFunctor {  bool operator()(double const* const* parameters, double* residuals) const {  }}
DynamicNumericDiffCostFunction<MyCostFunctor>* cost_function =  new DynamicNumericDiffCostFunction<MyCostFunctor>(new MyCostFunctor);cost_function->AddParameterBlock(5);cost_function->AddParameterBlock(10);cost_function->SetNumResiduals(21);