楼主: cydage
1457 0

[其他] ceres-solver各Cost Function的核心组件差异性比较及使用范例 [推广有奖]

  • 0关注
  • 0粉丝

等待验证会员

小学生

14%

还不是VIP/贵宾

-

威望
0
论坛币
0 个
通用积分
0
学术水平
0 点
热心指数
0 点
信用等级
0 点
经验
40 点
帖子
3
精华
0
在线时间
0 小时
注册时间
2018-1-30
最后登录
2018-1-30

楼主
cydage 发表于 2025-11-25 13:27:48 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币
在Ceres Solver中,以下几个核心组件被广泛用于构建和操作代价函数。它们在求导方式、参数灵活性以及适用场景上各有特点,合理选择可显著提升优化效率与开发便捷性。
NumericDiffCostFunction
### 核心组件功能对比 下表总结了各组件的关键特性,包括求导机制、参数配置的灵活性、典型应用场景及性能表现: | 组件名称 | 求导方式 | 参数灵活性 | 适用场景 | 性能特点 | |-------------------------------|----------------------------------------|------------------------------|------------------------------------------------------|--------------------------------------------| | NumericDiffCostFunction | 数值微分(支持前向差分、中心差分、Ridders方法) | 编译时固定参数块数量与维度 | 解析导数难以实现或成本较高的情况 | 精度较低,计算开销大,尤其在高维参数空间中更明显 | | AutoDiffCostFunction | 自动微分(基于模板元编程) | 编译时固定参数块数量与维度 | 存在解析导数但手动推导复杂的模型 | 高精度,高效计算,推荐作为首选方案 | | DynamicAutoDiffCostFunction | 自动微分 | 运行时动态确定参数块数量或大小 | 参数结构在运行期才确定的场景,如Bezier曲线拟合、神经网络训练 | 灵活性强,但性能略低于静态自动微分版本 | | CostFunctionToFunctor | 适配器模式,不直接参与求导 | 依赖所包装的CostFunction | 需将传统CostFunction转换为Functor以集成到其他系统中 | 提升代码复用性,不影响原始求导性能 | | NumericDiffFunctor | 数值微分(封装于Functor内部) | 编译时固定参数块数量与维度 | 需要将数值微分逻辑以Functor形式使用的场合 | 行为类似NumericDiffCostFunction,接口更灵活 | | ConditionedCostFunction | 不改变原求导方式,通过包装调整残差行为 | 依赖被包装的CostFunction | 需对不同残差施加缩放因子或条件控制其对总损失影响的场景 | 增强灵活性,可用于调节残差权重,不影响底层求导机制 |
AutoDiffCostFunction
### 典型使用示例说明 #### 示例一:NumericDiffCostFunction 的应用 **适用场景** 当目标函数的解析梯度难以推导或实现复杂时,例如简单的非线性最小二乘拟合问题,可采用数值微分方式快速构建代价函数。
DynamicAutoDiffCostFunction
**代码实现** ```cpp #include "ceres/ceres.h" struct CostFunctor { bool operator()(const double* const x, double* residual) const { residual[0] = 10.0 - x[0]; // 残差定义:r = 10 - x return true; } }; int main() { double x = 0.5; // 初始猜测值 ceres::Problem problem; // 使用中心差分法构造数值微分代价函数 ceres::CostFunction* cost_function = new ceres::NumericDiffCostFunction( new CostFunctor); problem.AddResidualBlock(cost_function, nullptr, &x); ceres::Solver::Options options; options.minimizer_progress_to_stdout = true; ceres::Solver::Summary summary; ceres::Solve(options, &problem, &summary); std::cout << summary.BriefReport() << "\n"; return 0; } ```
CostFunctionToFunctor
#### 示例二:AutoDiffCostFunction 的实现方式 **适用场景** 适用于具有明确数学表达式的模型,虽然可以手动求导,但过程繁琐易错,例如多项式拟合、指数衰减模型等。
NumericDiffFunctor
**代码实现** ```cpp #include "ceres/ceres.h" struct CostFunctor { template bool operator()(const T* const x, T* residual) const { residual[0] = T(10.0) - x[0]; // 残差函数:r = 10 - x,支持自动微分类型T return true; } }; int main() { double x = 0.5; // 初始值 ceres::Problem problem; // 构造基于自动微分的代价函数 ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction(new CostFunctor); problem.AddResidualBlock(cost_function, nullptr, &x); ceres::Solver::Options options; options.minimizer_progress_to_stdout = true; ceres::Solver::Summary summary; ceres::Solve(options, &problem, &summary); std::cout << summary.BriefReport() << "\n"; return 0; } ```
ConditionedCostFunction
以上内容展示了Ceres中主要代价函数组件的核心差异与实际应用方式。根据具体问题的需求,在精度、性能与开发便利性之间做出权衡,有助于构建高效稳健的优化系统。

