视觉SLAM实践入门——(15)使用g2o求解PnP

《一起学习rgbdSLAM》中g2o部分报错的问题 《一起学习rgbdSLAM》中g2o部分报错的问题 在高博的《一起学习rgbdSLAM》第六讲中使用到了g2o工具。但是由于版本问题,按照高博的写法来编写代码在编译过程中会报错。所以需要对该部分代码稍作修改。 我使用的系统是ubuntu16.04。 教程中的源代码 // 选择优化方法 typedef g2o::BlockSolver_6_3 SlamBlockSolver; ... 阅读详情

使用g2o求解PnP的步骤:

1、定义顶点和边的数据类型

2、提取ORB特征、匹配点对

3、构造顶点、边

4、定义、设置求解器

5、调用求解器的 initializeOptimization 函数求解问题

 

 

如果无法链接g2o动态库,可以参考这篇文章的注意事项

https://blog.youkuaiyun.com/floatinglong/article/details/116159431

 

#include <iostream>
using namespace std;

#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/calib3d/calib3d.hpp>
using namespace cv;

#include <Eigen/Core>
using namespace Eigen;

#include <chrono>

#include <sophus/se3.hpp>

#include <g2o/core/base_vertex.h>
#include <g2o/core/base_unary_edge.h>
#include <g2o/core/sparse_optimizer.h>
#include <g2o/core/block_solver.h>
#include <g2o/core/solver.h>
#include <g2o/core/optimization_algorithm_gauss_newton.h>
#include <g2o/solvers/dense/linear_solver_dense.h>

//提取ORB特征并匹配
void findAndMatchOrbFeature(const Mat &img1, const Mat &img2, 
	vector<KeyPoint> &kp1, vector<KeyPoint> &kp2, 
	vector<DMatch> &matches)
{	
	Ptr<FeatureDetector> detector = ORB::create();
	Ptr<DescriptorExtractor> desc = ORB::create();
	Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
	
	//检测角点
	detector->detect(img1, kp1);
	detector->detect(img2, kp2);
	
	//计算描述子
	Mat desc1, desc2;	
	desc->compute(img1, kp1, desc1);
	desc->compute(img2, kp2, desc2);
	
	//匹配点对
	vector<DMatch> allMatches;
	matcher->match(desc1, desc2, allMatches);
	
	//检测误匹配
	double minDist = 10000;
	for(auto m : allMatches)
		if(m.distance < minDist)	minDist = m.distance;
	
	double useDist = max(2*minDist, 30.0);
	for(auto m : allMatches)	
		if(m.distance <= useDist)
			matches.push_back(m);	
}

// 顶点,模板参数:优化变量维度和数据类型
class Vertex : public g2o::BaseVertex<6, Sophus::SE3d> {
public:
	EIGEN_MAKE_ALIGNED_OPERATOR_NEW

	// 重置
	virtual void setToOriginImpl() override {	//override保留字表示当前函数重写了基类的虚函数
		_estimate = Sophus::SE3d();
	}
 
  // 更新
	virtual void oplusImpl(const double *update) override {
		Eigen::Matrix<double, 6, 1> updateVector;
		updateVector << update[0], update[1], update[2], update[3], update[4], update[5];	
		_estimate = Sophus::SE3d::exp(updateVector) * _estimate;
	}
 
  // 存盘和读盘:留空
	virtual bool read(istream &in) {}
	virtual bool write(ostream &out) const {}
};
 
// 误差模型 模板参数:观测值维度,类型,连接顶点类型
class Edge : public g2o::BaseUnaryEdge<2, Eigen::Vector2d, Vertex> {
public:
	EIGEN_MAKE_ALIGNED_OPERATOR_NEW
 
	Edge(const Eigen::Vector3d &p3d, const Eigen::Matrix3d &k) :  _p3d(p3d), _k(k) {}
 
