template <typename CostFunctor,
int kNumResiduals, // Number of residuals, or ceres::DYNAMIC.
int... Ns> // Size of each parameter block
class AutoDiffCostFunction : public
SizedCostFunction<kNumResiduals, Ns> {
public:
// Instantiate CostFunctor using the supplied arguments.
template<class ...Args>
explicit AutoDiffCostFunction(Args&& ...args);
explicit AutoDiffCostFunction(std::unique_ptr<CostFunctor> functor);
explicit AutoDiffCostFunction(CostFunctor* functor, ownership = TAKE_OWNERSHIP);
// Ignore the template parameter kNumResiduals and use
// num_residuals instead.
AutoDiffCostFunction(CostFunctor* functor,
int num_residuals,
ownership = TAKE_OWNERSHIP);
AutoDiffCostFunction(std::unique_ptr<CostFunctor> functor,
int num_residuals);
};
为了能够自动计算导数,用户在使用的时候需要自定义一个 class 并且重载 operator() 运算,且要使用模板参数 T。自动求导框架使用 Jet 对象来替换模板参数 T,以便于计算导数,但这些操作对用户来说都是隐藏的,因此用户只需要把这里的 T 当做一个 double 或者 float 类型的数据进行使用即可。括号运算符重载函数必须将残差的计算结果写入到最后一个非 const 参数中去,同时返回 true 预示着计算成功。
例如,考虑一个误差 e=k−x⊤y,其中 x,y 都是两个维度的向量,k 是一个常数,该误差的形式是最小二乘问题中的常见模式, x⊤y 值可能是一系列测量结果的模型期望值,其中每次测量都有一个 k 来构成代价函数实例,也就是说 x,y 使我们的待优化变量,实际中我们会有多个 k ,每个都会构成一个残差项。可以参考曲线拟合的思路。通过 Ceres 去实现上述建模的问题,我们首先要定义一个对象,并对括号运算符进行重载,计算残差。
auto* cost_function
= new AutoDiffCostFunction<MyScalarCostFunctor, 1, 2, 2>(1.0);
^ ^ ^
| | |
Dimension of residual ------+ | |
Dimension of x ----------------+ |
Dimension of y -------------------+
auto functor = std::make_unique<CostFunctorWithDynamicNumResiduals>(1.0);
auto* cost_function
= new AutoDiffCostFunction<CostFunctorWithDynamicNumResiduals,
DYNAMIC, 2, 2>(
std::move(functor), ^ ^ ^
runtime_number_of_residuals); <----+ | | |
| | | |
| | | |
Actual number of residuals ------+ | | |
Indicate dynamic number of residuals --------+ | |
Dimension of x ------------------------------------+ |
Dimension of y ---------------------------------------+
A common beginner’s error when first using AutoDiffCostFunction is to get the sizing wrong. In particular, there is a tendency to set the template parameters to (dimension of residual, number of parameters) instead of passing a dimension parameter for every parameter block. In the example above, that would be <MyScalarCostFunction, 1, 2>, which is missing the 2 as the last template argument.