SLAM 求解IPC算法

基础知识:方差,协方差,协方差矩阵


方差:描述了一组随机变量的离散程度

        方差= 每个样本值 与 全部样本的平均值 相差的平方和  再求平均数,记作:

        例如:计算数字1-5的方差,如下

去中心化:为了后续计算的方便,会对样本进行去中心化处理,方法是将全部样本按照平均值平移

例如:1-5每个数字都向负方向移动3(平均值)个单位,计算方差后结果依然是2


协方差:协方差描述了不同特征之间的相关情况,通过计算协方差,可以判断不同特征之间的关联关系。协方差=m个样本的(特征a-均值ua )乘以(特征b - 均值 ub)的乘积累加到一起再除以m-1

        例如1:一组数据点(1,1)(2,2)(3,3)(4,4)(5,5)他们的协方差计算如下

        例如2:同理

        例如3:同理

为了更方便的计算协方差,同样的也可以将数据去中心化处理

总之:协方差表示了不同特征之间的相关情况,想个特征值之间的协方差>0,则正相关,<0则负相关,=0则不相关


协方差矩阵:计算了不同维度的协方差,他是一个对对称矩阵,由方差和协方差两部分组成,其中,对角线上的元素是各个随机变量的方差,非对角线上的元素为两两随机变量之间的协方差。

在计算协方差矩阵时,需要将m个样本的特征按照列向量的方式,保存在矩阵中,然后计算矩阵和矩阵转置的乘积,再除以m,得到协方差矩阵

        例如:m个样本,每个样本有a和b两个特征,将这些样本按照列向量的方式,保存到矩阵x中,计算m个样本的协方差矩阵,他等于x乘以x的转置,再除以m。


1.SVD求解ICP方法C++代码展示,总结起来分为3步

#include<iostream>
#include<vector>
#include<eigen>
using namespace std;
//函数用于估计两组三维点集之间的旋转矩阵 R 和平移向量 t
//通过这段代码,可以实现对两组三维点集之间的姿态关系进行估计和计算,其中旋转矩阵R_用于描述旋转关系,平移向量t_用于描述平移关系
void pose_estimation_3d3d(const vector<Point3f>& pts1,const vector<Point3f>& pts2,Mat& R, Mat& t)
{// 计算两组三维点的质心Point3f p1, p2;int N = pts1.size();for (int i=0; i<N; i++){p1 += pts1[i];p2 += pts2[i];}p1 /= N;p2 /= N;// 对每个减去质心,得到新的点集q1,q2vector<Point3f> q1(N), q2(N);for (int i=0; i<N; i++){q1[i] = pts1[i] - p1;q2[i] = pts2[i] - p2;}// 计算协方差矩阵3x3 q1*q2^TEigen::Matrix3d W = Eigen::Matrix3d::Zero();for (int i=0; i<N; i++){W += Eigen::Vector3d(q1[i].x, q1[i].y, q1[i].z) * Eigen::Vector3d(q2[i].x,q2[i].y, q2[i].z).transpose();}cout << "W=" << W << endl;// SVD on W  对矩阵 W 进行奇异值分解(SVD)得到 U 和 V 矩阵。Eigen::JacobiSVD<Eigen::Matrix3d> svd(W, Eigen::ComputeFullU | Eigen::ComputeFullV);Eigen::Matrix3d U = svd.matrixU();Eigen::Matrix3d V = svd.matrixV();cout << "U=" << U << endl;cout << "V=" << V << endl;//根据计算出的 U 和 V 矩阵计算旋转矩阵 R 和平移向量 t。Eigen::Matrix3d R_ = U * (V.transpose());Eigen::Vector3d t_ = Eigen::Vector3d(p1.x, p1.y, p1.z) - R_ * Eigen::Vector3d(p2.x, p2.y, p2.z);//p1 p2分别为两组数据的中心点//将计算得到的旋转矩阵 R 和平移向量 t 转换为 OpenCV 的 Mat 类型。// convert to cv::MatR = (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));
}

经过上面的步骤,其实就可以得到R和T了,但是,这时候就出现了一个问题——结果不准确。在算法实现中,如果出现了求解值不准确的情况,那么一般做法就是——多求几次,也就是迭代!可以参考如下:

  • 从B点云中一一找到A中点的对应距离最近点,构成最近点集C
  • 把C点集存入Eigen矩阵中,和A点云去中心化后,求SVD分解,得到R矩阵和T向量(一个旋转一个平移)
  • 开始迭代,通过R×A+T得到新的点云A1
  • 重新执行1到3步骤,这次是从B中找A1的最近点
  • 求得到的点云An和它的最近点集Cn的平均距离dst,当dst小于设定的阈值时,跳出循环

