Ceres Solver优化库学习笔记

news/2025/11/26 20:30:01/文章来源:https://www.cnblogs.com/y-z-h/p/19274572

1. Ceres Solver

1.1 简介

  1. 定位:一个功能强大、通用的非线性最小二乘问题求解器。
  2. 哲学:提供一套丰富的API,让用户能够轻松地定义和构建残差项,并自动或手动指定微分方式,最终求解这些残差的平方和的最小值。它更像一个“数学工具”。
  3. 由Google维护,非常活跃。
  4. 主要开源项目:VINS、OB-GINS

1.2 使用示例

// 在 problem 中构建最小二乘问题
ceres::Problem problem;
// 在 problem 中加入残差块
// cost_function 里面构造残差及雅克比矩阵
// loss_function 抗差函数,不用就传 nullptr
// parameter_blocks 待估状态量
problem.AddResidualBlock(CostFunction* cost_function, LossFunction* loss_function, const std::vector<double*>& parameter_blocks);
// 通过 options 配置最小二乘求解器,没特殊情况可以都默认,具体情况可查阅官网
ceres::Solver::Options options;
// 通过 summary 接收求解结果
ceres::Solver::Summary summary;
// 进行求解器求解
ceres::Solve(options, &problem, &summary);

非线性约束最小二乘按照求导方式可以分为:自动求导(Automatic Derivatives),数值求导(Numeric derivatives),解析求导(Analytic Derivatives)。
具体选哪种求导方式进行计算呢?ceres solver官方推荐:

(1)优先选择用Automatic Derivatives;
(2)如果Analytic Derivatives比Automatic Derivatives计算效率或精度上有明显优势,才用Analytic Derivatives
(3)如果Automatic Derivatives和Analytic Derivatives都不能解决问题,再考虑用Numeric derivatives

这里主要介绍Automatic Derivatives和Analytic Derivatives的用法,Numeric derivatives的具体用法可以在Ceres Solver官网查看

1.3 自动求导

需要在结构体中重载operator()函数,函数里面只需要构建残差,然后调用ceres::AutoDiffCostFunction()函数进行自动求导。
operator()形参中是(参数块[0], 参数块[1],..., 参数块[n],残差块维数),其需与ceres::AutoDiffCostFunction<自己定义的结构体名称,残差块维数,参数块[0]维数, 参数块[1]维数,..., 参数块[n]维数>()对应,其还需与ceres::Problem::AddResidualBlock()形参中的参数块对应。如果参数块分开写最多可以写10个参数块,大于10个就需要组成std::vector的形式。

  ResidualBlockId AddResidualBlock(CostFunction* cost_function,LossFunction* loss_function,const std::vector<double*>& parameter_blocks);ResidualBlockId AddResidualBlock(CostFunction* cost_function,LossFunction* loss_function,double* x0, double* x1, double* x2,double* x3, double* x4, double* x5,double* x6, double* x7, double* x8,double* x9);
#include <iostream>
#include <opencv2/core/core.hpp>
#include <ceres/ceres.h>
#include <chrono>using namespace std;struct AutoCostFunctor
{AutoCostFunctor(double x, double y): m_x(x), m_y(y) {};template <typename T_>bool operator() (const T_* const a, const T_* const b, const T_* const c, T_* residual) const{residual[0] = m_y - exp(a[0] * m_x * m_x + b[0] * m_x + c[0]);return true;}const double m_x;const double m_y;
};int main(int argc, char **argv) {double ar = 1.0, br = 2.0, cr = 1.0;         // 真实参数值double ae = 2.0, be = -1.0, ce = 5.0;        // 估计参数值int N = 100;                                 // 数据点double w_sigma = 1.0;                        // 噪声Sigma值double inv_sigma = 1.0 / w_sigma;cv::RNG rng;                                 // OpenCV随机数产生器vector<double> x_data, y_data;      // 数据for (int i = 0; i < N; i++) {double x = i / 100.0;x_data.push_back(x);y_data.push_back(exp(ar * x * x + br * x + cr) + rng.gaussian(w_sigma * w_sigma));}double abc[3] = {ae, be, ce};cout << "true ae: " << ar << ",\tbe: " << br << ",\tce: " << cr << endl;cout << "initial ae: " << ae << ",\tbe: " << be << ",\tce: " << ce << endl;ceres::LossFunction* loss_function = new ceres::HuberLoss(1.0);ceres::Problem problem;for (int i = 0; i < N; i++){ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction<AutoCostFunctor, 1, 1, 1, 1>(new AutoCostFunctor(x_data[i], y_data[i]));//problem.AddResidualBlock(cost_function, loss_function, &ae, &be, &ce);problem.AddResidualBlock(cost_function, nullptr, &ae, &be, &ce);}ceres::Solver::Options options;options.linear_solver_type = ceres::DENSE_NORMAL_CHOLESKY;options.minimizer_progress_to_stdout = true;ceres::Solver::Summary summary;ceres::Solve(options, &problem, &summary);cout << "estimate ae: " << ae << ",\tbe: " << be << ",\tce: " << ce << endl;summary.BriefReport();return 0;
}