3. DynamicAutoDiffCostFunction

应用场景:
适用于参数块的数量或尺寸在程序运行时才能确定的情形,例如进行Bezier曲线拟合等动态优化问题。

代码示例(简化版本):

#include "ceres/ceres.h"
#include <vector>

struct DynamicCostFunctor {
  template <typename T>
  bool operator()(const T* const* parameters, T* residuals) const {
    // 假设 parameters[0] 指向一个大小可变的参数块
    // 根据实际逻辑计算残差值:residuals[0] = ...
    return true;
  }
};

int main() {
  std::vector<double> parameters = {0.5, 1.0, 1.5}; // 动态参数块
  double residual;
  ceres::Problem problem;

  // 实际使用中需构造 DynamicAutoDiffCostFunction
  // 示例代码如下(注释内为示意结构)
  // auto* cost_function = new ceres::DynamicAutoDiffCostFunction<DynamicCostFunctor>(
  //     new DynamicCostFunctor, ceres::TAKE_OWNERSHIP, parameters.size());
  // problem.AddResidualBlock(cost_function, nullptr, parameters.data());

  ceres::Solver::Options options;
  ceres::Solver::Summary summary;

  // 执行求解过程
  // ceres::Solve(options, &problem, &summary);
  // std::cout << summary.BriefReport() << "\n";

  return 0;
}

说明:
在实际实现过程中,必须根据具体问题动态设定参数块的维度和数量,并正确初始化相关成本函数结构。

DynamicAutoDiffCostFunction

4. CostFunctionToFunctor

应用场景:
当需要将传统的 CostFunction 封装为函数对象(functor)形式时,可以使用 CostFunctionToFunctor 进行转换,从而便于与自动微分等其他Ceres组件集成使用。

代码示例(结合自动微分使用):

#include "ceres/ceres.h"
#include "ceres/cost_function_to_functor.h"

// 定义一个继承自 CostFunction 的类
struct MyCostFunction : public ceres::CostFunction {
  MyCostFunction() : ceres::CostFunction(1, 1) {} // 1个残差项,1个参数块

  virtual bool Evaluate(double const* const* parameters,
                       double* residuals,
                       double** jacobians) const {
    residuals[0] = 10.0 - parameters[0][0];
    if (jacobians && jacobians[0]) {
      jacobians[0][0] = -1.0;
    }
    return true;
  }
};

// 定义对应的函数对象
struct CostFunctor {
  template <typename T>
  bool operator()(const T* const x, T* residual) const {
    residual[0] = T(10.0) - x[0];
    return true;
  }
};