如果发现还不准确,那么有可能是它的迭代条件——也就是平均距离dst判断出错了,出现这种原因一般就是点云中出现了离散点,导致某两点的距离出现了异常,带动了整个dst判断出错。解决方案如下(很管用):

  • 遍历A点找寻最近点,如果A中的某个点Ai和它的最近点距离大于某个阈值,则剔除,不参与接下来的计算。
  • 从B点云中一一找到A中点的对应距离最近点,构成最近点集C
  • 把C点集存入Eigen矩阵中,和A点云去中心化后,求SVD分解,得到R矩阵和T向量(一个旋转一个平移)
  • 开始迭代,通过R×A+T得到新的点云A1
  • 重新执行1到4,每次执行都要剔除一下离散点。
  • 求得到的点云An和它的最近点集Cn的平均距离dst,当dst小于设定的阈值时,跳出循环

2.非线性优化求解ICP c++代码展示

#include <iostream>
#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>
using namespace std;
//定义VertexPose顶点              //顶点为6个优化变量,每个类型为SE3d(表示三维空间中的刚体变换,即旋转和平移)
class VertexPose : public g2o::BaseVertex<6, Sophus::SE3d> {public:EIGEN_MAKE_ALIGNED_OPERATOR_NEW;// 设置初始化的更新值 virtual void setToOriginImpl() override { _estimate = Sophus::SE3d();}// left multiplication on SE3virtual void oplusImpl(const double *update) {Eigen::Matrix<double, 6, 1> update_eigen;//前三个元素表示平移在 x、y、z 轴上的分量,后三个元素表示旋转的绕 x、y、z 轴的旋转量update_eigen << update[0], update[1], update[2],update[3], update[4], update[5];_estimate = Sophus::SE3d::exp(update_eigen) * _estimate;//exp 将update_eigen向量转换成SE3d 类型的刚体变换}virtual bool read(std::istream &in) override {return true;}virtual bool write(std::ostream &out) const override { return true;}
};
//定义边 一元边,连接一个顶点VertexPose ,和一个包含三维向量的观测
class EdgeProjectXYZRGBDPoseOnly : public g2o::BaseUnaryEdge<3, Eigen::Vector3d, bcv::VertexPose> 
{public:EIGEN_MAKE_ALIGNED_OPERATOR_NEW;EdgeProjectXYZRGBDPoseOnly(const Eigen::Vector3d &point) : _point(point) {}virtual void computeError() override {const VertexPose* p = static_cast<const VertexPose*> (_vertices[0]);//真实观测值 _measurement 与 估计观测值 p->estimate() * _point之间的误差_error = _measurement - p->estimate() * _point;//将顶点的估计值所代表的变换作用于点 _point,得到的新的位置信息}//linearizeOplus 函数实现了对雅可比矩阵的线性化操作virtual void linearizeOplus() override {VertexPose *p = static_cast<VertexPose*> (_vertices[0]);//从图优化中获取与当前边相连的顶点Sophus::SE3d T = p->estimate();//获取顶点的估计值(优化变量,用于计算位姿变换)Eigen::Vector3d xyz_trans = T * _point;//通过估计的值 计算当其点_point转换后的坐标//雅可比矩阵从 (0,0) 开始的 3×3 子矩阵(前三行前三列),设置为负的单位矩阵,表示误差函数对位姿变量的平移部分的导数_jacobianOplusXi.block<3, 3>(0, 0) = -Eigen::Matrix3d::Identity();//雅可比矩阵的前三行后三列部分,利用 Sophus 库的 hat 操作将向量 xyz_trans 转换为反对称矩阵,通常表示误差函数对位姿变量的旋转部分的导数_jacobianOplusXi.block<3, 3>(0, 3) = Sophus::SO3d::hat(xyz_trans);}bool read(std::istream &in) { return true; }bool write(std::ostream &out) const { return true; }protected:Eigen::Vector3d _point;
};
//定义求解器
void ICPSolver::NLOSolver(std::vector<cv::Point3f> &pts1,std::vector<cv::Point3f> &pts2,cv::Mat &R, cv::Mat &t)
{typedef g2o::BlockSolverX BlockSolverType;//优化问题求解器typedef g2o::LinearSolverDense<BlockSolverType::PoseMatrixType> LinearSolverType;//稠密线性方程求解类型// new一个 g2o优化器 采用高斯牛顿优化算法auto solver = new g2o::OptimizationAlgorithmGaussNewton(g2o::make_unique<BlockSolverType>(g2o::make_unique<LinearSolverType>()));//构建优化问题的图模型g2o::SparseOptimizer optimizer; // graph modeloptimizer.setAlgorithm(solver); // set solveroptimizer.setVerbose(true); // print info//添加顶点bcv::VertexPose *p = new VertexPose();p->setId(0);//顶点idp->setEstimate(Sophus::SE3d());//初始估计值optimizer.addVertex(p);//添加边for(size_t i = 0; i < pts1.size(); i++) {bcv::EdgeProjectXYZRGBDPoseOnly *e = new bcv::EdgeProjectXYZRGBDPoseOnly(Eigen::Vector3d(pts2[i].x, pts2[i].y, pts2[i].z));e->setVertex(0, p);//将上一步的顶点设置为边e的第一个顶点,本次只有一个顶点e->setMeasurement(Eigen::Vector3d(pts1[i].x, pts1[i].y, pts1[i].z));//设置了边的测量值(实际位置)e->setInformation(Eigen::Matrix3d::Identity());//设置边的信息矩阵为单位矩阵,表示边的置信度optimizer.addEdge(e);}auto t1 = std::chrono::system_clock::now();optimizer.initializeOptimization();//初始化优化器optimizer.optimize(100);//迭代次数auto t2 = std::chrono::system_clock::now();auto d = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();std::cout << "duration: " << d << " ms" << std::endl;std::cout << "after optim:\n";std::cout << "T=\n" << p->estimate().matrix() << std::endl;Eigen::Matrix3d R_ = p->estimate().rotationMatrix();//estimate()提取估计值,rotationMatrix()提取旋转矩阵Eigen::Vector3d t_ = p->estimate().translation();//提取平移向量std::cout <<"det(R_)=" << R_.determinant() << std::endl;std::cout <<"R_R_^T=" << R_ * R_.transpose() << std::endl;std::cout << "R:\n" << R_ << std::endl;std::cout << "t:\n" << t_ << std::endl;R = (cv::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 = (cv::Mat_<double>(3, 1) << t_(0, 0), t_(1, 0), t_(2, 0));
}

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

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

相关文章

Power BI ----SVG(圆环图)

圆环图助力矩阵图 定义度量值放置视觉对象 SVG是什么鬼&#xff0c;在现在的Web世界中越来越凸显这一标准的优势。关于SVG&#xff0c;我们只需要知道一点就好 ---- SVG 意为可缩放矢量图形&#xff08;Scalable Vector Graphics&#xff09;。它是使用 XML 格式定义的图像。 由…

【LeetCode 算法刷题笔记-路径篇】

1.0112. 路径总和 1.1 题目大意 描述&#xff1a;给定一个二叉树的根节点 root 和一个值 targetSum。 要求&#xff1a;判断该树中是否存在从根节点到叶子节点的路径&#xff0c;使得这条路径上所有节点值相加等于 targetSum。如果存在&#xff0c;返回 True&#xff1b;否则…

elementUI Tree 树形控件单选实现

文章目录 展示效果代码实现elementui Tree树形控件其他详细数据 在Element UI中&#xff0c;树形控件&#xff08;el-tree&#xff09;本身不支持单选功能。但是&#xff0c;你可以通过监听节点点击事件并手动更新选中状态来实现单选树。 以下是一个简单的例子&#xff0c;展示…

React【Day1】

B站视频链接 一、React介绍 React由Meta公司开发&#xff0c;是一个用于 构建Web和原生交互界面的库 React的优势 相较于传统基于DOM开发的优势 组件化的开发方式不错的性能 相较于其它前端框架的优势 丰富的生态跨平台支持 React的市场情况 全球最流行&#xff0c;大…

pandas读写excel,csv

1.读excel 1.to_dict() 函数基本语法 DataFrame.to_dict (self, orientdict , into ) --- 官方文档 函数种只需要填写一个参数&#xff1a;orient 即可 &#xff0c;但对于写入orient的不同&#xff0c;字典的构造方式也不同&#xff0c;官网一共给出了6种&#xff0c…

API(时间类)

一、Date类 java.util.Date类 表示特定的瞬间&#xff0c;精确到毫秒。 Date常用方法&#xff1a; public long getTime() 把日期对象转换成对应的时间毫秒值。 public void setTime(long time) 把方法参数给定的毫秒值设…

python网络爬虫实战教学——requests的使用(1)

文章目录 专栏导读1、前言2、get请求3、抓取网页4、抓取二进制数据5、请求头 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN 数据分析领域优质创作者&#xff0c;专注于分享python数据分析领域知识。 ✍ 本文录入于《python网络爬虫实战教学》&#xff0c;本专栏针对…

部署Prometheus+grafana详解

目录 一、prometheus 介绍 二、prometheus 对比 zabbix 三、prometheus 监控插件 四、部署 1、下载所需的包 2.编辑prometheus的配置文件 3、编辑alertmanager 的配置文件 4、tmpl 模板&#xff08;将此文件创建在/opt/alertmanager/tmpl/&#xff09; 5.启动&#xff0…

探索国内ip切换App:打破网络限制

在国内网络环境中&#xff0c;有时我们会遇到一些限制或者屏蔽&#xff0c;使得我们无法自由访问一些网站或服务。而国内IP切换App的出现&#xff0c;为解决这些问题提供了非常便捷的方式。这些App可以帮助用户切换IP地址&#xff0c;让用户可以轻松地访问被限制或屏蔽的网站&a…

leetcode刷题(javaScript)——BFS广度优先遍历相关场景题总结

广度优先搜索&#xff08;BFS&#xff09;在JavaScript编程中有许多实际应用场景&#xff0c;特别是在解决图、树等数据结构相关问题时非常常见。在JavaScript中&#xff0c;可以使用队列来实现广度优先搜索算法。通过将起始节点加入队列&#xff0c;然后迭代地将节点的邻居节点…

css background-color属性无效

因为工作需要&#xff0c;最近在帮H5同事开发几个页面&#xff0c;在使用H5进行如下布局的时候&#xff0c;发现设置 background-color为白色无效。 代码如下&#xff1a; <div class "bottomBar"><div style"position: fixed; left: 20px;">…

同步服务器操作系统公网仓库到本地02--搭建http内网仓库源 _ 麒麟KOS _ 统信UOS _ 中科方德 NFSCNS

原文链接&#xff1a;同步服务器操作系统公网仓库到本地02 —搭建http内网仓库源| 麒麟KOS | 统信UOS | 中科方德 NFSCNS Hello&#xff0c;大家好啊&#xff01;继之前我们讨论了如何同步服务器公网仓库到本地服务器之后&#xff0c;今天我们将进入这个系列的第二篇文章——通…

美容美发行业在线下单小程序源码系统 一键在线预约 带完整的安装代码包以及安装部署教程

近年来&#xff0c;美容美发市场竞争日益激烈&#xff0c;传统的经营模式已难以满足消费者的多样化需求。为了适应市场变化&#xff0c;提升服务质量&#xff0c;许多商家开始寻求数字化转型。然而&#xff0c;由于技术门槛较高&#xff0c;很多商家在开发在线预约系统时遇到了…

中兴通讯服务器荣获滴滴“最佳需求响应「和衷共济」奖”

在数字经济加速发展的背景下&#xff0c;算力成为数字产业的核心支撑力量&#xff0c;而服务器和存储产品更是为互联网创新体验提供了底层基础设施保障。在此背景下&#xff0c;中兴通讯服务器产品有效支撑滴滴出行智慧交通解决方案&#xff0c;凭借卓越表现&#xff0c;获得滴…

StarRocks-2.5.13部署安装

1、安装jdk11 tar xf jdk-11.0.16.1_linux-x64_bin.tar.gz mv jdk-11.0.16.1 /data/soft/jdk-11 # 配置在/etc/profile中 export JAVA_HOME/data/soft/jdk-11 export CLASSPATH.:/data/soft/jdk-11/lib export PATH/data/soft/jdk-11/bin:$PATH # 验证jdk [rootdb-public-03 s…

大广赛获奖作品一览

大广赛指全国大学生广告艺术大赛&#xff0c;这是一项由中国教育部高等教育司指导、中国高等教育学会广告教育专业委员会主办的全国性高校文科大赛&#xff0c;也是迄今为止全国规模大、覆盖高等院校广、参与师生人数多、作品水准高的国家级大学生赛事。 大广赛的竞赛形式主要…

vue 隐藏导航栏和菜单栏

初始效果&#xff1a; 效果&#xff1a;

梦百合发布“正确睡眠观”,再次呼吁“别睡硬床”

3月21日“世界睡眠日”当天,MLILY梦百合召开了主题为“别睡硬床”的品牌发布会,梦百合家居董事长倪张根发布了一场线上主题演讲,普及睡硬床可能带来的危害,呼吁国人“别睡硬床!”,并发布“100万张硬床垫改造计划”,期望消费者通过从体验一张薄垫开始,从而逐步认识到睡硬床的危害…

Go——map

一.map介绍和使用 map是一种无序的基于key-value的数据结构&#xff0c;Go语言的map是引用类型&#xff0c;必须初始化才可以使用。 1. 定义 Go语言中&#xff0c;map类型语法如下&#xff1a; map[KeyType]ValueType KeyType表示键类型ValueType表示值类型 map类型的变量默认…

网络原理(4)——TCP协议的特性

目录 一、滑动窗口 1、ack丢了 2、数据丢了 二、流量控制&#xff08;流控&#xff09; 三、拥塞控制 拥塞窗口动态变化的规则 四、延时应答 五、捎带应答 六、面向字节流 七、异常情况 &#xff08;1&#xff09;进程崩溃了 &#xff08;2&#xff09;其中一方关机…