ceres学习笔记(一)

news2024/11/22 16:34:39

本来还想着先对照着官方doc来学习的。突然在csdn里面搜了下,发现了几篇高质量的文章,就先对应这几篇文章学习,来快速入门。

一、ceres求解问题一般步骤

使用ceres-solver求解优化问题一般分为下面三步:

1.第一步:构建代价函数costFunction,就是寻优的目标表达式。具体做法是定义一个costFunction的结构体,然后在结构体内重载operator()运算符,有博主指出这是c++中一种仿函数的技巧。

2.第二步:通过代价函数构建待求解的优化问题。

3.第三步:配置求解器,就是设置方程怎么求解、求解过程是否输出等,最后调用solve方法。

二、实际代码求解过程

以官网的的入门例子来说明实际代码如何编写。

求解\frac{1}{2}*\left ( 10-x \right )^2的最小值。

按上面的求解步骤来:

第一步构造代价函数

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

这里是写了一个struct来构建代价函数CostFunctor,它里面重载了operator()运算符。其中x表示自变量,residual表示因变量。注意它们都是数组的,使用的全是数组名为参数。还有一点是这里使用了三个const,最后一个const是表明不会改变数据成员函数。前面两个const是指声明该指针变量里面的内容不可改变,同时该内容指向的内容亦不可改变。

参考资料:

const int、const int *、int *cosnt、const int * const、const int &的区别_Ruo_Xiao的博客-CSDN博客_const int[3]

C++之const类成员变量,const成员函数 - CTHON - 博客园

第二步通过代价函数构建待求解的优化问题

Problem problem;
  CostFunction* cost_function =
      new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor); 
  problem.AddResidualBlock(cost_function, NULL, &x); 

首先生成问题对象,然后生成代价函数cost_function,它使用第一步定义的代价函数结构体CostFunctor来作为参数生成,使用自动求导的方式。第一个1表示输出的纬度,就是residual的纬度。第二个1表示输入的纬度就是x。后面的就是向问题中添加误差项,第二个参数NULL表示不用添加核函数,在aloam中就设置的核函数,这里没有设置,第三个参数就是待寻找最优解的x。

第三步配置problem并求解它

  Solver::Options options;
  options.linear_solver_type = ceres::DENSE_QR;
  options.minimizer_progress_to_stdout = true;
  Solver::Summary summary;
  Solve(options, &problem, &summary);

创建一个options它用来设置求解的一些选项。使用ceres::DENSE_QR线性求解器,设置可以输出到cout,然后就是设置了summary的优化信息,主要是一些文字信息,记录了优化的过程,和结果什么的。然后使用Solve函数开始求解,第一个参数是设置的选项,第二个是问题,第三个是信息。

完整代码如下:

#include<iostream>
#include<ceres/ceres.h>

using namespace std;
using namespace ceres;

//第一部分:构建代价函数,重载()符号,仿函数的小技巧
struct CostFunctor {
   template <typename T>
   bool operator()(const T* const x, T* residual) const {
     residual[0] = 0.5*(T(10.0) - x[0])*(T(10.0) - x[0]);
     return true;
   }
};

//主函数
int main(int argc, char** argv) {
  google::InitGoogleLogging(argv[0]);

  // 寻优参数x的初始值,为5
  double initial_x = 5.0;
  double x = initial_x;

  // 第二部分:构建寻优问题
Problem problem;
  CostFunction* cost_function=
      new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor); //使用自动求导,将之前的代价函数结构体传入,第一个1是输出维度,即残差的维度,第二个1是输入维度,即待寻优参数x的维度。
  problem.AddResidualBlock(cost_function, NULL, &x); //向问题中添加误差项,本问题比较简单,添加一个就行。

  //第三部分: 配置并运行求解器
  Solver::Options options;
  options.linear_solver_type = ceres::DENSE_QR; //配置增量方程的解法
  options.minimizer_progress_to_stdout = true;//输出到cout
  Solver::Summary summary;//优化信息
  Solve(options, &problem, &summary);//求解!!!

  // std::cout << summary.BriefReport() << "\n";//输出优化的简要信息
