[关闭]
@Xiaobuyi 2017-09-23T22:56:36.000000Z 字数 3414 阅读 3184

Ceres总结

数学基础


1 Ceres求解优化问题的思路

  自己根据对Ceres的学习,总结在使用Ceres进行优化问题的求解的时候一般要进行以下几个步骤:
1. 建立求解的Problem

  1. ceres::Problem Problem;

  建立求解Problem主要包含两个步骤:设定代价函数和添加残差。
设定代价函数--->设定求导方式 求导方式主要有自动求导自定义导数两种方式(自动求导中的数值导数NumericDiffCostFunctor等方式不加以区分,当做同类求导方式);
设定代价函数--->设定损失函数 设定损失函数的目的是为了减少野值(outliers)对最小二乘求解的影响。常见的loos function有:CauchyLoss
(1) 以自动求导(AutoDiff)为例,分析建立problem的过程

  1. ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);

自定义的求导方式,在定义代价函数的时候,定义为ceres::AutoDiffCostFunctionCostFunctor为提前定义好的代价函数。后面的第一个数字表示残差的个数,其余后面每一个数字表示一个参数块,数字大小表示参数块包含的参数个数,可以参考文档。完整的定义为

  1. template <typename CostFunctor,
  2. int kNumResiduals, // Number of residuals, or ceres::DYNAMIC.
  3. int N0, // Number of parameters in block 0.
  4. int N1 = 0, // Number of parameters in block 1.
  5. int N2 = 0, // Number of parameters in block 2.
  6. int N3 = 0, // Number of parameters in block 3.
  7. int N4 = 0, // Number of parameters in block 4.
  8. int N5 = 0, // Number of parameters in block 5.
  9. int N6 = 0, // Number of parameters in block 6.
  10. int N7 = 0, // Number of parameters in block 7.
  11. int N8 = 0, // Number of parameters in block 8.
  12. int N9 = 0> // Number of parameters in block 9.
  13. class AutoDiffCostFunction : public
  14. SizedCostFunction<kNumResiduals, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9> {
  15. public:
  16. explicit AutoDiffCostFunction(CostFunctor* functor);
  17. // Ignore the template parameter kNumResiduals and use
  18. // num_residuals instead.
  19. AutoDiffCostFunction(CostFunctor* functor, int num_residuals);
  20. };

添加代价函数cost_function和损失函数NULL,其中x为状态量。

  1. Problem.AddResidualBlock(cost_function, NULL, &x);

代价函数的定义,

  1. struct CostFunctor
  2. {
  3. template <typename T>
  4. bool operator()(const T* const x, T* residual) const
  5. {
  6. //residual[0] = 0.5*pow(T(10.0) - x[0], 2);
  7. residual[0] = T(10.0) - x[0];
  8. return true;
  9. }
  10. };

(2) 自定义导数(Analytic Derivatives),分析建立problem的过程

  1. ceres::CostFunction* Cost_function1 = new CustomFunction_F1;

其中 CustomFunction_F1 为自定义的代价函数,,与自动求导的定义方式还是有明显的区别的。其具体的内容如下

  1. class CustomFunction_F4:
  2. public ceres::SizedCostFunction<1, 1, 1>
  3. {
  4. virtual ~CustomFunction_F4() {}
  5. virtual bool Evaluate(double const* const* parameters, double* redisuals, double** jacobins)const
  6. {
  7. double x1 = parameters[0][0];
  8. double x4 = parameters[1][0];
  9. redisuals[0] = sqrt(10.0)*(x1 - x4)*(x1 - x4);
  10. if(jacobins!=NULL && jacobins[0]!=NULL)
  11. {
  12. jacobins[0][0] = sqrt(10.0)*(2*x1 - 2*x4);
  13. }
  14. if(jacobins!=NULL && jacobins[0]!=NULL)
  15. {
  16. jacobins[1][0] = sqrt(10.0)*(2*x4 - 2*x1);
  17. }
  18. return true;
  19. }
  20. };

首先看自定义类的的继承类ceres::SizedCostFunction<1, 1, 1>,这里可以理解为固定大小的代价函数,第一个数字代表含有的残差个数,后面的每一个数字代表一个参数快,数字的大小代表参数快包含的参数的个数。具体参见定义

  1. template <typename CostFunctor,
  2. int kNumResiduals, // Number of residuals, or ceres::DYNAMIC.
  3. int N0, // Number of parameters in block 0.
  4. int N1 = 0, // Number of parameters in block 1.
  5. int N2 = 0, // Number of parameters in block 2.
  6. int N3 = 0, // Number of parameters in block 3.
  7. int N4 = 0, // Number of parameters in block 4.
  8. int N5 = 0, // Number of parameters in block 5.
  9. int N6 = 0, // Number of parameters in block 6.
  10. int N7 = 0, // Number of parameters in block 7.
  11. int N8 = 0, // Number of parameters in block 8.
  12. int N9 = 0> // Number of parameters in block 9.
  13. class AutoDiffCostFunction : public
  14. SizedCostFunction<kNumResiduals, N0, N1, N2, N3, N4, N5, N6, N7, N8, N9> {
  15. public:
  16. explicit AutoDiffCostFunction(CostFunctor* functor);
  17. // Ignore the template parameter kNumResiduals and use
  18. // num_residuals instead.
  19. AutoDiffCostFunction(CostFunctor* functor, int num_residuals);
  20. };

代价函数中redisuals[0]即为原函数,也是本次优化的残差函数。后面的雅克比矩阵按照参数块的顺序写出即可。

2.设置求解器属性
  求解器的属性设置也比较重要,具体设置的时候要参考Ceres的文档进行设置。如下所示,为一个简单的案例

  1. ceres::Solver::Options Option;
  2. Option.linear_solver_type = ceres::DENSE_QR;
  3. Option.minimizer_progress_to_stdout = true;

其中linear_solver_type为迭代过程中的线性求解方法,如QR分解等。
3.求解Problem

  1. ceres::Solver::Summary Summary;
  2. ceres::Solve(Option, &Problem, &Summary);

其中,Summary包含了求解problem中一些信息,如迭代次数、迭代时间、每次迭代的残差值等等。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注