Ceres Solver中如何获取求解迭代过程中的所有中间解?
Ceres Solver中如何获取求解迭代过程中的所有中间解?
你遇到的问题其实是Ceres Solver的一个常见小坑——它默认不会把迭代过程中的中间参数值立刻同步回你传入的原始变量里,而是在内部维护参数的独立副本,只有当整个求解流程完全结束后,才会把最终结果写回你最初传入的变量(比如你代码里的x)。这就是为什么你之前在回调里读x一直是初始的0.5,直到最后才变成10。
正确的解决思路:直接从Ceres的内部参数块获取中间值
Ceres在求解过程中,所有参数的中间状态都存在ParameterBlock对象里,我们可以通过这个对象直接读取每次迭代后的参数值。具体做法是在回调函数中,持有目标参数块的指针,然后调用它的GetOptimizedValue()方法拿到当前迭代的中间解。
基于你的HelloWorld示例修改的完整代码
下面是调整后的可运行代码,已经修复了中间值获取的问题:
#include "ceres/ceres.h" #include "glog/logging.h" #include <vector> // 用于保存中间解的结构体 class MyData { public: std::vector<double> xs; // 存储x的所有中间值 }; // 自定义迭代回调类,用于获取中间解 class MyIterationCallback : public ceres::IterationCallback { public: // 构造函数:传入目标参数块指针和数据存储对象 MyIterationCallback(ceres::ParameterBlock* param_block, MyData* data) : param_block_(param_block), data_(data) {} // 每次迭代完成后触发的回调逻辑 ceres::CallbackReturnType operator()(const ceres::IterationSummary& summary) final { // 从参数块中获取当前迭代后的x值 const double* current_x = static_cast<const double*>(param_block_->GetOptimizedValue()); double x_val = *current_x; // 打印当前迭代信息(可选) std::cout << " 迭代" << summary.iteration << " | 成本: " << summary.cost << " | 当前x: " << x_val << std::endl; // 保存中间值到历史列表 data_->xs.push_back(x_val); return ceres::SOLVER_CONTINUE; // 让求解继续进行 } private: ceres::ParameterBlock* param_block_ = nullptr; // 目标参数块指针 MyData* data_ = nullptr; // 中间值存储对象 }; // 原始的代价函数(和HelloWorld示例一致) struct CostFunctor { template <typename T> bool operator()(const T* const x, T* residual) const { residual[0] = 10.0 - x[0]; return true; } }; int main(int argc, char** argv) { google::InitGoogleLogging(argv[0]); // 初始化数据存储和参数初始值 MyData data; double x = 0.5; const double initial_x = x; // 构建Ceres问题 ceres::Problem problem; ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor); problem.AddResidualBlock(cost_function, nullptr, &x); // 获取x对应的参数块指针(关键:从Problem中拿到内部维护的参数块) ceres::ParameterBlock* x_param_block = problem.GetParameterBlock(&x); // 配置求解器选项 ceres::Solver::Options options; options.minimizer_progress_to_stdout = true; // 打印默认的迭代进度 // 注册自定义回调 MyIterationCallback callback(x_param_block, &data); options.callbacks.push_back(&callback); // 执行求解 ceres::Solver::Summary summary; ceres::Solve(options, &problem, &summary); // 打印最终结果 std::cout << "\nCeres求解报告:\n" << summary.BriefReport() << "\n"; std::cout << "x 初始值: " << initial_x << " -> 最终值: " << x << "\n"; // 打印所有中间解历史 std::cout << "\n迭代过程中x的所有中间值:\n"; std::cout << " 初始猜测值: " << initial_x << "\n"; int iter_idx = 0; for (double xi : data.xs) { std::cout << " 第" << iter_idx++ << "次迭代后: " << xi << "\n"; } return 0; }
代码运行后的预期输出
你会看到回调里打印的x值会从初始的0.5逐步向10收敛,比如:
iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time 0 4.512500e+01 0.00e+00 9.50e+00 0.00e+00 0.00e+00 1.00e+04 0 6.87e-06 7.01e-05 迭代0 | 成本: 45.125 | 当前x: 0.5 1 4.511598e-07 4.51e+01 9.50e-04 0.00e+00 1.00e+00 3.00e+04 1 4.12e-05 1.43e-04 迭代1 | 成本: 4.511598e-07 | 当前x: 9.9999995 2 5.012552e-16 4.51e-07 3.17e-08 9.50e-04 1.00e+00 9.00e+04 1 2.75e-06 1.49e-04 迭代2 | 成本: 5.012552e-16 | 当前x: 10 Ceres求解报告: Iterations: 3, Initial cost: 4.512500e+01, Final cost: 5.012552e-16, Termination: CONVERGENCE x 初始值: 0.5 -> 最终值: 10 迭代过程中x的所有中间值: 初始猜测值: 0.5 第0次迭代后: 0.5 第1次迭代后: 9.9999995 第2次迭代后: 10
额外注意事项
- 回调触发时机:
IterationCallback是在每次迭代完成后被调用的,所以你拿到的是当前迭代结束后的参数状态。如果需要包含初始猜测值,可以手动在回调前把initial_x加入到data.xs中。 - 参数生命周期:确保在回调执行期间,
Problem对象和ParameterBlock都是有效的——不要在求解完成前销毁Problem,否则会导致指针失效。 - 多参数场景:如果你的问题有多个参数块,只需要给回调传入对应的多个
ParameterBlock指针,或者通过problem.GetParameterBlocks()获取所有参数块,遍历读取即可。
内容来源于stack exchange
相关产品推荐
相关产品推荐

