贝利信息

c++中如何使用Ceres Solver进行非线性优化? (最小二乘法实例)

日期:2026-01-15 00:00 / 作者:尼克
Ceres Solver 的 CostFunction 是用于定义残差函数的接口,必须以 ∑ᵢ ‖fᵢ(x)‖² 形式表达优化问题;用户需实现 operator() 计算残差,支持自动/数值微分,参数块顺序与维度须严格匹配。

什么是 Ceres Solver 的 CostFunction

Ceres 中的优化问题必须表达为残差(residual)形式,即最小化 ∑ᵢ ‖fᵢ(x)‖²。你不能直接写目标函数,而是要实现一个 CostFunction 子类或使用 AutoDiffCostFunction / NumericDiffCostFunction 包装计算逻辑。

最常用的是自动微分:你只提供残差计算的 operator(),Ceres 自动求导。关键点:

如何构造并运行一个最小二乘优化问题?

以拟合曲线 y = a·exp(b·x) + c 为例,有 3 个待估参数 a, b, c,已知若干 (x_i, y_i) 数据点:

struct ExponentialResidual {
  ExponentialResidual(double x, double y) : x_(x), y_(y) {}
  template 
  bool operator()(const T* const a, const T* const b, const T* const c,
                  T* residual) const {
    residual[0] = y_ - (*a) * exp(*b * x_) - (*c);
    return true;
  }
 private:
  const double x_, y_;
};

// 构建问题 ceres::Problem problem; double a = 1.0, b = 0.1, c = 0.0; std::vector> data = {{0.0, 1.2}, {1.0, 3.1}, {2.0, 8.0}}; for (const auto& p : data) { ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction( new ExponentialResidual(p.first, p.second)); problem.AddResidualBlock(cost_function, nullptr, &a, &b, &c); }

// 配置并求解 ceres::Solver::Options options; options.linear_solver_type = ceres::DENSE_QR; options.minimizer_progress_to_stdout = true;

ceres::Solver::Summary summary; ceres::Solve(options, &problem, &summary); printf("Final a=%f, b=%f, c=%f\n", a, b, c);

注意:AutoDiffCostFunction 表示:残差维度 1,三个参数块各占 1 维;若参数是向量(如旋转四元数),维度就得改成 4。

为什么优化不收敛或报 Check failed: is_valid

常见原因不是模型本身,而是数值或配置问题:

C++ 编译时链接失败:找不到 ceres::Problemceres::Solve

不是头文件没包含,而是链接阶段缺失库或顺序错误。确保:

最易忽略的一点:Ceres 默认关闭 OpenMP 和 SuiteSparse,如果你的优化变量多于几百维,又没开 -DCERES_USE_EIGEN_SPARSE=ON-DCERES_USESuiteSparse=ON,它会静默退回到稠密求解器,速度骤降且内存爆炸 —— 这类问题不会报错,只会让你等十分钟还不出结果。