//最终结果
  std::cout << "x : " << initial_x
            << " -> " << x << "\n";
  return 0;
}

还有CMakeLists.txt内容:

cmake_minimum_required(VERSION 2.8)
project(ceres)

find_package(Ceres REQUIRED)
include_directories(${CERES_INCLUDE_DIRS})

add_executable(xxx xxx.cpp)
target_link_libraries(xxx ${CERES_LIBRARIES})

比较简单不做解释。

参考资料:

一文助你Ceres 入门——Ceres Solver新手向全攻略_福尔摩睿的博客-CSDN博客_ceres solver

三、非线性拟合求解

使用ceres进行非线性拟合求解,它指定一系列的点对来拟合一个曲线的系数。

首先有一系列的点,这些点是是通过对曲线y=e^{0.3x+0.1}插值的点然后加上高斯噪声获得的。通过这些点来拟合曲线,y=a*e^{bx+c}。这个形似是这个,就是通过这些点来优化算出算出a,b,c的值。

第一步构造代价函数:

struct ExponentialResidual {
    ExponentialResidual(double x, double y)
            : x_(x), y_(y) {}

    template <typename T> bool operator()(const T* const a, const T* const b, const T* const c, T* residual) const {
        residual[0] = y_ - a[0] * exp(b[0] * x_ + c[0]);
        return true;
    }

private:
    const double x_;
    const double y_;
};

第二步通过代价函数设置优化问题:

    Problem problem;
    for (int i = 0; i < kNumObservations; ++i) {
        CostFunction* cost_function = new AutoDiffCostFunction<ExponentialResidual, 1, 1, 1, 1>(new ExponentialResidual(data[2 * i], data[2 * i + 1]));
        problem.AddResidualBlock(
                cost_function,
                NULL,
                &a, &b, &c);
    }

这里和前面的不一样的是这里有很多的数据,需要不断的把数据添加进problem。

第三步配置problem并求解:

    Solver::Options options;
    options.max_num_iterations = 25;
    options.linear_solver_type = ceres::DENSE_QR;
    options.minimizer_progress_to_stdout = true;

    Solver::Summary summary;
    Solve(options, &problem, &summary);

一些对于options的设置。

完整的程序:

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

using ceres::AutoDiffCostFunction;
using ceres::CostFunction;
using ceres::Problem;
using ceres::Solver;
using ceres::Solve;


