使用ceres的要点
CostFunction
AutoDiffCostFunction
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:
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);
};
模板化 operator() 函数的类, 其中operator() 函数 被ceres称为functor.
要获得自动微分的成本函数,您必须定义一个带有模板化 operator()(functor)的类,该类根据模板参数 T 计算成本函数。
autodiff 框架将适当的 Jet 对象替换为 T 以计算必要时导数,但这是隐藏的,您应该像 T 是标量类型(例如双精度浮点数)一样编写函数。
该函数必须在最后一个参数(唯一的非常量参数)中写入计算值并返回 true 以表示成功。
例如
class MyScalarCostFunctor {
MyScalarCostFunctor(double k): k_(k) {}
template <typename T>
bool operator()(const T* const x , const T* const y, T* e) const {
e[0] = k_ - x[0] * y[0] - x[1] * y[1];
return true;
}
private:
double k_;
};
请注意,在 operator() 的声明中,输入参数 x 和 y 首先出现,并作为常量指针传递给 T 的数组。如果有三个输入参数,则第三个输入参数将在 y 之后。
输出总是最后一个参数,也是一个指向数组的指针。
在上面的例子中,e 是一个标量,所以只设置了 e[0]。
然后给定这个类定义,它的自动微分成本函数可以构造如下。
CostFunction* cost_function
= new AutoDiffCostFunction<MyScalarCostFunctor, 1, 2, 2>(
new MyScalarCostFunctor(1.0)); ^ ^ ^
| | |
Dimension of residual ------+ | |
Dimension of x ----------------+ |
Dimension of y -------------------+
如果 MyScalarCostFunctor的构造函数不需要参数, () 可以省略
CostFunction* cost_function
= new AutoDiffCostFunction<MyScalarCostFunctor, 1, 2, 2>(
new MyScalarCostFunctor);
在这个例子中,k 的每个测量值通常都有一个实例。
在上面的实例化中,MyScalarCostFunction, <1, 2, 2> 后面的模板参数将 functor描述为从两个参数(均为二维)计算一维输出。
默认情况下 AutoDiffCostFunction 将拥有传递给它的成本 functor 指针,即。 当 AutoDiffCostFunction 本身被删除时,将对 cost functor 调用 delete 。 然而,这在某些情况下可能是不可取的,因此也可以在构造函数中指定 DO_NOT_TAKE_OWNERSHIP 作为第二个参数,同时将指针传递给不需要被 AutoDiffCostFunction 删除的成本函子。 例如:
MyScalarCostFunctor functor(1.0)
CostFunction* cost_function
= new AutoDiffCostFunction<MyScalarCostFunctor, 1, 2, 2>(
&functor, DO_NOT_TAKE_OWNERSHIP);
AutoDiffCostFunction 还支持具有运行时确定的残差数的成本函数。 例如:
CostFunction* cost_function
= new AutoDiffCostFunction<MyScalarCostFunctor, DYNAMIC, 2, 2>(
new CostFunctorWithDynamicNumResiduals(1.0), ^ ^ ^
runtime_number_of_residuals); <----+ | | |
| | | |
| | | |
Actual number of residuals ------+ | | |
Indicate dynamic number of residuals --------+ | |
Dimension of x ------------------------------------+ |
Dimension of y ---------------------------------------+
NumericDiffCostFunction
数值微分, 不需要模板, 只需要重载()就行
在某些情况下,无法定义模板化成本函子,例如当残差的评估涉及对您无法控制的库函数的调用时。 在这种情况下,可以使用数值微分。 用户定义了一个计算残差值的函子,并使用它构造一个 NumericDiffCostFunction。 例如,对于 f(x)=10−x
相应的Functor是
// A cost functor that implements the residual r = 10 - x.
struct CostFunctor {
bool operator()(const double* const x, double* residual) const {
residual[0] = 10.0 - x[0];
return true;
}
};
// Build the problem.
Problem problem;
// Set up the only cost function (also known as residual). This uses
// numeric differentiation to obtain the derivative (jacobian).
CostFunction* cost_function =
new NumericDiffCostFunction<CostFunctor, CENTRAL, 1, 1> (new CostFunctor);
problem.AddResidualBlock(cost_function, NULL, &x);
Analytic Derivatives
在某些情况下,无法使用自动微分。 例如,可能的情况是,以封闭形式计算导数比依赖自动微分代码使用的链式规则更有效。
在这种情况下,可以提供您自己的残差和雅可比计算代码。 为此,如果您在编译时知道参数和残差的大小,请定义 CostFunction 或 SizedCostFunction 的子类。 例如,这里是实现 f(x)=10−x 的 SimpleCostFunction
// 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() {}
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;
}
};
// Build the problem.
Problem problem;
// Set up the only cost function (also known as residual).
CostFunction* cost_function = new QuadraticCostFunction;
problem.AddResidualBlock(cost_function, NULL, &x);
SimpleCostFunction::Evaluate 提供了一个输入参数数组、一个用于残差的输出数组残差和一个用于 Jacobian 的输出数组 jacobians。 jacobians 数组是可选的,Evaluate 需要检查它是否为非空,如果是,则用残差函数的导数值填充它。 在这种情况下,由于残差函数是线性的,雅可比矩阵是常数 4 。
从上面的代码片段可以看出,实现CostFunction对象有点繁琐。 我们建议除非您有充分的理由自己管理雅可比计算,否则您可以使用 AutoDiffCostFunction 或 NumericDiffCostFunction 来构建残差块。
CostFunctionToFunctor
CostFunctionToFunctor是一个适配器类,它允许用户在模板函数中使用CostFunction对象,用于自动微分。这允许用户无缝地混合analytic, numeric and automatic differentiation。
http://ceres-solver.org/nnls_modeling.html#costfunctiontofunctor
LossFunction
Ceres 包括许多预定义的损失函数。 为简单起见,我们描述了它们的未缩放版本。 下图以图形方式说明了它们的形状。 更多细节可以在 include/ceres/loss_function.h 中找到。
Shape of the various common loss functions.
class TrivialLoss
ρ(s)=s
class HuberLoss
ρ(s)={s2s√−1s≤1s>1
class SoftLOneLoss
ρ(s)=2(1+s−−−−√−1)
class CauchyLoss
ρ(s)=log(1+s)
class ArctanLoss
ρ(s)=arctan(s)
class TolerantLoss
ρ(s,a,b)=blog(1+e(s−a)/b)−blog(1+e−a/b)
class ComposedLoss
Given two loss functions f and g, implements the loss function h(s) = f(g(s)).
class ComposedLoss : public LossFunction {
public:
explicit ComposedLoss(const LossFunction* f,
Ownership ownership_f,
const LossFunction* g,
Ownership ownership_g);
};
class ScaledLoss
Sometimes you want to simply scale the output value of the robustifier. For example, you might want to weight different error terms differently (e.g., weight pixel reprojection errors differently from terrain errors).
Given a loss function ρ(s)
and a scalar a, ScaledLoss implements the function aρ(s)
.
Since we treat a nullptr Loss function as the Identity loss function, rho
= nullptr: is a valid input and will result in the input being scaled by a. This provides a simple way of implementing a scaled ResidualBlock.
class LossFunctionWrapper
Sometimes after the optimization problem has been constructed, we wish to mutate the scale of the loss function. For example, when performing estimation from data which has substantial outliers, convergence can be improved by starting out with a large scale, optimizing the problem and then reducing the scale. This can have better convergence behavior than just using a loss function with a small scale.
This templated class allows the user to implement a loss function whose scale can be mutated after an optimization problem has been constructed, e.g,
Problem problem;
// Add parameter blocks
CostFunction* cost_function =
new AutoDiffCostFunction < UW_Camera_Mapper, 2, 9, 3>(
new UW_Camera_Mapper(feature_x, feature_y));
LossFunctionWrapper* loss_function(new HuberLoss(1.0), TAKE_OWNERSHIP);
problem.AddResidualBlock(cost_function, loss_function, parameters);
Solver::Options options;
Solver::Summary summary;
Solve(options, &problem, &summary);
loss_function->Reset(new HuberLoss(1.0), TAKE_OWNERSHIP);
Solve(options, &problem, &summary);
LocalParameterization
class LocalParameterization
流型空间下的变量的更新方式定义.
可以在流形上执行的基本操作是 ⊞ 操作,它计算在 x 处的切线空间中沿 delta 移动的结果,然后投影回 x 所属的流形。也称为 Retraction,⊞ 是欧几里德空间中向量加法的推广。
⊞ 在 SO(3) 上是使用指数映射定义的,从切线空间 (R3) 到流形。 指数映射 Exp
LocalParameterization 接口允许用户定义参数块并与它们所属的流形相关联。 它通过定义 Plus (⊞) 运算及其在 Δ=0 处相对于 Δ 的导数来实现
ceres为四元数的更新定义了2个方法, 分别是 QuaternionParameterization 与 EigenQuaternionParameterization .
后者考虑了Eigen中四元数读取与存取时w的顺序.
class AutoDiffLocalParameterization
utoDiffLocalParameterization 对 LocalParameterization 的作用与 AutoDiffCostFunction 对 CostFunction 的作用相同。 它允许用户定义一个实现 LocalParameterization::Plus() 操作的模板化函子,它使用自动微分来实现雅可比矩阵的计算。
要获得自动微分局部参数化,您必须定义一个具有模板化 operator()(函子)的类,该类计算
x′=⊞(x,Δx),
eg:
struct QuaternionPlus {
template<typename T>
bool operator()(const T* x, const T* delta, T* x_plus_delta) const {
const T squared_norm_delta =
delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2];
T q_delta[4];
if (squared_norm_delta > 0.0) {
T norm_delta = sqrt(squared_norm_delta);
const T sin_delta_by_delta = sin(norm_delta) / norm_delta;
q_delta[0] = cos(norm_delta);
q_delta[1] = sin_delta_by_delta * delta[0];
q_delta[2] = sin_delta_by_delta * delta[1];
q_delta[3] = sin_delta_by_delta * delta[2];
} else {
// We do not just use q_delta = [1,0,0,0] here because that is a
// constant and when used for automatic differentiation will
// lead to a zero derivative. Instead we take a first order
// approximation and evaluate it at zero.
q_delta[0] = T(1.0);
q_delta[1] = delta[0];
q_delta[2] = delta[1];
q_delta[3] = delta[2];
}
Quaternionproduct(q_delta, x, x_plus_delta);
return true;
}
};
LocalParameterization* local_parameterization =
new AutoDiffLocalParameterization<QuaternionPlus, 4, 3>;
| |
Global Size ---------------+ |
Local Size -------------------+
Problem
问题包含稳健边界约束非线性最小二乘问题 (1)。 要创建最小二乘问题,请使用 Problem::AddResidalBlock() 和 Problem::AddParameterBlock() 方法。
例如,包含 3 个大小分别为 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()。
Problem::AddParameterBlock() 显式地向问题添加一个参数块。可选地,它也允许用户将 LocalParameterization 对象与参数块相关联。忽略具有相同参数的重复调用。使用相同的双指针但大小不同的重复调用会导致未定义的行为。
您可以使用 Problem::SetParameterBlockConstant() 将任何参数块设置为常量,并使用 SetParameterBlockVariable() 撤消此操作。
事实上,您可以将任意数量的参数块设置为常量,而 Ceres 足够聪明,可以弄清楚您构建的问题的哪一部分取决于可以自由更改并且只花时间解决它的参数块。因此,例如,如果您构建了一个具有 100 万个参数块和 200 万个残差块的问题,然后将除一个参数块之外的所有参数块设置为常量,并说只有 10 个残差块取决于这个非常量参数块。那么,如果你用一个参数块和 10 个残差块定义了一个问题,Ceres 解决这个问题所花费的计算量将是相同的。
所有权
问题默认拥有 cost_function、loss_function 和 local_parameterization 指针。 这些对象在问题的整个生命周期中都保持有效。 如果用户希望控制这些对象的销毁,那么他们可以通过在 Problem::Options 结构中设置相应的枚举来实现。
请注意,即使问题拥有 cost_function 和 loss_function 的所有权,也不排除用户在另一个残差块中重新使用它们。 析构函数只对每个 cost_function 或 loss_function 指针调用 delete 一次,而不管有多少剩余块引用它们。
class Problem::Options
Ownership Problem::Options::cost_function_ownership
Default: TAKE_OWNERSHIP
Ownership Problem::Options::loss_function_ownership
Default: TAKE_OWNERSHIP
Ownership Problem::Options::local_parameterization_ownership
Default: TAKE_OWNERSHIP
bool Problem::Options::enable_fast_removal
Default: false
bool Problem::Options::disable_all_safety_checks
Default: false
Context *Problem::Options::context
Default: nullptr
用于解决此问题的 Ceres 全局上下文。 这可能有助于减少计算时间,因为 Ceres 可以重用昂贵的对象来创建。 上下文对象可以是 nullptr,在这种情况下,Ceres 可能会创建一个。
Ceres 不拥有指针的所有权。
EvaluationCallback *Problem::Options::evaluation_callback
Default: nullptr
Problem类成员函数
template <typename Ts...> ResidualBlockId Problem::AddResidualBlock(CostFunction* cost_function, LossFunction* loss_function, double* x0, Ts... xs)
在总成本函数中添加一个残差块。成本函数携带有关它期望的参数块大小的信息。该函数检查它们是否与 parameter_blocks 中列出的参数块的大小匹配。如果检测到不匹配,程序将中止。 loss_function 可以是 nullptr,在这种情况下,术语的成本只是残差的平方范数。
参数块可以作为 vector<double*> 或 double* 指针一起传递。
用户可以选择使用 AddParameterBlock 显式添加参数块。这会导致额外的正确性检查;但是,如果参数块不存在,AddResidualBlock 会隐式添加参数块,因此不需要显式调用 AddParameterBlock。
问题对象默认拥有 cost_function 和 loss_function 指针。这些对象在问题对象的生命周期内一直有效。如果用户希望控制这些对象的销毁,那么他们可以通过在 Options 结构中设置相应的枚举来实现。
注意:即使问题拥有 cost_function 和 loss_function 的所有权,它也不排除用户在另一个残差块中重新使用它们。析构函数只对每个 cost_function 或 loss_function 指针调用 delete 一次,而不管有多少剩余块引用它们。
Example usage:
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);
void Problem::AddParameterBlock(double *values, int size, LocalParameterization *local_parameterization)
为问题添加具有适当大小的参数块, 并将参数与local_parameterization相关联。
忽略具有相同参数的重复调用。 使用相同的双指针但大小不同的重复调用会导致未定义的行为。
LocalParameterization定义了变量在流型空间下的更新方式.
void Problem::AddParameterBlock(double *values, int size)
为问题添加具有适当大小和参数化的参数块。 忽略具有相同参数的重复调用。 使用相同的双指针但大小不同的重复调用会导致未定义的行为。
void Problem::SetParameterBlockConstant(const double *values)
在优化过程中保持指定的参数块不变。
void Problem::SetParameterization(double *values, LocalParameterization *local_parameterization)
默认情况下,local_parameterization 归问题所有。 可以为多个参数设置相同的参数化; 析构函数小心地只删除一次局部参数化。 使用 nullptr 调用 SetParameterization 将清除任何先前设置的参数化。
将指定参数块与local_parameterization相关联, LocalParameterization定义了变量在流型空间下的更新方式.
void Problem::SetParameterLowerBound(double *values, int index, double lower_bound)
Set the lower bound for the parameter at position index in the parameter block corresponding to values. By default the lower bound is -std::numeric_limits<double>::max(), which is treated by the solver as the same as −∞
void Problem::SetParameterUpperBound(double *values, int index, double upper_bound)
Set the upper bound for the parameter at position index in the parameter block corresponding to values. By default the value is std::numeric_limits<double>::max(), which is treated by the solver as the same as ∞
rotation.h
Ceres的许多应用程序都涉及到一些优化问题,其中一些变量对应于旋转。为了减轻工作的痛苦,使用各种不同的旋转表示(角轴、四元数和矩阵),我们提供了一组简便的模板函数。这些函数是模板化的,这样用户就可以在Ceres求解器的自动微分框架中使用它们。
void AngleAxisToQuaternion(T const *angle_axis, T *quaternion)
将轴角表示的值转换为四元数。
angle_axis是三个一组的,它的角度用弧度表示,它的方向与旋转轴是一致的,quaternion是四个一组,它将包含生成的四元数。
void QuaternionToAngleAxis(T const *quaternion, T *angle_axis)
将四元数转换成等效的轴角表示。
quaternion必须是单位四元数,angle_axis将被填充 用弧度表示的旋转 ,方向是旋转轴方向。
RotationMatrixToAngleAxis
AngleAxisToRotationMatrix
RotationMatrixToAngleAxis
AngleAxisToRotationMatrix
在3x3旋转矩阵与轴角表示之间的转换。
EulerAnglesToRotationMatrix
EulerAnglesToRotationMatrix
在3x3旋转矩阵与欧拉角表示之间的转换。
{pitch,roll,yaw} 欧拉角是分别沿着 {x,y,z} 轴.
QuaternionToScaledRotation
QuaternionToScaledRotation
将4元向量转化为3×3缩放的旋转矩阵。
他选择的旋转是这样的,对于四元数 [1 0 0 0],得到一个单位矩阵,对于小的 a,b,c ,四元数[1 a b c]得到矩阵
I+2⎡⎣⎢⎢0c−b−c0ab−a0⎤⎦⎥⎥+O(q2)
对应着Rodrigues近似,最后一个矩阵表示[a b c ]的叉乘矩阵。结合 R(q1∗q2)=R(q1)∗R(q2)的特性,这一特性定义了从q到R的映射。
QuaternionToRotation
旋转矩阵用 Frobenius 归一化。RR′=I
(det(R)=1)
UnitQuaternionRotatePoint
通过四元数旋转一个点,会对四元数进行归一化。
QuaternionRotatePoint
要求四元数模长不为0。
QuaternionProduct
四元数乘法
CrossProduct
四元数叉乘
AngleAxisRotatePoint
轴角旋转点
Cubic Interpolation
优化问题通常涉及以一个值表形式给出的函数,例如一个图像。计算这些函数及其导数需要对这些值进行插值,插值表函数是一个很大的研究领域,有很多库实现了各种插值方案。然而,在Ceres的自动微分框架中使用它们是相当痛苦的。为此,Ceres提供了对一维和二维表格函数进行插值的能力。
一维插值是基于 Cubic Hermite Spline,也被称为Catmull-Rom Spline。这就产生了一阶可微的内插函数。二维插值方案是一维方案的推广,插值函数被认为在两个维度是可分离的。
http://ceres-solver.org/nnls_modeling.html#cubic-interpolation
Ceres-Solver学习笔记(6)
https://blog.csdn.net/HUAJUN998/article/details/76222745?spm=1001.2014.3001.5501
Solver
class Solver::Options 与 Solver::Summary
Solver::Options 整体的控制求解器的行为,我们把变量设置和默认值列举如下。
http://ceres-solver.org/nnls_solving.html#solver-summary
Ceres-Solver学习笔记(8) https://blog.csdn.net/HUAJUN998/article/details/76341292?spm=1001.2014.3001.5501
// Build and solve the problem.
Solver::Options options;
options.minimizer_progress_to_stdout = true;
options.max_num_iterations = 500;
options.use_nonmonotonic_steps = false;
options.num_threads = 1;
options.linear_solver_type = ceres::DENSE_QR;
options.linear_solver_type = ceres::DENSE_SCHUR;
Solver::Summary summary;
Solve(options, &problem, &summary);
std::cout << summary.FullReport() << "\n";
std::cout << summary.BriefReport() << "\n";
DEFINE_string(linear_solver, "sparse_schur", "Options are: "
"sparse_schur, dense_schur, iterative_schur, sparse_normal_cholesky, "
"dense_qr, dense_normal_cholesky and cgnr.");
void SetLinearSolver(Solver::Options* options) {
CHECK(StringToLinearSolverType(FLAGS_linear_solver,
&options->linear_solver_type));
// 设置PreconditionerType,可选项为:"identity, jacobi,
// schur_jacobi, cluster_jacobi, ""cluster_tridiagonal."
CHECK(StringToPreconditionerType(FLAGS_preconditioner,
&options->preconditioner_type));
// 设置VisibilityClusteringType,可选项为:"single_linkage,
// canonical_views"
CHECK(StringToVisibilityClusteringType(FLAGS_visibility_clustering,
&options->visibility_clustering_type));
// 设置SparseLinearAlgebraLibraryType,可选项为:"suite_sparse,
// cx_sparse"
CHECK(StringToSparseLinearAlgebraLibraryType(
FLAGS_sparse_linear_algebra_library,
&options->sparse_linear_algebra_library_type));
// 设置DenseLinearAlgebraLibraryType,可选项为:"eigen,
// lapack."
CHECK(StringToDenseLinearAlgebraLibraryType(
FLAGS_dense_linear_algebra_library,
&options->dense_linear_algebra_library_type));
// 线程数
options->num_linear_solver_threads = FLAGS_num_threads;
// 是否使用舍尔补
options->use_explicit_schur_complement = FLAGS_explicit_schur_complement;
}
void SetMinimizerOptions(Solver::Options* options) {
// 最大迭代次数
options->max_num_iterations = FLAGS_num_iterations;
// 迭代过程是否输出
options->minimizer_progress_to_stdout = true;
// 线程数
options->num_threads = FLAGS_num_threads;
// 每次迭代的精度
options->eta = FLAGS_eta;
// 求解时间
options->max_solver_time_in_seconds = FLAGS_max_solver_time;
// 信任区间算法/nonmonotic
options->use_nonmonotonic_steps = FLAGS_nonmonotonic_steps;
if (FLAGS_line_search) {
options->minimizer_type = ceres::LINE_SEARCH;
}
CHECK(StringToTrustRegionStrategyType(FLAGS_trust_region_strategy,
&options->trust_region_strategy_type));
// 可选项:raditional_dogleg,subspace_dogleg
CHECK(StringToDoglegType(FLAGS_dogleg, &options->dogleg_type));
options->use_inner_iterations = FLAGS_inner_iterations;
}
bool Solver::Options::minimizer_progress_to_stdout
Default: false
默认情况下,Minimizer 进度会根据 vlog 级别记录到 STDERR。 如果此标志设置为 true,并且 Solver::Options::logging_type 不是 SILENT,则将日志输出发送到 STDOUT。
对于 TRUST_REGION_MINIMIZER,进度显示如下
iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time
0 4.185660e+06 0.00e+00 1.09e+08 0.00e+00 0.00e+00 1.00e+04 0 7.59e-02 3.37e-01
1 1.062590e+05 4.08e+06 8.99e+06 5.36e+02 9.82e-01 3.00e+04 1 1.65e-01 5.03e-01
2 4.992817e+04 5.63e+04 8.32e+06 3.19e+02 6.52e-01 3.09e+04 1 1.45e-01 6.48e-01
int Solver::Options::max_num_iterations
Default: 50
求解器应运行的最大迭代次数。
bool Solver::Options::use_nonmonotonic_steps
Default: false
放宽对信任域算法采取严格递减步骤的要求。 允许短时间内cost值处于上升状态
int Solver::Options::num_threads
Default: 1
Ceres 用于评估雅可比行列式的线程数
LinearSolverType Solver::Options::linear_solver_type
Default: SPARSE_NORMAL_CHOLESKY / DENSE_QR
用于在 Levenberg-Marquardt 算法的每次迭代中计算线性最小二乘问题的解的线性求解器类型。 如果 Ceres 构建时支持 SuiteSparse 或 CXSparse 或 Eigen 的稀疏 Cholesky 分解,则默认为 SPARSE_NORMAL_CHOLESKY,否则为 DENSE_QR。
MinimizerType Solver::Options::minimizer_type
Default: TRUST_REGION
LINE_SEARCH 和 TRUST_REGION 算法之间进行选择。 有关更多详细信息,请参阅信任区域方法和行搜索方