	// 计算误差
	virtual void computeError() override {	    
	const Vertex *v = static_cast<const Vertex *> (_vertices[0]);	//static_cast将_vertices[0]的类型强制转换为const Edge *
    const Sophus::SE3d T = v->estimate();    //取出顶点优化变量
	
	Eigen::Vector3d tp3d = T * _p3d;	//旋转后的空间坐标
	
	tp3d = _k * tp3d / tp3d[2];	//映射到像素平面,前两维是像素坐标,因为这里用到了k的运算,因此k要定义为Eigen库里的类型	
	Eigen::Vector2d tp2d = tp3d.head<2>();
    
	_error = _measurement - tp2d;    //计算误差
  }
 
  // 计算雅可比矩阵
virtual void linearizeOplus() override {
    const Vertex *v = static_cast<const Vertex *> (_vertices[0]);
    const Sophus::SE3d T = v->estimate();	//取出上次估计的T
	
	Eigen::Vector3d tp3d = T * _p3d;	//3d点经过旋转
	double invZ = 1 / tp3d[2], invZ2 = invZ * invZ;
	double fx = _k(0, 0);
	double fy = _k(1, 1);
	double cx = _k(0, 2);
	double cy = _k(1, 2);
	
	_jacobianOplusXi << -fx * invZ,
		0,
		fx * tp3d[0] * invZ2,
		fx * tp3d[0] * tp3d[1] * invZ2,
		-fx - fx * tp3d[0] * tp3d[0] * invZ2,
		fx * tp3d[1] * invZ,
		0,
		-fy * invZ,
		fy * tp3d[1] * invZ2,
		fy + fy * tp3d[1] * tp3d[1] * invZ2,
		-fy * tp3d[0] * tp3d[1] * invZ2,
		-fy * tp3d[0] * invZ;
  }
 
	virtual bool read(istream &in) {}
	virtual bool write(ostream &out) const {}
 
private:
	Eigen::Vector3d _p3d;
	Eigen::Matrix3d _k;
};



