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

什么是 Ceres Solver 的 CostFunction?
Ceres 中的优化问题必须表达为残差(residual)形式,即最小化 ∑ᵢ ‖fᵢ(x)‖²。你不能直接写目标函数,而是要实现一个 CostFunction 子类或使用 AutoDiffCostFunction / NumericDiffCostFunction 包装计算逻辑。
最常用的是自动微分:你只提供残差计算的 operator(),Ceres 自动求导。关键点:
-
operator()的第一个参数是参数块指针数组(const double* const*),第二个是残差输出(double*),返回bool - 参数块顺序必须和
AddParameterBlock()一致,且每个块大小要匹配 - 残差维度(输出长度)和参数块维度(输入长度)在构造
AutoDiffCostFunction时就固定,写错会导致段错误或收敛失败
如何构造并运行一个最小二乘优化问题?
以拟合曲线 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。
立即学习“C++免费学习笔记(深入)”;
为什么优化不收敛或报 Check failed: is_valid?
常见原因不是模型本身,而是数值或配置问题:
- 初始值太离谱(例如
b初始为1e6导致exp(b*x)溢出),改用合理量级初值(如对数尺度预估) - 残差函数里用了未定义行为:比如
log(x)但x ≤ 0,或除零、sqrt负数 —— 加if (x 并返回false让 Ceres 跳过该残差 - 没设置
options.max_num_iterations,而问题病态导致迭代太久,建议设为100左右先试 - 用了
nullptr作为损失函数(loss function),但数据含大离群点,应改用new ceres::HuberLoss(0.1)
C++ 编译时链接失败:找不到 ceres::Problem 或 ceres::Solve
不是头文件没包含,而是链接阶段缺失库或顺序错误。确保:
- 链接时包含
-lceres,且放在你的目标文件之后(如g++ main.o -lceres -lglog -lgflags -lpthread -o app) - 如果用 CMake,不要只写
find_package(Ceres),还要target_link_libraries(your_target ${CERES_LIBRARIES}),且${CERES_LIBRARIES}必须在你自己源文件编译目标之后 - 确认安装的 Ceres 是带
Solver支持的(非 minimal 版本),检查ceres-config.cmake中是否含ceres_FOUND和CERES_SOLVER_LIBRARY
最易忽略的一点:Ceres 默认关闭 OpenMP 和 SuiteSparse,如果你的优化变量多于几百维,又没开 -DCERES_USE_EIGEN_SPARSE=ON 或 -DCERES_USESuiteSparse=ON,它会静默退回到稠密求解器,速度骤降且内存爆炸 —— 这类问题不会报错,只会让你等十分钟还不出结果。









