@Xiaobuyi
2017-09-23T22:56:36.000000Z
字数 3414
阅读 3184
数学基础
自己根据对Ceres的学习,总结在使用Ceres进行优化问题的求解的时候一般要进行以下几个步骤:
1. 建立求解的Problem
ceres::Problem Problem;
建立求解Problem主要包含两个步骤:设定代价函数和添加残差。
设定代价函数--->设定求导方式 求导方式主要有自动求导和自定义导数两种方式(自动求导中的数值导数NumericDiffCostFunctor
等方式不加以区分,当做同类求导方式);
设定代价函数--->设定损失函数 设定损失函数的目的是为了减少野值(outliers)对最小二乘求解的影响。常见的loos function有:CauchyLoss
(1) 以自动求导(AutoDiff)为例,分析建立problem的过程
ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
自定义的求导方式,在定义代价函数的时候,定义为ceres::AutoDiffCostFunction
。CostFunctor
为提前定义好的代价函数。后面的第一个数字表示残差的个数,其余后面每一个数字表示一个参数块,数字大小表示参数块包含的参数个数,可以参考文档。完整的定义为
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 : public
SizedCostFunction<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);
};
添加代价函数cost_function
和损失函数NULL
,其中x
为状态量。
Problem.AddResidualBlock(cost_function, NULL, &x);
代价函数的定义,
struct CostFunctor
{
template <typename T>
bool operator()(const T* const x, T* residual) const
{
//residual[0] = 0.5*pow(T(10.0) - x[0], 2);
residual[0] = T(10.0) - x[0];
return true;
}
};
(2) 自定义导数(Analytic Derivatives),分析建立problem的过程
ceres::CostFunction* Cost_function1 = new CustomFunction_F1;
其中 CustomFunction_F1
为自定义的代价函数,,与自动求导的定义方式还是有明显的区别的。其具体的内容如下
class CustomFunction_F4:
public ceres::SizedCostFunction<1, 1, 1>
{
virtual ~CustomFunction_F4() {}
virtual bool Evaluate(double const* const* parameters, double* redisuals, double** jacobins)const
{
double x1 = parameters[0][0];
double x4 = parameters[1][0];
redisuals[0] = sqrt(10.0)*(x1 - x4)*(x1 - x4);
if(jacobins!=NULL && jacobins[0]!=NULL)
{
jacobins[0][0] = sqrt(10.0)*(2*x1 - 2*x4);
}
if(jacobins!=NULL && jacobins[0]!=NULL)
{
jacobins[1][0] = sqrt(10.0)*(2*x4 - 2*x1);
}
return true;
}
};
首先看自定义类的的继承类ceres::SizedCostFunction<1, 1, 1>
,这里可以理解为固定大小的代价函数,第一个数字代表含有的残差个数,后面的每一个数字代表一个参数快,数字的大小代表参数快包含的参数的个数。具体参见定义
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 : public
SizedCostFunction<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);
};
代价函数中redisuals[0]
即为原函数,也是本次优化的残差函数。后面的雅克比矩阵按照参数块的顺序写出即可。
2.设置求解器属性
求解器的属性设置也比较重要,具体设置的时候要参考Ceres的文档进行设置。如下所示,为一个简单的案例
ceres::Solver::Options Option;
Option.linear_solver_type = ceres::DENSE_QR;
Option.minimizer_progress_to_stdout = true;
其中linear_solver_type
为迭代过程中的线性求解方法,如QR分解等。
3.求解Problem
ceres::Solver::Summary Summary;
ceres::Solve(Option, &Problem, &Summary);
其中,Summary
包含了求解problem中一些信息,如迭代次数、迭代时间、每次迭代的残差值等等。