视觉SLAM实践入门——(17)使用非线性优化方法求解ICP

视觉SLAM的前段后端最详细的梳理(硕士入门知识框架更新) 文章目录视觉SLAM的组成结构视觉里程计(VO)-特征点法 -构建稀疏点云地图-直接法 -构建稠密或半稠密的地图 --需要使用 RGB-D 传感器视觉里程计--总结:SLAM 后端研究滤波器法非线性优化后端方式的差异(有五种优化方式):单目视觉里程计的初始化:运动估计:半直接法运动估计的优化方案:关于解决累计误差的一些方法:关键帧的建图及后端优化:特征的深度估计:图优化的后端研究:视觉与IMU融合的分类:视觉与IMU的融合的优势:存在的问题:优秀的开源: 视觉SLAM的组成结构 视觉里程计(VO) -特 阅读详情

使用g2o求解ICP的步骤:

1、定义顶点和边的类型

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

3、定义并设置求解器

4、定义并设置顶点和边

5、调用求解器的 initializeOptimization 函数进行优化

 

使用g2o进行优化,不仅可以对位姿进行估计,还可以同时对空间点进行优化(将空间点也加入优化变量中)

如果同时考虑点和相机,整个问题变得更自由,可能会得到其他的解。例如:可以让相机少转一些角度,而把点多移动一些

在BA中希望有尽可能多的约束,因为多次观测会带来更多的信息,能够更准确地估计每个变量

 

通过几个g2o的例子可以看到,g2o在求解非线性优化问题时具有很明显的通用性

使用g2o求解问题的关键在于构建图优化:

1、定义顶点,要明确优化变量是什么

2、定义边,要明确误差是什么、雅可比的计算公式

3、定义求解器,完成一些配置

 

#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>
#include <Eigen/SVD>
#include <Eigen/Dense>

#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);	
}

//将像素坐标转化为归一化坐标
Point2d pixelToNor(const Point2d &p, const Mat & k)
{
	//像素坐标 = k * 归一化坐标
	//xp = fx * xn + cx
	return Point2d((p.x - k.at<double>(0, 2)) / k.at<double>(0, 0), (p.y - k.at<double>(1, 2)) / k.at<double>(1, 1));
}

//顶点,模板参数:优化变量维度和数据类型
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> delta_r;
		delta_r << update[0], update[1], update[2], update[3], update[4], update[5];
		_estimate = Sophus::SE3d::exp(delta_r) * _estimate;
	}

	//存盘和读盘:留空
	virtual bool read(istream &in) {}

	virtual bool write(ostream &out) const {}
};

//边, 模板参数:观测值维度,类型,连接顶点类型
class Edge : public g2o::BaseUnaryEdge<3, Eigen::Vector3d, Vertex> {
public:
	EIGEN_MAKE_ALIGNED_OPERATOR_NEW

	Edge(const Eigen::Vector3d &p) : BaseUnaryEdge(), _p(p) {}

	//计算误差
	virtual void computeError() override 
	{	
		const Vertex *v = static_cast<const Vertex *> (_vertices[0]);	//static_cast将_vertices[0]的类型强制转换为const CurveFittingVertex *
		const Sophus::SE3d T = v->estimate();
		_error = _measurement - T*_p;
	}

	//计算雅可比矩阵
	virtual void linearizeOplus() override 
	{
		const Vertex *v = static_cast<const Vertex *> (_vertices[0]);
		const Sophus::SE3d T = v->estimate();
		
		// e = p - Tp'
		// de/d李代数
		
		Eigen::Vector3d p = T*_p;
		//<>指定子块大小,()指定子块起点
		_jacobianOplusXi.block<3, 3>(0, 0) = -Eigen::Matrix<double, 3, 3>::Identity();
		_jacobianOplusXi.block<3, 3>(0, 3) = Sophus::SO3d::hat(p);	//hat返回向量对应的反对称矩阵
	}