可以将new ceres::AutoDiffCostFunction<AutoCostFunctor, 1, 1, 1, 1>(new AutoCostFunctor(x_data[i], y_data[i]));部分写成函数封装在struct AutoCostFunctor中,俗称工厂方法或工厂函数

static ceres::CostFunction* Create(double x, double y)
{return new ceres::AutoDiffCostFunction<AutoCostFunctor, 1, 1, 1, 1>(new AutoCostFunctor(x, y));
}

1.4 解析求导

需要重载一个CostFunction::Evaluate()函数,函数里面需要构建残差和雅克比矩阵。
'ceres::SizedCostFunction<残差块维数, 参数块[0]维数, 参数块[1]维数,..., 参数块[n]维数> 其需与ceres::Problem::AddResidualBlock()形参中的参数块对应。理论上多个参数块可以写成一个大的一维参数块,但是ceres solver会根据参数块对雅克比矩阵进行分块,如果写成一个大的一维参数块,将导致雅克比矩阵也是一整块,不能进行一些分块计算影响性能,所以最好还是根据参数的具体含义对参数进行分块。

#include <iostream>
#include <opencv2/core/core.hpp>
#include <ceres/ceres.h>using namespace std;class ManualCostFunctor : public ceres::SizedCostFunction<1, 3>
{
public:ManualCostFunctor(double x, double y): m_x(x), m_y(y) {}virtual ~ManualCostFunctor() {};virtual bool Evaluate(double const* const* parameters, double *residuals, double **jacobians) const{const double a = parameters[0][0], b = parameters[0][1], c = parameters[0][2];const double f = exp(a*m_x*m_x +b*m_x +c);residuals[0] = m_y - f;if ((jacobians == nullptr)|| (jacobians[0] == nullptr)){return true;}else{jacobians[0][0] = -f * m_x * m_x;jacobians[0][1] = -f * m_x;jacobians[0][2] = -f;return true;}}private:const double m_x;const double m_y;
};int main(int argc, char **argv) {double ar = 1.0, br = 2.0, cr = 1.0;         // 真实参数值double ae = 2.0, be = -1.0, ce = 5.0;        // 估计参数值int N = 100;                                 // 数据点double w_sigma = 1.0;                        // 噪声Sigma值double inv_sigma = 1.0 / w_sigma;cv::RNG rng;                                 // OpenCV随机数产生器vector<double> x_data, y_data;      // 数据for (int i = 0; i < N; i++) {double x = i / 100.0;x_data.push_back(x);y_data.push_back(exp(ar * x * x + br * x + cr) + rng.gaussian(w_sigma));}double abc[3] = {ae, be, ce};cout << "true a: " << ar << ",\tb: " << br << ",\tc: " << cr << endl;cout << "initial a: " << ae << ",\tb: " << be << ",\tc: " << ce << endl;ceres::LossFunction* loss_function = new ceres::HuberLoss(1.0);ceres::Problem problem;for (int i = 0; i < N; i++){ceres::CostFunction* cost_function = new ManualCostFunctor(x_data[i], y_data[i]);//problem.AddResidualBlock(cost_function, loss_function, &ae, &be, &ce);problem.AddResidualBlock(cost_function, nullptr, abc);}ceres::Solver::Options options;options.linear_solver_type = ceres::DENSE_NORMAL_CHOLESKY;options.minimizer_progress_to_stdout = true;ceres::Solver::Summary summary;ceres::Solve(options, &problem, &summary);cout << "estimate a: " << abc[0] << ",\tb: " << abc[1] << ",\tc: " << abc[2] << endl;summary.BriefReport();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/977630.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flash动画制作总结

对于下周要进行验收的Flash动画大作业,我一开始并没有接触过这个,在进行询问AI和这个项目制作的步骤 从一开始的,AI建议使用HTML,因为现在大部分主流的浏览器已经不适配Flash插件了,导致一开始没有思路 后来在进行…

什么是Go语言

Go 语言(也称为 Golang)是由 Google 公司开发的静态类型、编译型编程语言,于 2009 年正式发布。它由 Robert Griesemer、Rob Pike 和 Ken Thompson(C 语言创始人之一)主导设计,旨在解决大型软件工程中的效率、复…

人工智能之数据分析 Matplotlib:第一章 简介和安装

人工智能之数据分析 Matplotlib:第一章 简介和安装人工智能之数据分析 Matplotlib 第一章 简介和安装@目录人工智能之数据分析 Matplotlib前言一、Matplotlib 简介二、安装 Matplotlib1. 使用 pip 安装(推荐)2. 使…

在C#中操作Word文档时,如何处理表格中的数据?

在 C# 中操作 Word 表格数据时,需结合使用的库(如Microsoft.Office.Interop.Word或DocX)进行数据填充、读取、修改、验证等操作。以下分场景详细说明处理方法: 一、数据填充(写入表格) 1. 使用 Interop.Word 填充…

第四十九篇

今天是11月26号,上了离散,两节马原

如何使用DocX库在C#中创建和格式化Word表格?

使用 DocX 库在 C# 中创建和格式化 Word 表格无需安装 Microsoft Office,且操作轻量、跨平台。以下是详细的实现步骤、示例代码及关键格式化技巧: 一、准备工作:安装 DocX 库 通过 NuGet 包管理器安装 DocX(由 Xce…

feature map是什么

特征图(Feature Map) 是卷积神经网络(CNN)对输入图像进行特征提取后得到的中间表示,可以理解为一组压缩但语义丰富的“特征图像”。在目标检测中,它是连接主干网络与检测头的核心桥梁。 一、直观形象:特征图长什…

10-数据格式转换

WKT数据格式处理 概述 WKT(Well-Known Text)是一种用于表示几何对象的标准文本格式。作为一种通用的几何数据表示方法,WKT可以作为不同GIS数据格式之间转换的桥梁。本章介绍WKT格式的特点、使用方法以及基于WKT进行…

elasticsearch创建用户、角色

我们要创建一个叫xjw的用户,这个用户要有较高的es权限和kibana权限,同时实现多用户下的数据隔离先创建角色 indices下的names属性里配置的是我们能操作的索引、模版等等数据的前缀“xjw-”,如果不带这个前缀会报错4…

09-国土TXT格式

国土TXT格式 概述 国土TXT格式是中国自然资源部门使用的地块坐标文本格式,主要用于土地调查、不动产登记等业务场景。理解并正确处理该格式是从事国土相关GIS开发的必备技能。 文件格式 基本结构 国土TXT文件由多个部…

P30_利用GUP训练(二)

P30_利用GUP训练(二)1.代码实战: (1)调用.to()方法即可。 .to(device) device = torch.device("cpu") torch.device("cuda")点击查看代码 #12.定义训练的设备 # device = torch.device("…

重磅!图灵奖得主 Bengio 领衔 30 + 顶流学者联合发文!首次给 AGI 下量化定义

论文标题:A Definition of AGI 作者团队:人工智能安全中心、加州大学伯克利分校、Morph实验室、密歇根大学等 发布时间:2025年10月21日 👉一键直达论文 👉Lab4AI大模型实验室论文阅读 ✅Lab4AI平台提供AI导读和…

GitHub Actions安全漏洞:GITHUB_TOKEN部分泄露风险分析

本文详细分析了CVE-2025-31479安全漏洞,该漏洞会导致GitHub Actions中的GITHUB_TOKEN在异常输出中部分泄露,可能被攻击者利用进行供应链攻击,影响仓库安全性。CVE-2025-31479:canonical/get-workflow-version-acti…

使用 C# 自动创建和格式化 Word 表格

要在 C# 中自动创建和格式化 Word 表格,可借助Microsoft.Office.Interop.Word库或DocX(更轻量,无需安装 Office)。以下分别介绍两种方案的实现方法: 方案一:使用 Microsoft.Office.Interop.Word(需安装 Office)…

Mac SPSS 26 dmg 安装步骤详解 简单易懂一步步教你装(附安装包)

Mac SPSS 26 dmg 安装步骤详解 简单易懂一步步教你装(附安装包)​ SPSS 26​ 是一款专门用来分析数据的软件,很多人做统计、调查、市场研究时会用到。它能帮你算平均数、百分比、做各种图表,还能跑复杂的分析模型,…

NeurIPS 2025Mamba引爆3D重建!MVSMamba:效率与精度双双超越Transformer

论文标题:MVSMamba: Multi-View Stereo with State Space Model 作者团队:北京科技大学 发布时间:2025年11月4日 👉一键直达论文 👉Lab4AI大模型实验室论文阅读 ✅Lab4AI平台提供AI导读和AI翻译等工具,辅助论文…

StackOverflow已经死亡了吗

StackOverflow已经死亡了吗 最近,一张有趣的图片在X平台上引发了程序员社区的热烈讨论。用户@_devJNS将StackOverflow比作《忍者神龟》中的斯普林特大师,而ChatGPT、Claude、DeepSeek和Gemini则化身为新一代忍者。配…

零代码,分钟级定制:我用LLaMA-Factory轻松造了个“票务专家”AI

传统的基于关键词匹配的聊天机器人难以理解用户复杂的、多意图的自然语言查询(例如,“我想下周从北京飞往上海,看看上午的机票,最好是不用太早起床的航班,并且帮我选一个靠过道的位置”),导致用户体验不佳,转而…

2025AI培训权威排名:AI时代新商学引领行业变革

行业痛点分析 在当前的AI培训领域,技术挑战层出不穷。随着人工智能技术的快速发展,企业对具备AI技能的人才需求日益增加,然而,传统的培训模式往往难以跟上这一趋势。数据显示,超过60%的企业反映,现有的培训内容与…