const int kNumObservations = 67;
//观测值
const double data[] = {
        0.000000e+00, 1.133898e+00,
        7.500000e-02, 1.334902e+00,
        1.500000e-01, 1.213546e+00,
        2.250000e-01, 1.252016e+00,
        3.000000e-01, 1.392265e+00,
        3.750000e-01, 1.314458e+00,
        4.500000e-01, 1.472541e+00,
        5.250000e-01, 1.536218e+00,
        6.000000e-01, 1.355679e+00,
        6.750000e-01, 1.463566e+00,
        7.500000e-01, 1.490201e+00,
        8.250000e-01, 1.658699e+00,
        9.000000e-01, 1.067574e+00,
        9.750000e-01, 1.464629e+00,
        1.050000e+00, 1.402653e+00,
        1.125000e+00, 1.713141e+00,
        1.200000e+00, 1.527021e+00,
        1.275000e+00, 1.702632e+00,
        1.350000e+00, 1.423899e+00,
        1.425000e+00, 1.543078e+00,
        1.500000e+00, 1.664015e+00,
        1.575000e+00, 1.732484e+00,
        1.650000e+00, 1.543296e+00,
        1.725000e+00, 1.959523e+00,
        1.800000e+00, 1.685132e+00,
        1.875000e+00, 1.951791e+00,
        1.950000e+00, 2.095346e+00,
        2.025000e+00, 2.361460e+00,
        2.100000e+00, 2.169119e+00,
        2.175000e+00, 2.061745e+00,
        2.250000e+00, 2.178641e+00,
        2.325000e+00, 2.104346e+00,
        2.400000e+00, 2.584470e+00,
        2.475000e+00, 1.914158e+00,
        2.550000e+00, 2.368375e+00,
        2.625000e+00, 2.686125e+00,
        2.700000e+00, 2.712395e+00,
        2.775000e+00, 2.499511e+00,
        2.850000e+00, 2.558897e+00,
        2.925000e+00, 2.309154e+00,
        3.000000e+00, 2.869503e+00,
        3.075000e+00, 3.116645e+00,
        3.150000e+00, 3.094907e+00,
        3.225000e+00, 2.471759e+00,
        3.300000e+00, 3.017131e+00,
        3.375000e+00, 3.232381e+00,
        3.450000e+00, 2.944596e+00,
        3.525000e+00, 3.385343e+00,
        3.600000e+00, 3.199826e+00,
        3.675000e+00, 3.423039e+00,
        3.750000e+00, 3.621552e+00,
        3.825000e+00, 3.559255e+00,
        3.900000e+00, 3.530713e+00,
        3.975000e+00, 3.561766e+00,
        4.050000e+00, 3.544574e+00,
        4.125000e+00, 3.867945e+00,
        4.200000e+00, 4.049776e+00,
        4.275000e+00, 3.885601e+00,
        4.350000e+00, 4.110505e+00,
        4.425000e+00, 4.345320e+00,
        4.500000e+00, 4.161241e+00,
        4.575000e+00, 4.363407e+00,
        4.650000e+00, 4.161576e+00,
        4.725000e+00, 4.619728e+00,
        4.800000e+00, 4.737410e+00,
        4.875000e+00, 4.727863e+00,
        4.950000e+00, 4.669206e+00,
};
//1.代价函数结构体
struct ExponentialResidual {
    ExponentialResidual(double x, double y)
            : x_(x), y_(y) {}

    template <typename T> bool operator()(const T* const a, const T* const b, const T* const c, T* residual) const {
        residual[0] = y_ - a[0] * exp(b[0] * x_ + c[0]);
        return true;
    }

private:
    const double x_;
    const double y_;
};

int main(int argc, char** argv) {

    double a = 0.0;
    double b = 0.0;
    double c = 0.0;
  
  //构建寻优问题
    Problem problem;
    for (int i = 0; i < kNumObservations; ++i) {
        CostFunction* cost_function = new AutoDiffCostFunction<ExponentialResidual, 1, 1, 1, 1>(new ExponentialResidual(data[2 * i], data[2 * i + 1]));
        problem.AddResidualBlock(
                cost_function,
                NULL,
                &a, &b, &c);
    }
   //配置并运行求解器
    Solver::Options options;
    options.max_num_iterations = 25;
    options.linear_solver_type = ceres::DENSE_QR;
    options.minimizer_progress_to_stdout = true;

    Solver::Summary summary;
    Solve(options, &problem, &summary);
    std::cout << summary.BriefReport() << "\n";
    std::cout << "Real a: " << 1.0 << "      b: " << 0.3 << "      c: " << 0.1<< "\n";
    std::cout << "Initial a: "<< 0.0 <<"Initial b: " << 0.0 << " c: " << 0.0 << "\n";
    std::cout << "Final   a: " << a << "Final   b: " << b << " c: " << c << "\n";
    return 0;
}

最后输出不太对,有点问题。但主要是理解,不用太在意。

 参考资料:

Ceres入门——Ceres的基本使用方法_Andy是个男子名的博客-CSDN博客_ceres使用

四、总结

感觉ceres理解起来比g2o容易多了,不知道是不是因为google文档写的比较好,大家就用的很多,相关的参考资料也很多,所以很好上手。之后内容准备先把doc刷一遍,加强印象。

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

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

相关文章

aws cdk 配置 lambda 函数的金丝雀发布

