18.Ceres官方教程-Modeling Non-linear Least Squares (6) Problem
Problem保持了非线性最小二乘问题的强化的边界。要创建最小二乘问题,可以使用Problem::AddResidualBlock()和Problem::AddParameterBlock()。
例如,下面这个Problem包含了三个参数块,维度分别为3,4,5。同时有两个残差块,维度分别是2和6。
double x1[] = { 1.0, 2.0, 3.0 };
double x2[] = { 1.0, 2.0, 3.0, 5.0 };
double x3[] = { 1.0, 2.0, 3.0, 6.0, 7.0 };
Problem problem;
problem.AddResidualBlock(new MyUnaryCostFunction(...), x1);
problem.AddResidualBlock(new MyBinaryCostFunction(...), x2, x3);
顾名思义,Problem::AddResidualBlock()向问题中添加了一个残差块。它添加了一个CostFunction,一个可选的LossFunction,并将CostFunction连接到一组参数块。
代价函数携带关于它所期望的参数块大小的信息。函数检查这些参数是否与parameter_blocks中列出的参数块的大小匹配。如果检测到不匹配,程序将中止。
Loss_function可以是nullptr,在这种情况下,该项的代价就是残差的平方范数。
用户可以使用Problem::AddParameterBlock()显式添加参数块。这将导致额外的正确性检查;然而,如果参数块不存在,Problem::AddResidualBlock()会隐式添加参数块,
因此不需要显式调用Problem::AddParameterBlock()。
AddParameterBlock()显式地向Problem添加了一个参数块。它还允许用户将LocalParameterization对象与参数块关联起来。具有相同参数的重复调用将被忽略。使用相同的双指针但大小不同的重复调用将导致未定义行为。
您可以使用Problem::SetParameterBlockConstant()将任何参数块设置为常量,并使用SetParameterBlockVariable()撤消此操作。
事实上,你可以设置任意数量的参数块为常量,Ceres足够聪明,能够知道你构建的问题的哪一部分取决于参数块,这些参数块可以自由更改,只需要花时间来解决它。
举个例子,如果你构造了一个有一百万个参数块和两百万个残差块的问题,然后把除了一个参数块之外的所有块都设置为常量,那么只有10个残差块依赖于这个非常量参数块。
如果你用一个参数块和10个残差块定义一个问题,那么Ceres在解决这个问题上所花费的计算工作量将是相同的。
Ownership
默认情况下,Problem拥有cost_function、loss_function和local_parameterization指针。这些对象在Problem的生命中仍然存在。
如果用户希望控制这些对象的销毁,那么他们可以通过在Problem::Options结构中设置相应的枚举来做到这一点。
请注意,即使Problem获得了cost_function和loss_function的所有权,它也不能阻止用户在另一个残差块中重用它们。
析构函数小心地对每个cost_function或loss_function指针只调用一次delete,不管有多少残差块引用它们。
TODO...
TODO...
TODO...
TODO...
TODO...
TODO...
TODO...
TODO...
在总代价函数中添加一个残差块。代价函数携带关于它所期望的参数块大小的信息。函数检查这些参数是否与parameter_blocks中列出的参数块的大小匹配。
如果检测到不匹配,程序将中止。Loss_function可以是nullptr,在这种情况下,该项的代价就是残差的平方范数。
形参块可以作为vector<double>或double指针一起传递。
用户可以选择使用AddParameterBlock显式添加参数块。这将导致额外的正确性检查;然而,如果参数块不存在,AddResidualBlock会隐式添加参数块,因此不需要显式调用AddParameterBlock。
Problem对象默认接受cost_function和loss_function指针的所有权。这些对象在Problem对象的生命周期中仍然存在。如果用户希望控制这些对象的销毁,那么他们可以通过在Options结构中设置相应的枚举来实现。
注意:即使Problem获得了cost_function和loss_function的所有权,它也不能阻止用户在另一个残差块中重用它们。析构函数小心地对每个cost_function或loss_function指针只调用一次delete,不管有多少残差块引用它们。
使用示例:
double x1[] = {1.0, 2.0, 3.0};
double x2[] = {1.0, 2.0, 5.0, 6.0};
double x3[] = {3.0, 6.0, 2.0, 5.0, 1.0};
vector<double*> v1;
v1.push_back(x1);
vector<double*> v2;
v2.push_back(x2);
v2.push_back(x1);
Problem problem;
problem.AddResidualBlock(new MyUnaryCostFunction(...), nullptr, x1);
problem.AddResidualBlock(new MyBinaryCostFunction(...), nullptr, x2, x1);
problem.AddResidualBlock(new MyUnaryCostFunction(...), nullptr, v1);
problem.AddResidualBlock(new MyBinaryCostFunction(...), nullptr, v2);
为问题添加一个大小适当的参数块。具有相同参数的重复调用将被忽略。使用相同的双指针但大小不同的重复调用将导致未定义行为。
向问题添加一个具有适当大小和参数化的参数块。具有相同参数的重复调用将被忽略。使用相同的双指针但大小不同的重复调用将导致未定义行为。