最常见的一个例子是 链接,不再复制粘贴
还是之前的曲线拟合问题: ,如果改用解析求导,需要构建一个继承CostFunction
的类。核心函数是bool CostFunction: :Evaluate(double const *const *parameters, double *residuals, double **jacobians)
,计算残差向量和雅可比矩阵
parameters: parameters是一个大小为
CostFunction::parameter_block_sizes_.size()
(输入参数块的数量和大小)的数组,parameters[i]是一个大小为parameter_block_sizes_[i]
的数组,其中包含CostFunction
所依赖的第i个参数块。parameters不会为nullptrresiduals: 是一个大小为
num_residuals_
(输出残差的个数)的数组。residuals也不会为nullptrjacobians: 是一个大小为
CostFunction::parameter_block_sizes_.size()
的数组。如果jacobians
是nullptr,用户只需要计算残差jacobians[i]: 是一个大小为
num_residuals x parameter_block_sizes_[i]
的行数组,如果jacobians[i]
不为nullptr,用户需要计算关于parameters[i]
的残差向量的雅可比矩阵,并将其存储在这个数组中,即
如果jacobians
为nullptr,通常我们只需要在Evaluate
函数中实现residuals
的计算,jacobians
后面可以通过Ceres提供的自动求导(数值求导)替代,否则,还需要在Evaluate
函数中实现jacobians
的计算
如果parameters
大小和residuals
大小在编译时是已知的,就可以使用SizeCostFunction
,该函数可以将paramters大小和residuals大小设置为模板参数,用户只需要在使用的时候给模板参数赋值就可以1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28// 模板参数依次为: number of residuals, first parameter的维度
class MyCostFun : public SizedCostFunction<1, 1, 1, 1>
{
public:
MyCostFun(double x, double y):
m_x(x), m_y(y){}
virtual bool Evaluate(double const* const* parameters,
double* residuals,
double** jacobians) const
{
double a = parameters[0][0];
double b = parameters[0][1];
double c = parameters[0][2];
residuals[0] = m_y - exp(a*m_x*m_x + b*m_x + c);
if (jacobians != NULL && jacobians[0] != NULL)
{
jacobians[0][0] = -exp(a*m_x*m_x + b*m_x + c)* m_x * m_x;
jacobians[0][1] = -exp(a*m_x*m_x + b*m_x + c)* m_x;
jacobians[0][2] = -exp(a*m_x*m_x + b*m_x + c);
}
return true;
}
protected:
double m_x;
double m_y;
};
拟合 时,使用SizedCostFunction<1, 2>
,只计算雅格比jacobians[0][0]
和 jacobians[0][1]
1 | int main(int argv, char** argc) |
一开始我设置代价函数的模板为SizedCostFunction<1, 3>
,结果运行报错: [problem_impl.cc:286] Check failed: num_parameter_blocks == cost_function->parameter_block_sizes().size() (3 vs. 1)
问题在于parameter_block_sizes
只有1,而我们需要3,也就是对应abc三个参数。读SizedCostFunction
源码发现,模板定义为template <int kNumResiduals, int... Ns>
,构造函数只有两行1
2set_num_residuals(kNumResiduals);
*mutable_parameter_block_sizes() = std::vector<int32_t>{Ns...};
因此从第2个模板参数开始,有几个参数就对应parameter_block_sizes
,改为SizedCostFunction<1, 1, 1, 1>