之前的文章介绍了使用sam框架完成lambda函数的金丝雀发布&#xff0c;这里使用cdk创建lambda函数项目实现此功能 Building CI/CD pipelines for lambda canary deployments using AWS CDK 项目的结构如下图所示 lambda堆栈示例 应用程序和环境配置 #!/usr/bin/env python3 im…

数据结构与算法0—大纲

数据结构&#xff1a;数据结构是计算机存储、组织数据的方式。是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组. 算法&#xff1a;是指解题方案的准确而完整的描述&#xff0c;是一系列解决问题的清晰指令&#xff0c;算法代表着用系统的方法…

Vue动态路由、动态路由如何进行参数的传递、$router和$route的区别、命名路由、命名视图

一、什么是动态路由 ​ 将URL地址中可变的内容设置成参数&#xff0c;根据不同的参数渲染不同的组件。&#xff08;组件可以复用&#xff09; 二、动态路由如何进行参数的传递 ​ 1. 如何设置URL地址中的参数&#xff1a;“/url/:参数名” ​ 2. 在组…

Linux环境下配置Nginx

文章目录安装环境配置安装Nginx需要安装第三方的开发包安装Nginx启动并访问Nginx安装完毕Linux——centos7版本 安装环境配置 Nginx是C语言编写的&#xff0c;所以需要配置C语言编译环境&#xff08;要联网&#xff09; 安装gcc环境 [roota ~]# yum install gcc-c 已加载插件…

【Unity3D编辑器扩展】Unity3D中解决Text的清晰度问题

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 在程序开发中&#xff0c;常常会遇到Unity中Text文字不清晰的问…

中兴服务器迈络思NS312网卡down掉网口物理指示灯不熄灭解决方法--NS312型号ConnectX-4 Lx

中兴服务器迈络思NS312网卡down掉网口物理指示灯不熄灭解决方法–NS312型号ConnectX-4 Lx 中兴服务器做网卡倒卡测试时&#xff0c;出现如下问题&#xff1a; 中兴服务器迈络思网卡&#xff0c;卡片NS312&#xff0c;型号ConnectX-4 Lx&#xff0c;万兆网卡在执行ifdown或ip l…

SmartZone ICR/OCR Professional 7.2 NET Crack

SmartZone ICR/OCR 专业版 v7.2 采集 by Ω578867473 检测并报告字体属性、字体大小和大写字母高度。特征 检测并报告字体属性、字体大小、大写字母高度和基线详细信息&#xff0c;作为 Microsoft Windows 上单个字符识别结果的一部分。检测和报告图像方向并识别图像&#xff0…

Spring的生命周期

Spring作为当前Java最流行、最强大的轻量级框架&#xff0c;受到了程序员的热烈欢迎。准确的了解Spring Bean的生命周期是非常必要的。我们通常使用ApplicationContext作为Spring容器。这里&#xff0c;我们讲的也是 ApplicationContext中Bean的生命周期。而实际上BeanFactory也…

【云原生进阶之容器】第四章Operator原理4.1节--定制资源(Custom Resource)

1 定制资源概述 定制资源(Custom Resource) 是对 Kubernetes API 的扩展。 本页讨论何时向 Kubernetes 集群添加定制资源,何时使用独立的服务。 1.1 定制资源 资源(Resource) 是 Kubernetes API 中的一个端点, 其中存储的是某个类别的 API 对象的一个集合。 例如内置的 …

Revit中墙连接方式和墙连接显示及修改问题

关于Revit墙连接方式、显示及其调整修改的理解&#xff0c;首先我们要来理清楚几个问题 1、什么是Revit墙连接&#xff1f; 在Revit里墙与墙之间的连接就是墙连接&#xff0c;它可以是同类型墙之间的连接&#xff0c;也可以是不同类型墙之间的连接&#xff0c;可以是2堵墙、3…

【链表】leetcode面试题 02.07. 链表相交(C/C++/Java/Js)

