当前位置: 首页 > news >正文

Ceres Solver优化库学习笔记

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.zskr.cn/news/61909.html

相关文章:

  • Flash动画制作总结
  • 第四十九篇
  • 10-数据格式转换
  • 09-国土TXT格式
  • Mac SPSS 26 dmg 安装步骤详解 简单易懂一步步教你装(附安装包)
  • 小程序商城客服系统传递咨询产品信息卡片,传递订单信息卡片
  • 48(11.28)
  • 46(11.26)
  • Python模块与包完全教程:从导入到封装发布(附实战)
  • 【Webpack连载一】入门简介。了解为什么需要Webpack,解决哪些开发中通病 - 实践
  • 借助gdb推进修改oracle scn
  • 2025年11月红外防潮系统,碳红外防潮取暖系统,别墅红外防潮系统厂家推荐:实力防潮品牌解析,采购无忧之选!
  • Ai元人文:谦卑的舞台搭建者——岐金兰与她的未完成之歌
  • 2025年下半年UVLED面光源、UVLED线光源、UV固化箱、UV解胶机、UV固化炉厂家Top 5推荐指南:选购必看榜单
  • 数据破界,价值共生:东软锚定AI时代民生新答卷
  • 2025年下半年UVLED面光源、UVLED线光源、UV固化箱、UV解胶机、UV固化炉厂家综合评测与选购指南
  • 2025年江苏徐州板式家具、模压托盘、桥洞力学板、三聚氰胺饰面板品牌公司综合推荐指南:五大优质厂商深度解析
  • Check Point R82 Gaia - 面向安全应用的下一代操作系统
  • 2025年下半年候车亭、公交站台、电子站牌、公交站牌、公交候车厅选购指南:十大优质供应商推荐
  • 2025年下半年轴连轴承、水泵轴承、转向轴承、圆锥滚子轴承、汽车水泵轴承厂家综合推荐指南:十大优质供应商盘点
  • EMNLP 2022自然语言处理技术全景概览
  • “租易 - 快捷租房管理小程序” Alpha 阶段团队贡献分与 Postmortem 会议总结文档
  • 2025年下半年热风炉、火焰检测器、低氮燃烧器、废气废液焚烧、沼气直燃设备厂家推荐榜单前十强:专业指南与选择攻略
  • CSS:icon图标悬停时有底部背景色
  • 2025年塑料托盘、塑胶卡板、吹塑托盘、塑料栈板、防渗漏托盘厂家精选推荐Top 5指南
  • 2025.11.26
  • 大数据技术简史:十年演化,万象归流 - 智慧园区
  • 2025年11月GPU平台TOP5推荐:大模型微调全场景适配与稳定性指南
  • Nexpose 8.29.0 for Linux Windows - 漏洞扫描
  • 自签生成SSL证书IIS和nginx全流程