	virtual bool read(istream &in) {}

	virtual bool write(ostream &out) const {}

private:
	Eigen::Vector3d _p;
};

//用g20进行优化
void poseEstimate3d3d(const vector<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d>> &p1s3d, 
	const vector<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d>> &p2s3d, 
	Mat &R, Mat &t)
{
	//构建图优化,先设定g2o
	typedef g2o::BlockSolver<g2o::BlockSolverTraits<6, 3>> BlockSolverType;  //优化变量维度为6,误差值维度为3
	typedef g2o::LinearSolverDense<BlockSolverType::PoseMatrixType> LinearSolverType; // 线性求解器类型

	//梯度下降方法,可以从GN, LM, DogLeg 中选
	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);

	//往图中增加边
	for (int i = 0; i < (int)p1s3d.size(); i++) 
	{
		Edge *edge = new Edge(p1s3d[i]);
		edge->setId(i);
		edge->setVertex(0, v);                // 设置连接的顶点
		edge->setMeasurement(p2s3d[i]);      // 观测数值
		edge->setInformation(Eigen::Matrix<double, 3, 3>::Identity()); //信息矩阵
		optimizer.addEdge(edge);
	}

	//执行优化
	optimizer.initializeOptimization();
	optimizer.optimize(10);

	//以Mat格式重构T
	Sophus::SE3d T = v->estimate();
	Eigen::Matrix3d R_ = T.rotationMatrix();
	Eigen::Vector3d t_ = T.translation();	
	R = (Mat_<double>(3, 3) << 
		R_(0, 0), R_(0, 1), R_(0, 2),
		R_(1, 0), R_(1, 1), R_(1, 2),
		R_(2, 0), R_(2, 1), R_(2, 2));
	t = (Mat_<double>(3, 1) << 
		t_(0, 0), t_(1, 0), t_(2, 0));
}

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

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

	//提取ORB特征、匹配点对
	vector<KeyPoint> kp1, kp2;
	vector<DMatch> matches;
	findAndMatchOrbFeature(img1, img2, kp1, kp2, matches);
	
		
	//读取深度图,获得3d点对,每个像素占据16字节(单通道)
	Mat img3 = imread(argv[3], CV_LOAD_IMAGE_UNCHANGED);
	Mat img4 = imread(argv[4], CV_LOAD_IMAGE_UNCHANGED);	
	Mat k = (Mat_<double>(3, 3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);
	vector<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d>> p1s3d, p2s3d;
	for(auto m : matches)
	{
		Point2d p12d = kp1[m.queryIdx].pt;
		unsigned short pixel1 = img3.ptr<unsigned short>((int)p12d.y)[(int)p12d.x];
		Point2d p22d = kp2[m.trainIdx].pt;
		unsigned short pixel2 = img4.ptr<unsigned short>((int)p22d.y)[(int)p22d.x];
		
		if(pixel1 == 0 || pixel2 == 0)	continue;
		
		double depth = pixel1 / 5000.0;		
		Point2d pNor2d = pixelToNor(p12d, k);				
		Eigen::Vector3d p3d(pNor2d.x*depth, pNor2d.y*depth, depth);
		p1s3d.push_back(p3d);
		
		depth = pixel2 / 5000.0;
		pNor2d = pixelToNor(p22d, k);		
		p3d = Eigen::Vector3d(pNor2d.x*depth, pNor2d.y*depth, depth);
		p2s3d.push_back(p3d);		
	}
	
	//g2o求解ICP估计问题	
	Mat R, t;
	poseEstimate3d3d(p1s3d, p2s3d, R, t);
	cout << "R: " << endl << R << endl << "t: " << t.t() << endl;
	
	//验证点对
	for(int i = 0; i < (int)p1s3d.size(); i++)
	{
		Mat p1 = (Mat_<double>(3, 1) << p2s3d[i](0, 0), p2s3d[i](1, 0), p2s3d[i](2, 0));
		Mat p2 = (Mat_<double>(3, 1) << p1s3d[i](0, 0), p1s3d[i](1, 0), p1s3d[i](2, 0));
		
		cout << i << "\t:" << p1.t() << " and " << (R*p2+t).t() << endl;
	}
	
	
	return 0;
}

 

 