leetcode面试题 02.07. 链表相交1 题目2 思路2.1 思路一2.2 思路二(强推--5行代码&#xff09;3 代码3.1 C版本3.1.1思路一&#xff1a;3.1.2 思路二3.2 C版本3.2.1 思路一3.2.2 思路二3.3 Java版本3.3.1 思路一3.3.2 思路二3.4 JavaScript版本4 总结1 题目 给你两个单链表的头…

【进阶C语言】动态内存管理+柔性数组

文章目录1.动态内存的开辟内存的布局内存池内存碎片内存泄漏2.动态内存函数malloc功能函数calloc功能函数realloc功能函数开辟时遇到的两种情况free功能函数3.  建议4.柔性数组特性&#xff1a;定义使用优点1.动态内存的开辟 内存的布局 我们常用的内存开辟函数都是在堆区开辟…

几个特殊的运算符重载(前置\后置++、前置\后置--、<<、>>)

几个特殊的运算符重载 概念 运算符重载是一个非常重要的概念&#xff0c;在运算符重载中我们可以重新定义 运算符 的具体含义&#xff0c;一个运算符重载函数的定义是 T operator 运算符 (参数) &#xff0c;对于运算符重载有以下重点。 以下讨论的都是双操作数的运算符 双操…

uniapp: 本应用使用HBuilderX x.x.xx 或对应的cli版本编译,而手机端SDK版本是 x.x.xx。不匹配的版本可能造成应用异常。

目录场景与问题描述&#xff1a;原因分析&#xff1a;解决方案&#xff1a;方案一&#xff1a;更新HbuilderX版本方案二&#xff1a;设置固定的版本方案三&#xff1a;忽略版本&#xff08;不推荐&#xff09;场景与问题描述&#xff1a; 项目场景&#xff1a;示例:通过使用Hb…

【docker13】Dockfile

1.Dockerfile是什么 Dockerfile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本 自己的理解是&#xff1a;将多次繁琐的commit命令构成一个Dockerfile文本然后一次性执行完成&#xff0c;可以简化复杂程度 Dockerfile官网 构建三部…

Docker部署 registry

系列文章目录 Docker部署 registry Docker搭建 svn Docker部署 Harbor Docker 部署SQL Server 2017 Docker 安装 MS SqlServer Docker部署 Oracle12c Docker部署Jenkins Docker部署 registry系列文章目录前言一、registry搭建二、使用步骤1. pull registry2. run image3. 验证…

通过gcloud创建Google Kubernetes Engine(GKE)并通过kubectl访问

1 简介 GKE(Google Kubernetes Engine)是一个K8s平台&#xff0c; 我们可以使用gcloud来创建GKE集群。在开始之前&#xff0c;可以查看&#xff1a;《初始化一个GCP项目并用gcloud访问操作》。 2 创建GKE集群 2.1 打开API 在创建集群之前&#xff0c;需要打开Google API&am…

图扑数字孪生水利工程,助力水资源合理利用

前言从大禹治水到三峡大坝的建造&#xff0c;人类为控制和调配自然界的地表水和地下水&#xff0c;修建了许多的水利工程。对水资源进行了广泛的开发利用&#xff0c;诸如农业灌溉、工业和生活用水、水力发电、航运、港口运输、淡水养殖、旅游等。将图扑软件与 GIS、粒子仿真、…

力扣算法(Java实现)—字符串入门(9题)

文章目录1.反转字符串2.整数反转3.字符串中的第一个唯一字符4.有效的字母异位词5.验证回文串6.字符串转换整数 (atoi)7.实现strStr()8.外观数列9.最长公共前缀&#x1f48e;&#x1f48e;&#x1f48e;&#x1f48e;&#x1f48e; 更多资源链接&#xff0c;欢迎访问作者gitee仓…

STM32那些事

STM32芯片型号命名方式STM32开发板的GPIO编程GPIO的函数调用顺序&#xff1a;&#xff08;1&#xff09;使能GPIO时钟&#xff1a;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);第一个参数是GPIO对象&#xff0c;第二个参数是枚举使能&#xff08;2&#xff09;初始化…