1.残差块构造的问题
1.使用模板定义的()运算符,可以让Ceres使用自动求导,这里的关键是使用模板。其次这种函数应该是有专门的名字叫做函数对象,也就是tutorial中的functor。对于函数对象返回bool值的又称为谓词,所以这里可能是函数对象或者谓词的使用吗?具体需要好好学学这方面
2.下面的定义可以看出,由于要优化的变量是x,但是这里却把x定义成了最严格的const类型,为什么?
3.
struct CostFunctor {
template <typename T>
// 这里是一个函数对象,返回值为bool的特殊函数对象称为谓词
bool operator()(const T* const x, T* residual) const {
residual[0] = 10.0 - x[0];
return true;
}
};
2.数值求导和自动求导
当残差的定义无法使用模板的时候,比如残差的计算使用到了库函数,此时只能使用数值求导。
数值求导对象的定义相对自动求导,只是多了一个指定数值求导方式的变量,如下所示。
CostFunction* cost_function =
new NumericDiffCostFunction<NumericDiffCostFunctor, ceres::CENTRAL, 1, 1>(
new NumericDiffCostFunctor);
problem.AddResidualBlock(cost_function, nullptr, &x);
一般来说,我们推荐自动微分而不是数字微分。使用 C++ 模板使自动微分高效,而数值微分代价高昂,容易出现数值错误,并导致收敛速度较慢。
3.解析导数
也就是自己推导雅克比,然后提供给ceres使用。
// A CostFunction implementing analytically derivatives for the
// function f(x) = 10 - x.
class QuadraticCostFunction // 二次的代价函数
: public SizedCostFunction<1 /* number of residuals */,
1 /* size of first parameter */> {
public:
virtual ~QuadraticCostFunction() {}
// parameters是二维的:x是n维向量,有m个数据点,所以最后是mxn,二维
// residuals是一维的:对于每个数据点x都有一个残差,m个数据点,所以是mx1,一维
// 雅克比是二维的:对于每个残差,他的梯度都是nx1的向量。对于m个残差,所以总的J是mxn维的
virtual bool Evaluate(double const* const* parameters,
double* residuals,
double** jacobians) const {
double x = parameters[0][0];
// f(x) = 10 - x.
residuals[0] = 10 - x;
// f'(x) = -1. Since there's only 1 parameter and that parameter
// has 1 dimension, there is only 1 element to fill in the
// jacobians.
//
// Since the Evaluate function can be called with the jacobians
// pointer equal to NULL, the Evaluate function must check to see
// if jacobians need to be computed.
//
// For this simple problem it is overkill to check if jacobians[0]
// is NULL, but in general when writing more complex
// CostFunctions, it is possible that Ceres may only demand the
// derivatives w.r.t. a subset of the parameter blocks.
if (jacobians != NULL && jacobians[0] != NULL) {
jacobians[0][0] = -1;
}
return true;
}
};