3D-3D:ICP 3D-3D:ICP1. ICP算法2.SVD方法 1. ICP算法 2D-2D问题是通过已知的2D图像坐标点对来求解图像间的变换关系(即 RRR 和 ttt)。3D-2D问题是通过已知的3D空间点坐标和对应的2D像素坐标来求解图像的位姿。 所以很显然,3D-3D问题是根据已知的3D空间点对来求解图像图像间的变换关系。 假设我们有一组配对好的 3D 点 现在想要找到一个欧式变换RRR,ttt,使得... 阅读详情

相关推荐

视觉SLAM关键方法总结

点“计算机视觉life”关注,置顶更快接收消息! 最近在做基于激光信息的机器人行人跟踪发现如果单独利用激光信息很难完成机器人对行人的识别、跟踪等功能,因此考虑与视觉融合的方法,这样便可以充分利用激光雷达提供的精确位置信息及视觉提供的丰富纹理、颜色等场景信息。以下是最近调研视觉SLAM中的实现方法的总结,包括三方面内容:姿态计算、闭环检测、BA优化。 姿态计算 一、通过提取图像的特征描述子,如...

计算机视觉life 2223

一步一步入门使用g2o解决ICP问题-估计有匹配关系的两组3维点集之间的变换关系

1.先贴上效果图(本文计算机视觉life留下的一个解决ICP问题的课后作业) 题目网址:https://mp.weixin.qq.com/s?__biz=MzIxOTczOTM4NA==&mid=2247487562&idx=1&sn=f1c5030d8bb7e58891d49ee7d245ab7e&chksm=97d7f5dda0a07ccba22349823cf63fdfee955794fa19cd89bcef159210a5192ad8d96ea412a2&sc

qq_43526137的博客 472

3D激光SLAM的帧间匹配方法: Point-to-Plane ICP、NDT、Feature-based Method

0、前言 在说Point-to-Plane ICP 之前,先介绍一下ICP: 经典ICP 形如求相机的旋转矩阵一样,ICP的目的很简单,就是求解两堆点云之间的变换关系。具体的做法是,既然不知道 R和t (针对刚体运动),那我们就假设为未知量呗,然后通过某些方法求解。 假设有两堆点云,分别记为两个集合 X=x1,x2,…,xm 和 Y=y1,y2,…,ym(m并不总是等于n)。然后呢,我们不失一般性的,假设两个点云之间的变换为 R(旋转变换)和t(平移变换),这个问题描述成最小化均方误差: ...

努力努力努力 4004

4.迭代最近点ICP及非线性优化求解

