参考:
求导方式:https://www.jianshu.com/p/39d8e0f31bbf
struct CostFunctor {
template <typename T>
bool operator()(const T* const x, T* residual) const {
residual[0] = T(10.0) - x[0];
return true;
}
};
自动求导仿函数实现的operator()
函数必须是模板函数,因为Ceres
内部求导要用到。
可直接理解T
为double
。
1.2 构造CostFunction
CostFunction* cost_function = new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
Ceres
构造非线性最小二乘问题要先定义代价函数,即上面的CostFunction
,然后通过problem.AddResidualBlock(cost_function, NULL, &x);
去构造问题进行求解。
AutoDiffCostFunction
的模板参数:
- 第1个参数是仿函数
- 第2个参数是残差块中残差的数量
- 第3个参数是第一个参数块中参数的数量
- 如果有多个参数块,依次写出各个参数块中参数的数量
各个参数说明如下:
CostFunction* cost_function
= new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor(1.0));
^ ^
| |
Dimension of residual ------+ |
Dimension of x ----------------+
2. 数值求导
有时,无法定义自动求导的模板仿函数,比如参数的估计调用了无法控制的库函数或外部函数。
这种情况无法使用自动求导了,数值求导便可以派上用场了。
数值求导用法类似,先定义仿函数,然后传递给NumericDiffCostFunction
,然后去构造问题求解。
2.1 定义仿函数
struct NumericDiffCostFunctor {
bool operator()(const double* const x, double* residual) const {
residual[0] = 10.0 - x[0];
return true;
}
};
与自动求导的反函数不同的是,数值求导的operator()
函数不是模板函数,而是直接使用了double
2.2 构造CostFunction
CostFunction* cost_function =
new NumericDiffCostFunction<NumericDiffCostFunctor, ceres::CENTRAL, 1, 1>(
new NumericDiffCostFunctor);
这里注意NumericDiffCostFunction
的模板参数:
- 第1个参数是仿函数
- 第2个参数是数值求到的方式。这里选用了
CENTRAL
,还有FORWARD
、RIDDERS
等 - 第3个参数是残差块中残差的数量
- 第4个参数是第一个参数块中参数的数量
- 如果有多个参数块,依次写出各个参数块中参数的数量
各个参数说明如下:
CostFunction* cost_function
= new NumericDiffCostFunction<NumericDiffCostFunctor, CENTRAL, 1, 1>(
new NumericDiffCostFunctor; ^ ^ ^
| | |
Finite Differencing Scheme -+ | |
Dimension of residual ------------+ |
Dimension of x ----------------------+
3. 解析求导
有些情况,自己写求导解析式,计算效率会更高一些。
如果使用解析求导的方式,就要自行计算残差和雅克比。
3.1 定义代价函数类
代价函数以$ f(x)=10-x $
为例.
class QuadraticCostFunction : public ceres::SizedCostFunction<1, 1> {
public:
virtual ~QuadraticCostFunction() {}
virtual bool Evaluate(double const* const* parameters,
double* residuals,
double** jacobians) const {
const double x = parameters[0][0];
residuals[0] = 10 - x;
// Compute the Jacobian if asked for.
if (jacobians != NULL && jacobians[0] != NULL) {
jacobians[0][0] = -1;
}
return true;
}
};
自定义的代价函数类要继承自CostFunction
或者SizedCostFunction
。其实SizedCostFunction
是继承自CostFunction
的,只是确定了尺寸(各个参数块的数量)。
Evaluate()
函数中计算了残差和雅克比
3.2 构造CostFunction
CostFunction* cost_function = new QuadraticCostFunction;
3.3 什么情况下使用解析求导
按照官方说明,以下情况可以使用解析求导
- 函数式简单,便于求出导数解析式
- 能使用
Matlab
Maple
Mathmatic
SymPy
等数学软件求出了导数的解析式 - 性能极致要求
- 没有其他好的方法去求导
- 喜欢手算导数
4. 总结
综上所述,建议优先使用自动求导和数值求导的方式,对雅克比计算擅长者和极致性能追求者可考虑使用解析求导的方式。
5. 参考
http://ceres-solver.org/nnls_tutorial.html
作者:book_02
链接:https://www.jianshu.com/p/39d8e0f31bbf
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。