int main(int argc, char **argv)
{
	if(argc != 4)
	{
		cout << "error!" << endl;
		return 0;
	}

	Mat img1 = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	Mat img2 = imread(argv[2], CV_LOAD_IMAGE_COLOR);

	vector<KeyPoint> kp1, kp2;
	vector<DMatch> matches;
	findAndMatchOrbFeature(img1, img2, kp1, kp2, matches);
	
	//g2o
	typedef g2o::BlockSolver<g2o::BlockSolverTraits<6, 2>> BlockSolverType;  //优化变量维度为6,误差维度为2
	typedef g2o::LinearSolverDense<BlockSolverType::PoseMatrixType> LinearSolverType; // 线性求解器类型

	//梯度下降方法
	auto solver = new g2o::OptimizationAlgorithmGaussNewton(
	g2o::make_unique<BlockSolverType>(g2o::make_unique<LinearSolverType>()));
	g2o::SparseOptimizer optimizer;     // 图模型
	optimizer.setAlgorithm(solver);   // 设置求解器
	optimizer.setVerbose(true);       // 打开调试输出

	//构造顶点
	Vertex *v = new Vertex();
	v->setEstimate(Sophus::SE3d());
	v->setId(0);
	optimizer.addVertex(v);

	//取3d-2d点对
	Eigen::Matrix3d k;
	k << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1;
	vector<Eigen::Vector2d, Eigen::aligned_allocator<Eigen::Vector2d>> ps2d;
	vector<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d>> ps3d;	
	for(auto m : matches)
	{
		Point2d p2d = kp1[m.queryIdx].pt;
		int x = p2d.x, y = p2d.y;
		unsigned short pixel = img3.ptr<unsigned short>(y)[x];
		
		if(pixel == 0)	continue;
		
		double dPixel = pixel / 5000.0;
		Point2d pNor2d = pixelToNor(p2d, k);
		ps3d.push_back(Eigen::Vector3d(pNor2d.x*dPixel, pNor2d.y*dPixel, dPixel));
		
		p2d = kp2[m.trainIdx].pt;
		x = p2d.x;
		y = p2d.y;
		ps2d.push_back(Eigen::Vector2d(x, y));
	}
	
	//构造边
	for (int i = 0; i < (int)ps3d.size(); i++) {
		Edge *edge = new Edge(ps3d[i], k);
		edge->setId(i);
		edge->setVertex(0, v);            	// 设置连接的顶点
		edge->setMeasurement(ps2d[i]);      // 观测值
		edge->setInformation(Eigen::Matrix2d::Identity()); // 信息矩阵:协方差矩阵之逆,这里没有,填单位阵,维度与误差变量一致
		optimizer.addEdge(edge);
	}
	
	// 执行优化
	optimizer.initializeOptimization();
	optimizer.optimize(10);	//迭代次数

	// 输出优化值
	Sophus::SE3d T = v->estimate();
	cout << "estimated model: " << endl << T.matrix() << endl;

	
	return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

视觉Slam14讲第六章g2o安装报错,Ubuntu22.04 安装最新版本的g2o,会出现这个报错,这个最新版刚发布三天,就让我遇到了。安装完最新版本的Ceres库后再安装这个就会出现这个错误。 阅读详情

相关推荐

《视觉SLAM十四讲精品总结》7:VO—— 3D-2D:PnP+BA

引出: 2D-2D的对极几何需要8个以上点对,且存在初始化、纯旋转和尺度问题。 特征点3D位置可以由三角化或RGB-D相机深度图确定。 因此,双目或RGB-D相机直接使用PnP估计相机运动。单目视觉里程计,首先初始化,然后才能PnP。 PnP为( Perspective-n-Point)的简称,即给出n个3D空间点及其投影位置时,如何求解相机的位姿R t。 PnP优点:不需要对极约束,3...

try_again_later的博客 7654

G2O库安装踩坑(SLAM十四讲)

由于买的是高博的新书。。。。。。所以版本问题很d疼(又没学会克隆子模块) 因此库的版本与代码的兼容性一直很头疼 现在看到第六章,g2o下载下来却编译报错,怀疑就是版本问题。 因此直接在github上克隆最新的g2o 下面是总体安装流程: 1. G2O安装过程 git clone https://github.com/RainerKuemmerle/g2o # 克隆最新版本g2o cd g2o ...

一只小菜鸟的博客 1万+

视觉SLAM:使用 Sophus 的 SE3 类,自己设计 g2o 的节点与边,实现 PnP 和 ICP 的优化

/ 自定义顶点:SE3 位姿public:// 李代数更新:前6维为 [平移, 旋转](注意顺序)

GeekDongHuang的博客 945

使用g2o进行曲线拟合的问题

1.在CMakeLists.txt中,将set(CMAKE_CXX_STANDARD 11)或set(CMAKE_CXX_STANDARD 14)更改成set(CMAKE_CXX_STANDARD 17),原因是g2o版本比较新,需要C++编译版本高。问题二:/usr/local/include/g2o/stuff/tuple_tools.h:34:46: error: ‘std::index_sequence’ has not been declared。

weixin_52867897的博客 508

视觉SLAM⑦---视觉里程计Ⅰ(3D-2D:PnP,3D-3D:ICP)

3D-2D:PnP,3D-3D:ICP

qq_41694024的博客 2729

视觉SLAM笔记(37) 求解 PnP

使用 EPnP 求解位姿、李代数位姿、空间点位置、投影方程边、Bundle Adjustment(光束平差法)

氢键H-H 1万+

视觉SLAM-ch6-g2oCurveFitting编译报错

g2o版本过高,make_unique 在 g2o 命名空间中似乎不可用。换装g2o-20230223则问题解决。

weixin_47627859的博客 787

【从零开始学习SLAM】两帧之间的特征点视觉里程计并用g2o优化

文章目录总结visual_odometry.cpp代码详细注释run_vo.cpp代码详细注释算法解析源码解读g2o_types.hg2o_types.cpp参考资料 总结 Config类负责参数文件的读取。 Camera类存储相机的内参和外参,并完成相机坐标系、像素坐标系、和世界坐标系之间的坐标变换。 Frame类定义了 ID、时间戳、位姿、相机、图像、提供基本的数据存储和接口。 MapPoint表示路标点估计它的世界坐标,拿当前帧提取到的特征点与地图中的路标点匹配,来估计相机的运动 Map类存储关键帧

小秋 AI SLAM 入门实战教程 2133

【SLAM】G2O优化库超详细解析

通用图优化。为何叫通用呢?G2O的核里带有各种各样的求解器,而它的顶点、边的类型则多种多样。通过自定义顶点和边,事实上,只要一个优化问题能够表达成图,那么就可以用G2O去求解它。常见的,比如bundle adjustment、ICP、数据拟合,都可以用G2O来做。所谓图优化,是把优化问题表现成图的一种方式,这里的图是图论意义上的图。一个图由若干个顶点,以及连着这些顶点的边组成。在这里,我们用顶点表示优化变量,而用边表示残差项。

Yngz_Miao的博客 1万+

极验第四代滑块验证码破解(三):滑块轨迹构造

极验第四代滑块验证码破解(三):滑块轨迹构造声明 声明 原创文章,请勿转载! 本文内容仅限于安全研究,不公开具体源码。维护网络安全,人人有责。 本文关联文章超链接:

帯泪的鱼的博客 8623

新建菜单Office被替换为腾讯文档 - 解决方案

右键新建菜单中的Office被替换为腾讯文档。原因是安装了腾讯文档软件,此软件安装时会自动将新建菜单中的Office替换为腾讯文档,下文介绍解决方案。

NSJim的博客 2926

【G2O】【G2O实践】【G2O的使用学习记录】

slam十四讲第二版】【课本例题代码向】【第六讲~非线性优化】【安装对应版本ceres2.0.0和g2o教程】【手写高斯牛顿、ceres2.0.0、g2o拟合曲线及报错解决方案】的3.1g2o的编译和安装本文所有的环境是基于g2o2.0.0,与g2o1.1.4的不兼容//曲线模型的顶点,模板参数优化变量维度和数据类型classCurveFittingVertexpublic//第一种实现方法start/*第一种解决方式....................................

qq_45954434的博客 2673

STM32F407+LAN8720A+LwIP实战:FreeModbus TCP从机功能开发指南

本文详细介绍了基于STM32F407和LAN8720A硬件平台,结合LwIP协议栈实现FreeModbus TCP从机功能的开发指南。内容涵盖硬件连接、LwIP移植、FreeModbus TCP从机实现、任务调度及调试技巧,为工业控制领域的以太网通信开发提供实用参考。

weixin_29190651的博客 546

视觉SLAM前端(间接法)——位姿求解(g2o)

目录: 对极约束 PnP问题 ICP问题 Gauss-Newton法 g2o求解最小二乘问题 对极约束 1.求解基础矩阵/本质矩阵 基础矩阵F Mat fundamental_matrix; fundamental_matrix = findFundamentalMat(points1, points2, FM_8POINT); 注意findFundamentalMat()函数的参数: 参数1,2:points_1;cv类型的中的特征点1,2容器类,像素坐标。 参数3:为求解方法,有8点法等等,为一

qq_38023849的博客 1432

【Labview与SQL Server互联(labview可以引用数据库)】

Labview与SQL Server互联

Magize的博客 3585

超声信号处理——硬件电路中的混频应用

本文介绍了超声多普勒系统的信号处理原理。系统通过波形振荡器产生高频信号,经换能器转换为超声波发射,接收反射信号后与原始信号混频,利用三角函数的积化和差特性产生包含多普勒频移的低频差频信号和高频和频信号。通过低通滤波器滤除高频成分后,即可获得纯净的多普勒信号。这种处理方法将MHz级高频信号转换为可听低频信号,降低了系统复杂度与成本。数学推导表明,差频信号频率正好

fcxgfdjy的博客 1123

视觉SLAM十四讲学习笔记-第七讲-视觉里程计-PnP和实践

专栏汇总 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-优快云博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-优快云博客 视觉SLAM十四讲学习笔记-第二讲-开发环境搭建_goldqiu的博客-优快云博客 视觉SLAM十四讲学习笔记-第三讲-旋转矩阵和Eigen库_goldqiu的博客-优快云博客 视觉SLAM十四讲学习笔记-第三讲-旋转向量、欧拉角、四元数_goldqiu的博客-优快云博客 视觉SLAM十四讲学习笔记-第三讲-相似、仿射、..

weixin_36773706的博客 4276

Win11家庭版如何让Win+R运行CMD默认管理员权限?3种方法实测有效

本文针对Win11家庭版用户,实测了三种让Win+R运行CMD默认获取管理员权限的有效方法。通过分析UAC机制与家庭版功能限制,详细介绍了注册表编辑、快捷方式创建与任务计划程序三种方案的原理、步骤与风险对比,帮助用户根据安全与效率需求选择最佳方案,实现一键高效管理。

sat99的博客 1266

视觉SLAM第7讲:视觉里程计2(3D-2D:PnP、3D-3D:ICP)

本文主要介绍了视觉SLAM中PnP和ICP两种位姿估计算法。PnP通过3D-2D点对估计相机位姿,包括直接线性变换(DLT)、P3P等方法,并展示了如何用非线性优化和g2o进行求解。ICP则处理3D-3D点对匹配问题,推导了基于SVD的解析解,并实现了g2o优化版本。实验部分对比了OpenCV、手写高斯牛顿法和g2o在不同场景下的性能,验证了这些方法在RGB-D SLAM中的有效性。最后总结了特征点视觉里程计中的关键技术点,包括特征匹配、2D-2D运动估计和3D点重建等核心问题。

weixin_45728280的博客 1009

2-6-1-1 QNX编程入门之进程和线程(一)

文章围绕进程和线程展开介绍。先是将进程类比为房子,线程类比为居住者,阐述其基本概念,如进程占用内存,线程可共享内存。接着讲解同步相关内容,介绍了互斥,线程通过互斥锁来控制对特定资源的访问,像使用浴室一样。还提到优先级,线程按优先级和等待时长来决定获取资源顺序。此外,说明了信号量,以厨房为例,区分计数为 1 和大于 1 的信号情况,且指出信号量可当互斥锁用,但互斥锁是特殊用途信号量,二者存在区别,最后提及后续会介绍其他同步机制。

星原飞火的博客 1323

DMDiff(2025):一种基于SAR光学数据融合的双分支多模态条件引导扩散模型-k学长深度学习专栏

DMDiff提出了一种基于扩散模型的多模态遥感图像去云方法,通过双分支网络分别提取SAR的结构信息和光学图像的光谱特征,再利用跨模态注意力机制实现特征融合。创新性地采用IAP策略直接预测无云图像而非噪声,有效解决了传统方法在遥感图像处理中易出现的光谱失真问题。实验表明该方法在PSNR等指标上显著优于现有技术,但存在计算量大、生成速度慢等不足。未来可结合加速采样策略和几何约束进一步优化。

421

<视觉SLAM——PNP问题>opencv/手写位姿估计/G2O的BA优化

- 当知道一个图像的3维信息与另一个图像的2维信息(单目相机拍摄,不含深度信息),就可以估计相机在两幅图像位姿,并利用BA对位姿进行优化。 - 本次实例中的代码均为高翔老师《视觉SLAM十四讲》第一版与第二版的代码,只不过其中根据我的理解进行了部分修改,并进行了详尽的注释。

weixin_52519143的博客 805

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

G2O (General Graph Optimization)

g2o/core/block_solver.h 中 BlockSolver 有两种定义方式。一种是。

m0_49384824的博客 1476

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

g2o入门

摘要:本文介绍了g2o(General Graph Optimization)图优化库的核心架构和使用方法。g2o采用图论视角描述优化问题,其求解器配置需由内向外进行,包括线性求解器、块求解器、迭代算法和稀疏优化器四层结构。重点讲解了顶点(Vertex)和边(Edge)的定义方法,其中顶点需实现重置和更新策略,边需实现残差计算和雅可比矩阵计算。最后给出了曲线拟合的代码实例,展示了如何构建优化器、添加顶点和边,并进行优化求解。

lovod的博客 596

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

视觉SLAM理论到实践系列(六)——特征点法视觉里程计(4)

三角测量和PnP

YangtseJin的博客 1512

G2O 库

图优化

sinoai 2182
上一篇: 视觉SLAM实践入门——(14)高斯牛顿法求解PnP
下一篇: 视觉SLAM理论入门——(9)视觉里程计之特征点法—ICP
floatinglong
博客等级 码龄9年 90粉丝 143原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值