欢迎访问个人网络日志🌹🌹知行空间🌹🌹在迭代最近点算法ICP及SVD求解中介绍了问题及使用分解求解的方法。除了,还可以使用非线性优化的方法来求解。还记得,优化的目标函数为:min⁡R,t12∑in∣∣pi−(Rqi+t)∣∣22\min\limits_{R,t}\frac{1}{2}\sum\limits_{i}^n||p_i-(Rq_i+t)||_2^2R,tmin​21​i∑n​∣∣pi​−(Rqi​+t)∣∣22​我们知道旋转和平移可以写成齐次变换的形式,T=[Rt00]T=\begin{bma

机器人视觉感知 1295

视觉SLAM笔记(39) 求解 ICP

SVD 方法和非线性优化方法求解 ICP

氢键H-H 1万+

视觉SLAM笔记(38) 3D-3D: ICP

3D-3D 的位姿估计问题、迭代最近点(Iterative Closest Point, ICP)、SVD 方法、非线性优化方法BA

氢键H-H 1万+

非线性优化求解ICP实例

ceres 求解icp

Enochzhu的博客 1330

DeepSeek 在 PostgreSQL 中,执行 `DELETE` 操作后,数据占用的空间并不会立即释放给操作系统

操作后,数据占用的空间并不会立即释放给操作系统。这是 PostgreSQL 的 MVCC(多版本并发控制)机制决定的——删除的数据只是被标记为“死元组”,物理空间仍然被表占用。这是最彻底的释放方式,会将表重写为一个没有空洞的新文件,将空间返回给操作系统。如果你的表非常大(如几百GB),或者这是生产环境的核心表,建议优先考虑。你可以告诉我你的表大概多大、是否能接受锁表,我可以给你更精确的建议。要真正释放空间,需要根据你的实际需求,从以下三种方案中选择一种。如果你既想彻底释放空间,又无法接受锁表,

m0_37843156的博客 403

SLAM ICP介绍、详细推导及相关变种

SLAM ICP介绍、详细推导及相关变种

欢迎交流! 1889

用Python+Charles搞定小程序付费短剧下载(附完整代码与避坑点)

本文详细介绍了如何利用Python和Charles抓包工具实现小程序付费短剧的自动化下载。通过分析小程序视频接口、破解请求参数,并编写Python脚本,读者可以轻松获取并保存短剧内容。文章还提供了完整的代码示例和常见问题的解决方案,特别适合对视频采集和抓包技术感兴趣的开发者。

weixin_28731231的博客 1187

浅谈车联网概述-1

车联网技术目前在车企中应用广泛,车联网通过车载传感器、无线通信等技术,能够将车辆上百的传感器数据上传至云端,当前智能网联车辆可以理解为一个“大号”手机。

Hello, World! 新能源汽车智能网联工程师,分享日常工作思考。 559

使用g2o进行点云ICP求解

【代码】使用g2o进行点云ICP求解。

qq_22230823的博客 680

WPS中VBA支持库安装全攻略:从报错到一键解决

VBA作为Office生态中广泛使用的宏语言,是办公自动化的重要基础。WPS表格虽然兼容Excel文件格式,但出于技术路线与授权原因,默认并未集成完整的VBA引擎与开发环境,导致用户打开含宏文件时频繁遇到“找不到工程或库”“未找到VBA支持库”等报错。理解VBA支持库包含引擎与IDE两层结构,并掌握系统位数、版本匹配、安全软件拦截等前置条件,是成功部署的关键。通过可靠的安装源与规范流程完成配置后,即可实现在WPS中运行Excel宏、批量处理数据、自定义函数等自动化场景,显著提升报表处理效率。本文围绕WPS中

weixin_31102231的博客 305

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

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

weixin_36773706的博客 3076

突破III-V/硅混合集成的挑战:UTC光电探测器能带工程仿真实践

本文探讨了UTC光电探测器在III-V/硅混合集成中的能带工程仿真实践,通过Ansys Lumerical平台实现多物理场仿真,解决晶格失配和能带不连续性等关键挑战。文章详细介绍了FDTD光学仿真和CHARGE电学仿真方法,优化器件性能,提升响应度和带宽,为高速光通信和量子计算提供技术支持。

weixin_29219539的博客 295

微调大模型如何准备数据集——常用数据集,Alpaca和ShareGPT

在大模型微调中,我们一般使用Alpaca 和 ShareGPT和两种格式的数据集。

Science prince的博客 1096

视觉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

【扣子AI开发】如何开发一个图像生成智能体?

【扣子AI开发】如何开发一个图像生成智能体,从创建到发布

tl437002770的博客 2427

高翔《视觉SLAM十四讲》从理论到实践

目录 第1讲前言:本书讲什么;如何使用本书; 第2讲 初始SLAM:引子-小萝卜的例子;经典视觉SLAM框架;SLAM问题的数学表述;实践-编程基础; 第3讲 三维空间刚体运动:旋转矩阵;实践-Eigen;旋转向量和欧拉角;四元数;相似、仿射、射影变换;实践-Eigen几何模块;可视化演示; 第4讲 李群与李代数:李群李代数基础;指数与对数映射;李代数求导与扰动模型;实践-Sophu...

Night___Raid的博客 4421

1、网站可靠性工程(SRE):原理、实践与转型指南

本文深入探讨了网站可靠性工程(SRE)的核心原理、实践方法以及组织转型策略。从SRE的基本概念如服务水平指标(SLI)、服务水平目标(SLO)和错误预算入手,详细解析了如何通过SRE实现产品开发、运营和管理团队的高效协作。此外,还涵盖了SRE的实施步骤,包括现状评估、组织认同、基础设施建设、事件响应、错误预算政策制定以及组织结构设计等内容。最后,文章展望了SRE的未来发展,强调了服务目录、合规监管及SRE文化建设的重要性,为企业的现代化运营体系转型提供了全面指南。

xk7n92h3p5m的博客 145

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

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

ICP求解

YangtseJin的博客 1030

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

SLAM十四讲之第七讲:3D-3D ICP求解

​ICP问题存在唯一解或无穷多解的情况。在唯一解的情况下,只要能找到极小值解,这个极小值就是全局最优值(因此不会遇到局部极小而非全局最小的情况)。这也意味着ICP求解可以任意选定初始值。这是已匹配点时求解ICP的一大好处。

qq_49561752的博客 579

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

视觉SLAM——位姿估计

******看完位姿估计原理过一段时间又忘记了,现参考高翔视觉SLAM十四讲做本文笔记。 SLAM中的里程计都是基于特征点方式的算法。 1.特征匹配 特征匹配是视觉SLAM中极为关键的一步,它解决了数据关联问题。通过图像与图像或图像与地图间描述子进行准确匹配,可以为后续姿态估计、优化等操作减轻大量负担。由于图像特征的局部特性、误匹配、重复纹理等,仅利用局部特征很难解决问题。首先讨论图像与图像匹配...

xranmail的博客 3039

SLAM后端--滤波方法和非线性优化方法

SLAM的后端一般分为两种处理方法,一种是以扩展卡尔曼滤波(EKF)为代表的滤波方法,一种是以图优化为代表的非线性优化方法。不过,目前SLAM研究的主流热点几乎都是基于图优化的。 滤波方法的优缺点: 优点:在当时计算资源受限、待估计量比较简单的情况下,EKF为代表的滤波方法比较有效,经常用在激光SLAM中。 缺点:它的一个大缺点就是存储量和状态量是平方增长关系,因为存储的是协方差矩阵,因此不...

追求卓越583的博客 1万+

视觉SLAM的ICP问题——SVD及G2O求解

- 迭代最近点法(ICP)可以用来求解两组三维图像(或三维点云)间的位姿变换关系。可以通过奇异值分解或非线性优化方式求解。 关于ICP求解的推导公式均在课本第七讲。 - 下面分别介绍SVD求解方法、自定义顶点的G2O求解方法、采用内置顶点的G2O求解方法。

weixin_52519143的博客 1168

激光SLAM从理论到实践学习——第四节(激光SLAM的前端配准方法)

激光的前端配准算法(帧间匹配算法) 前端配准在视觉内叫做Tracking或者帧间匹配,对激光SLAM是有非常大的影响的 帧间匹配不一定说的是前后两帧进行匹配,也可以是任意帧之间进匹配 是一个Map—>Scan的过程,一个Scan和一个Map去匹配的过程 常用的方法有下面四种 ICP匹配方法(对上节ICP算法的证明) PL-ICP匹配方法(与ICP算法相似,只是说误差是之间的...

摆正心态 3927
上一篇: 视觉SLAM实践入门——(16)使用SVD求解ICP
下一篇: ubuntu中安装另一个版本的opencv
floatinglong
博客等级 码龄9年 90粉丝 143原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值