int main() {
  double x = 0.5;
  ceres::Problem problem;

  // 方法一:直接使用 AutoDiffCostFunction

CostFunction

Functor


// 5. NumericDiffFunctor
// 场景:适用于需要将数值微分逻辑封装在 Functor 中的情形。
// 虽然 Ceres 提供了 NumericDiffCostFunction 来自动处理数值微分,
// 但有时用户希望自定义内部计算过程,此时可结合 NumericDiffFunctor 使用。

Functor
#include "ceres/ceres.h" struct NumericDiffFunctor { bool operator()(const double* const x, double* residual) const { // 示例中仅实现简单的残差计算:residual = 10.0 - x[0] // 实际应用中,此处可嵌入更复杂的数值差分策略 residual[0] = 10.0 - x[0]; return true; } }; int main() { double x = 0.5; // 初始值设定为 0.5 ceres::Problem problem; // 利用 NumericDiffFunctor 构建基于数值微分的代价函数 // 采用中心差分法(CENTRAL),残差维度为1,参数维度为1 ceres::CostFunction* cost_function = new ceres::NumericDiffCostFunction<NumericDiffFunctor, ceres::CENTRAL, 1, 1>( new NumericDiffFunctor); // 将构建好的代价函数添加至优化问题中 problem.AddResidualBlock(cost_function, nullptr, &x); // 配置求解器选项并执行优化 ceres::Solver::Options options; ceres::Solver::Summary summary; ceres::Solve(options, &problem, &summary); // 输出简要的求解结果报告 std::cout << summary.BriefReport() << "\n"; return 0; }

// 6. ConditionedCostFunction
// 场景:当需要对残差向量进行特定变换(如加权、缩放或条件调整)时使用。
// ConditionedCostFunction 允许在不修改原始 CostFunction 的前提下,
// 对其输出的残差应用额外的处理逻辑,例如用于平衡不同量纲项之间的权重。

NumericDiffCostFunction
#include "ceres/ceres.h" #include "ceres/conditioned_cost_function.h" // 原始代价函数 Functor,用于生成基础残差 struct OriginalCostFunctor { template <typename T> bool operator()(const T* const x, T* residual) const { residual[0] = T(10.0) - x[0]; // 残差目标:使 x 接近 10.0 return true; } }; // 缩放 Functor,用于对原始残差进行线性变换 struct ScalingFunctor { template <typename T> bool operator()(const T* const residual, T* scaled_residual) const { scaled_residual[0] = 2.0 * residual[0]; // 残差乘以系数 2 return true; } }; int main() { double x = 0.5; // 参数初值 ceres::Problem problem; // 创建原始自动微分代价函数 ceres::CostFunction* original_cost_function = new ceres::AutoDiffCostFunction<OriginalCostFunctor, 1, 1>( new OriginalCostFunctor); // 构造条件化代价函数容器,并注册缩放操作 std::vector<ceres::ConditioningCostFunction::Conditioner*> conditioners; conditioners.push_back(new ceres::AutoDiffCostFunction<ScalingFunctor, 1, 1>(new ScalingFunctor)); ceres::CostFunction* conditioned_cost_function = new ceres::ConditionedCostFunction(original_cost_function, conditioners); // 添加带条件处理的残差块到问题中 problem.AddResidualBlock(conditioned_cost_function, nullptr, &x); // 设置并运行求解器 ceres::Solver::Options options; ceres::Solver::Summary summary; ceres::Solve(options, &problem, &summary); // 打印求解摘要信息 std::cout << summary.BriefReport() << "\n"; // 清理动态分配的 conditioner 内存 for (auto conditioner : conditioners) { delete conditioner; } return 0; }
#include "ceres/ceres.h"
#include "ceres/conditioned_cost_function.h"

struct MyCostFunctor {
  template <typename T>
  bool operator()(const T* const x, T* residual) const {
    residual[0] = T(10.0) - x[0];
    return true;
  }
};

int main() {
  double x = 0.5;
  ceres::Problem problem;

  // 创建原始的代价函数
  ceres::CostFunction* cost_function =
      new ceres::AutoDiffCostFunction<MyCostFunctor, 1, 1>(new MyCostFunctor);

  // 定义缩放矩阵(1x1 矩阵,值为 2.0)
  Eigen::Matrix<double, 1, 1> scaling_matrix;
  scaling_matrix << 2.0;

  // 使用 ConditionedCostFunction 对代价函数进行条件化处理,并传入缩放数据
  ceres::ConditionedCostFunction* conditioned_cost_function =
      new ceres::ConditionedCostFunction(cost_function, scaling_matrix.data());

  // 将残差块添加到优化问题中
  problem.AddResidualBlock(conditioned_cost_function, nullptr, &x);

  // 配置求解器选项并执行求解
  ceres::Solver::Options options;
  ceres::Solver::Summary summary;
  ceres::Solve(options, &problem, &summary);

  // 输出求解摘要信息
  std::cout << summary.BriefReport() << "\n";

  return 0;
}

更标准的使用方式如上所示。通过定义一个自定义的 CostFunctor 并结合 AutoDiffCostFunction 实现自动微分,再利用 ConditionedCostFunction 对原始代价函数施加缩放变换,从而改善数值条件。

ConditionedCostFunction
// 示例说明:简化情况下可直接使用固定缩放因子构造 ConditionedCostFunction
// 注意:实际应用中推荐使用矩阵形式传递缩放参数以保证类型和维度一致性

auto* conditioned_cost_function =
    new ceres::ConditionedCostFunction(original_cost_function, new double[1]{2.0});

problem.AddResidualBlock(conditioned_cost_function, nullptr, &x);

ceres::Solver::Options options;
ceres::Solver::Summary summary;
ceres::Solve(options, &problem, &summary);
std::cout << summary.BriefReport() << "\n";

return 0;
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:function Solver Solve Cost RES

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
扫码
拉您进交流群
GMT+8, 2026-2-12 00:51