Ceres Solver中 SetParameterization函数的完整详解
1. 函数作用
SetParameterization
用于设置参数块(parameter block)的局部参数化方式。
在 Ceres 中,优化变量通常是一个实数向量(double*
),默认情况下直接在欧式空间中进行加减更新。
但对于某些参数(如四元数、旋转矩阵、SE(3) 位姿等),它们存在流形(manifold)约束,不能直接用普通加法更新。
SetParameterization
就是用来告诉 Ceres 如何在局部更新这些参数,即:
- 从全局参数空间映射到局部优化空间(Local Parameterization)
- 保持参数合法性(例如四元数始终单位化)
2. 函数原型
bool Problem::SetParameterization(double* values,LocalParameterization* local_parameterization
);
参数说明
-
values
指向参数块数据的指针(在AddParameterBlock
或AddResidualBlock
时注册过的那块内存)。 -
local_parameterization
指向一个继承自LocalParameterization
的对象,用于定义局部更新规则。
常用的现成类:ceres::IdentityParameterization
(默认,加法更新)ceres::QuaternionParameterization
(保持四元数单位化)ceres::HomogeneousVectorParameterization
(保持齐次向量单位化)- 自定义派生类(实现任意流形更新规则)
3. 使用步骤
-
创建问题
ceres::Problem problem;
-
注册参数块
problem.AddParameterBlock(parameter_ptr, size);
-
设置局部参数化
problem.SetParameterization(parameter_ptr,new ceres::QuaternionParameterization());
-
添加残差块
problem.AddResidualBlock(cost_function, loss_function, parameter_ptr);
-
求解
ceres::Solver::Options options; ceres::Solver::Summary summary; ceres::Solve(options, &problem, &summary);
4. 代码示例
四元数优化(保持归一化)
#include <ceres/ceres.h>
#include <ceres/rotation.h>
#include <iostream>// 一个简单的残差:假设目标是四元数 q 接近 [1,0,0,0]
struct QuaternionResidual {template<typename T>bool operator()(const T* const q, T* residual) const {residual[0] = q[0] - T(1.0);residual[1] = q[1] - T(0.0);residual[2] = q[2] - T(0.0);residual[3] = q[3] - T(0.0);return true;}
};int main() {double q[4] = {0.35, 0.2, 0.3, 0.1}; // 非单位化四元数ceres::Problem problem;ceres::CostFunction* cost_function =new ceres::AutoDiffCostFunction<QuaternionResidual, 4, 4>(new QuaternionResidual);problem.AddParameterBlock(q, 4);// 设置局部参数化为四元数流形problem.SetParameterization(q, new ceres::QuaternionParameterization());problem.AddResidualBlock(cost_function, nullptr, q);ceres::Solver::Options options;options.minimizer_progress_to_stdout = true;ceres::Solver::Summary summary;ceres::Solve(options, &problem, &summary);std::cout << "Final quaternion: "<< q[0] << " " << q[1] << " " << q[2] << " " << q[3] << "\n";return 0;
}
5. 关键注意事项
-
参数化对象的生命周期
SetParameterization
会接管你传入的LocalParameterization*
的生命周期,Ceres 会在Problem
析构时自动释放,不要手动 delete。
-
维度匹配
- 局部参数化的
GlobalSize()
必须等于参数块的大小; LocalSize()
是优化变量在局部空间的维度(可能更小),例如四元数GlobalSize=4
,LocalSize=3
。
- 局部参数化的
-
同一参数块只能设置一次
- 对同一个指针重复调用会出错。
-
流形合法性
- 在
Plus()
函数中必须保持参数合法(例如归一化四元数)。
- 在
-
避免在 SetParameterization 之后直接修改原始数据
- 尤其是修改后不再满足流形约束,可能导致求解过程异常。
6. 高级用法
-
自定义局部参数化
class MyParameterization : public ceres::LocalParameterization { public:virtual bool Plus(const double* x, const double* delta, double* x_plus_delta) const {// 自定义更新规则// x: 当前全局参数// delta: 局部增量return true;}virtual bool ComputeJacobian(const double* x, double* jacobian) const {// 定义局部到全局的雅可比return true;}virtual int GlobalSize() const { return 4; }virtual int LocalSize() const { return 3; } };
-
固定部分维度(部分参数化)
Ceres 提供SubsetParameterization
,可以固定参数中的某些分量不参与优化。std::vector<int> constant_idx = {0, 1}; // 固定前两个分量 problem.SetParameterization(ptr,new ceres::SubsetParameterization(5, constant_idx));
-
组合参数化
使用ProductParameterization
将多个参数化方式组合,例如SE3 = Quaternion × Translation
。