在 Ceres Solver 中,不可以完全不用 AddParameterBlock
。虽然在某些情况下调用 AddResidualBlock
时会隐式注册参数块,但这并不是推荐的做法,可能会导致潜在问题。
为什么需要 AddParameterBlock
?
AddParameterBlock
的核心作用是向优化器声明“待优化的参数”及其属性,包括:
- 参数的维度(例如 SE3 位姿是7维);
- 局部参数化方式(例如 SE3 位姿需要用流形参数化,确保优化过程中参数满足约束,如四元数模长为1);
- 是否固定参数(通过
SetParameterBlockConstant
固定某些参数不参与优化); - 参数的上下边界(通过
SetParameterLowerBound
/SetParameterUpperBound
限制参数范围)。
什么情况下可以“省略” AddParameterBlock
?
Ceres 有一个简化机制:当你调用 AddResidualBlock
时,如果传入的参数块之前没有通过 AddParameterBlock
显式注册,Ceres 会自动隐式注册该参数块,默认使用“欧氏空间参数化”(即认为参数是普通的欧氏向量,没有特殊约束)。
例如:
// 假设 t_ex_li 是一个7维数组(未通过 AddParameterBlock 注册)
double t_ex_li[7] = {1,0,0,0, 0,0,0}; // 四元数+平移
// 调用 AddResidualBlock 时传入 t_ex_li,Ceres 会隐式注册它
problem.AddResidualBlock(cost_function, nullptr, t_ex_li, t_a, t_b);
为什么不推荐依赖“隐式注册”?
对于 SLAM 中位姿优化(涉及 SE3、SO3 等流形空间参数),必须显式调用 AddParameterBlock
,否则会出问题:
- 缺少局部参数化:SE3 位姿如果用默认的欧氏参数化,优化过程中可能会出现“非法参数”(如四元数模长不等于1),导致结果错误甚至崩溃。
- 无法固定参数:如果某些参数(如外参)需要固定不优化,必须通过
AddParameterBlock
注册后再调用SetParameterBlockConstant
。 - 无法设置边界:无法对参数施加上下限约束(如限制平移量范围)。
- 可读性差:隐式注册会让代码逻辑不清晰,后续维护时难以快速识别所有待优化参数。
正确做法
始终显式调用 AddParameterBlock
注册参数块,尤其是位姿、外参等流形空间参数:
// 1. 显式注册参数块,指定维度和局部参数化
problem.AddParameterBlock(se3_lidar2ins_[0].data(), 7, new LocalParameterizationSE3());
problem.AddParameterBlock(se3_opts_[pre].data(), 7, new LocalParameterizationSE3());
problem.AddParameterBlock(se3_opts_[cur].data(), 7, new LocalParameterizationSE3());
// 2. (可选)固定某些参数不优化
// problem.SetParameterBlockConstant(se3_lidar2ins_[0].data());
// 3. 关联代价函数和参数块
problem.AddResidualBlock(cost_function, loss_function,
se3_lidar2ins_[0].data(),
se3_opts_[pre].data(),
se3_opts_[cur].data());
总结
- 简单的欧氏空间参数(如标量、普通向量)可以依赖隐式注册,但不推荐;
- 涉及流形空间的参数(如 SE3、SO3 位姿)必须显式调用
AddParameterBlock
,并指定局部参数化; - 显式注册能保证代码清晰、参数约束正确,是 Ceres 编程的最佳实践。