当前位置: 首页 > 编程日记 > 正文

ORB_SLAM2 PnPSolver

EPNP:已知4组(默认)3D-2D匹配点,构建参考点,通过计算参考点的相机坐标,线性组合成路标点的相机坐标。然后使用ICP估计相机间的位姿变换。
  需要注意的事,EPNP可以同时使用N组路标点构建M矩阵,然后求解出N个路标点的相机坐标。
  使用RANSCA+EPNP的方法估计相机位姿,大致步骤为:
(1)设置RANSAC参数,比如迭代次数、最少内点数。
(2)随机选出4组3D-2D点对,使用EPNP估计路标点的相机坐标。
(3)使用ICP估计当前帧位姿。
(4)计算内点数(重投影误差小于阈值的话即为内点),选内点数最多时对应的位姿为最优位姿。
(5)以内点为一组,再次进行EPNP+ICP估计相机位姿,计算内点。如果内点数满足要求,则获得了最优位姿。
  在关键帧模式(以上一帧位姿为当前帧位姿)、恒速运动模型(根据前两帧间的位姿变换估计当前帧位姿)中不会用到EPNP(因为不需要去估计位姿,只需要去优化)。在重定位、回环检测的时候需要使用EPNP估计当前帧位姿,然后进行优化。

构造函数

PnPsolver::PnPsolver(const Frame &F, const vector<MapPoint*> &vpMapPointMatches):

(1)保存每一个路标点、特征点的信息。
(2)设置相机内参
(3)设置RANSAC参数。

设置RANSAC参数

/*** @brief 设置RANSAC迭代的参数* @param[in] probability       用于计算RANSAC理论迭代次数所用的概率* @param[in] minInliers        退出RANSAC所需要的最小内点个数, 注意这个只是给定值,最终迭代的时候不一定按照这个来* @param[in] maxIterations     设定的最大RANSAC迭代次数* @param[in] minSet            表示求解这个问题所需要的最小的样本数目,简称最小集;参与到最小内点数的确定过程中,默认是4* @param[in] epsilon           希望得到的 内点数/总体数 的比值,参与到最小内点数的确定过程中* @param[in] th2               内外点判定时的距离的baseline(程序中还会根据特征点所在的图层对这个阈值进行缩放的)*/
void PnPsolver::SetRansacParameters(double probability, int minInliers, int maxIterations, int minSet, float epsilon, float th2)

设定RANSAC的参数

计算RANSAC理论次数的所用的概率、退出RANSAC的最少内点数、RANSAC的最大迭代次数等

确定退出RANSAC所需的最小内点数

    int nMinInliers = N*mRansacEpsilon; if(nMinInliers<mRansacMinInliers)nMinInliers=mRansacMinInliers;if(nMinInliers<minSet)nMinInliers=minSet;mRansacMinInliers = nMinInliers;

确定迭代次数

    int nIterations;if(mRansacMinInliers==N)//根据期望的残差大小来计算RANSAC需要迭代的次数nIterations=1;elsenIterations = ceil(log(1-mRansacProb)/log(1-pow(mRansacEpsilon,3)));mRansacMaxIts = max(1,min(nIterations,mRansacMaxIts));

计算四个控制点坐标

void PnPsolver::choose_control_points(void)

cws[4][3]:控制点坐标。第一维表示是哪个控制点,第二维表示是哪个坐标(x,y,z)。

第一个控制点坐标为所有点坐标之和的均值

  cws[0][0] = cws[0][1] = cws[0][2] = 0;// 遍历每个匹配点中世界坐标系3D点,然后对每个坐标轴加和// number_of_correspondences 默认是 4for(int i = 0; i < number_of_correspondences; i++)for(int j = 0; j < 3; j++)cws[0][j] += pws[3 * i + j];// 再对每个轴上取均值for(int j = 0; j < 3; j++)cws[0][j] /= number_of_correspondences;

计算其余三个控制点的坐标

  // 将所有的3D参考点写成矩阵,(number_of_correspondences * 3)的矩阵CvMat * PW0 = cvCreateMat(number_of_correspondences, 3, CV_64F);double pw0tpw0[3 * 3], dc[3], uct[3 * 3];         // 下面变量的数据区CvMat PW0tPW0 = cvMat(3, 3, CV_64F, pw0tpw0);     // PW0^T * PW0,为了进行特征值分解CvMat DC      = cvMat(3, 1, CV_64F, dc);          // 特征值CvMat UCt     = cvMat(3, 3, CV_64F, uct);         // 特征向量// Step 2.1:将存在pws中的参考3D点减去第一个控制点(均值中心)的坐标(相当于把第一个控制点作为原点), 并存入PW0for(int i = 0; i < number_of_correspondences; i++)for(int j = 0; j < 3; j++)PW0->data.db[3 * i + j] = pws[3 * i + j] - cws[0][j];// Step 2.2:利用特征值分解得到三个主方向// PW0^T * PW0// cvMulTransposed(A_src,Res_dst,order, delta=null,scale=1): // Calculates Res=(A-delta)*(A-delta)^T (order=0) or (A-delta)^T*(A-delta) (order=1)cvMulTransposed(PW0, &PW0tPW0, 1);// 这里实际是特征值分解cvSVD(&PW0tPW0,                         // A&DC,                              // W,实际是特征值&UCt,                             // U,实际是特征向量0,                                // VCV_SVD_MODIFY_A | CV_SVD_U_T);    // flagscvReleaseMat(&PW0);// Step 2.3:得到C1, C2, C3三个3D控制点,最后加上之前减掉的第一个控制点这个偏移量for(int i = 1; i < 4; i++) {// 这里只需要遍历后面3个控制点double k = sqrt(dc[i - 1] / number_of_correspondences);for(int j = 0; j < 3; j++)cws[i][j] = cws[0][j] + k * uct[3 * (i - 1) + j];

计算控制点的线性参数

4个控制点通过线性参数组合成任意路标点:
Pw = a1×C1+a2×C2+a3×C3+a4×C4(a1,a2,a3,a4为参数,C1、C2、C3、C4为参考点坐标)。

void PnPsolver::compute_barycentric_coordinates(void)

计算M矩阵

根据M矩阵,可求出参考点的相机坐标

void PnPsolver::fill_M(CvMat * M,const int row, const double * as, const double u, const double v)

计算L矩阵

void PnPsolver::compute_L_6x10(const double * ut, double * l_6x10)

计算LB = ρ中的雅克比矩阵、ρ-LB

void PnPsolver::compute_A_and_b_gauss_newton(const double * l_6x10, const double * rho,double betas[4], CvMat * A, CvMat * b)

使用QR分解来求解增量方程

/*** @brief 使用QR分解来求解增量方程 * @param[in]  A   系数矩阵* @param[in]  b   非齐次项* @param[out] X   增量*/
void PnPsolver::qr_solve(CvMat * A, CvMat * b, CvMat * X)

更新Beta的值

void PnPsolver::gauss_newton(const CvMat * L_6x10, const CvMat * Rho,double betas[4])

计算控制点的相机坐标

已知MTM特征值为0时的特征向量,已知deta的值,可求出控制点的相机坐标。

void PnPsolver::compute_ccs(const double * betas, const double * ut)

计算路标点的相机坐标

已知控制点的相机坐标,通过线性参数获得路标点的相机坐标

void PnPsolver::compute_pcs(void)

ICP求解相机位姿

根据路标点的世界坐标和相机坐标,估计相机的位姿。

void PnPsolver::estimate_R_and_t(double R[3][3], double t[3])

EPNP计算相机位姿

(1)计算4对控制点的世界坐标。
(2)计算线性系数α。
(3)构造M矩阵。
(4)通过SVD分解,计算MTM的0特征值对应的特征向量。
(5)计算L矩阵。
(6)计算线性系数β
(7)根据β和MTM的0特征值对应的特征向量,获得控制点的相机坐标---->根据α线性组合获得路标点的相机坐标。
(8)根据路标点在世界坐标系中的坐标和相机坐标系中的坐标,使用ICP估计相机位姿。
(9)根据相机位姿,求解每个路标点的重投影误差,判断内点个数。
(10)选择内点个数最多时的位姿为最优位姿。

double PnPsolver::compute_pose(double R[3][3], double t[3])

EPNP+RANSAC迭代求解

cv::Mat PnPsolver::iterate(int nIterations, bool &bNoMore, vector<bool> &vbInliers, int &nInliers)

设置EPNP每次取的点的个数

set_maximum_number_of_correspondences(mRansacMinSet);

进行迭代

迭代的条件:
(1)当前帧的迭代次数小于阈值。
(2)所有帧的迭代次数之和小于阈值。

while(mnIterations<mRansacMaxIts || nCurrentIterations<nIterations)

随机取出四组匹配点(RANSAC)

对于EPNP,可以同时利用N对点的信息,求解这N个路标点的相机坐标,这里默认为4随机取出四组3D-2D匹配点,同时在列表中删除已经取出的点。

        // Get min set of points// 随机选取4组(默认数目)最小集合for(short i = 0; i < mRansacMinSet; ++i){int randi = DUtils::Random::RandomInt(0, vAvailableIndices.size()-1);// 将生成的这个索引映射到给定帧的特征点idint idx = vAvailableIndices[randi];// 将对应的3D-2D压入到pws和us. 这个过程中需要知道将这些点的信息存储到数组中的哪个位置,这个就由变量 number_of_correspondences 来指示了add_correspondence(mvP3Dw[idx].x,mvP3Dw[idx].y,mvP3Dw[idx].z,mvP2D[idx].x,mvP2D[idx].y);// 从"可用索引表"中删除这个已经被使用的点vAvailableIndices[randi] = vAvailableIndices.back();vAvailableIndices.pop_back();} // 选取最小集

使用EPNP估计相机位姿

compute_pose(mRi, mti);

计算内点数

重投影误差小于阈值的话就是内点,否则为外点。

CheckInliers();

获得最优的位姿

选择内点数最多的那组位姿为当前帧的最优位姿。

            if(mnInliersi>mnBestInliers){mvbBestInliers = mvbInliersi;mnBestInliers = mnInliersi;cv::Mat Rcw(3,3,CV_64F,mRi);cv::Mat tcw(3,1,CV_64F,mti);Rcw.convertTo(Rcw,CV_32F);tcw.convertTo(tcw,CV_32F);mBestTcw = cv::Mat::eye(4,4,CV_32F);Rcw.copyTo(mBestTcw.rowRange(0,3).colRange(0,3));tcw.copyTo(mBestTcw.rowRange(0,3).col(3));} // 更新最佳的计算结果

进一步精求位姿

以上面求出的内点构建M矩阵,再一次求出相机位姿。

if(Refine())

相关文章:

iOS开发UI篇—多控制器和导航控制器简单介绍

iOS开发UI篇—多控制器和导航控制器简单介绍 一、多控制器 一个iOS的app很少只由一个控制器组成,除非这个app极其简单。当app中有多个控制器的时候,我们就需要对这些控制器进行管理 有多个view时&#xff0c;可以用一个大的view去管理1个或者多个小view&#xff0c;控制器也是如…

值传递 和 地址传递 的区别(好多句话才可以说清)

要知道什么是**值传递**和**地址传递**&#xff0c;首先要知道什么是**基本数据类型**和**引用类型**。 Java的基本数据类型就八种&#xff08;除了这八种之外都是引用类型&#xff09;&#xff1a;◆整型&#xff1a;byte(8位) short(16位) int(32位) long(64位)&#xff1…

日期类的时间从为什么是从1970年1月1日(格林威治时间)

I suspect that Java was born and raised on a UNIX system.UNIX considers the epoch (when did time begin) to be midnight, January 1, 1970.是说java起源于UNIX系统&#xff0c;而UNIX认为1970年1月1日0点是时间纪元. 但这依然没很好的解释"为什么",出于好奇&a…

MySQL从5.7.32升级到8.0.22

目录 一、备份与导出数据 二、升级到MySQL-8.0.22同时解决字符集问题 2.1、卸载MySQL-5.7.32 2.2、安装MySQL-8.0.22 2.3、修改配置文件 my.cnf 三、导入数据及配置 3.1、重新创建数据库wordpress 3.2、导入表结构和数据 3.3、启动httpd 今天&#xff0c;贫僧的个人博客…

50个顶级开源营销应用程序

显然&#xff0c;开源营销应用程序占有一席之地。如今&#xff0c;营销部门负责相当大比例的企业应用程序购买和部署决策。事实上&#xff0c;Gartner预测&#xff0c;到2017年&#xff0c;首席营销官&#xff08;CMO&#xff09;将比首席信息官&#xff08;CIO&#xff09;在I…

C++乘法的int越界问题

在做Leetcode 69. x 的平方根&#xff0c;出现了两种int越界的情况&#xff1a; 整数相乘越界 if(mid * mid < x)这样写的话&#xff0c;mid * mid可能越界&#xff0c;因此可以改为 if(mid < x/mid)整数本身越界 int n_r x;Leetcode给了个例子&#xff0c;x 21474…

C# DataSet与DataTable的区别和用法

DataSet是数据集&#xff0c;DataTable是数据表&#xff0c;DataSet存储多个DataTable。DataSet和DataTable像是专门存储数据的一个容器&#xff0c;在你查询数据库得到一些结果时可以存在里面。 DataSet功能强大有浏览、排序、搜索、过滤、处理分级数据、缓存更改等功能&#…

Java代码块总结(速读版)

**代码块必须直接定义在类中**&#xff0c;有两种&#xff1a;静态代码块 和 非静态代码块 静态代码块是在类&#xff08;class文件&#xff09;加载内存时执行&#xff0c;而非静态代码块是在创建对象&#xff08;new 类名();&#xff09;时执行。public class Test{static{ …

matplotlib01-plot折线图、scatter散点图

目录 一、plot绘制折线图一般用法 二、scatter绘制散点图一般用法 记录一下数据可视化的python库matplotlib&#xff0c;研究并纪录一下。 matplotlib.pyplot.subplots函数。subplots可以绘制一个或多个图表。返回变量fig表示整张图片&#xff0c;变量ax表示图片中的各个图表…

C++const关键字作用

修饰普通变量&#xff0c;表示不可修改&#xff08;在定义的时候必须初始化&#xff09; #include <iostream> using namespace std; const int a1 10; int main() {const int a3; // 错误&#xff0c;没有初始化const int a2 10;a1 10; // 错误&#xff0c;不可修…

系统设计规范化攻克了什么问题

系统设计规范化攻克了什么问题(一) 大家好,今天总结下我从事开发工作这几年里,对于项目规范化的一点想法和感触. 在笔者心里,规范是为了解决这个问题而存在的,某些规范都是为了相应问题而存在的.所以仅仅要是能解决这个问题的文档,声明都是规范. 在某时開始设计系统,带人做系统…

五分钟让你了解 Java方法(或者叫函数)

Java方法&#xff08;函数&#xff09;的形式如下&#xff1a; 访问权限 [修饰符] 返回值类型 方法名([参数列表]){方法体 } 访问权限&#xff1a;控制方法(函数)能否在其他“类”中使用有public(可以在其他类中使用)&#xff0c;protected&#xff0c;private(只能在该类中使用…

Bootstrap笔记

Bootstrap使用&#xff1a; 下载Bootstrap&#xff0c;用于生产环境的 Bootstrap不需要安装解压文件到任意目录创建index.html&#xff0c;在该页面编写代码页面起始位置添加<link rel"stylesheet" href"解压文件中css/bootstrap.min.css的路径"/>转…

Apache2.4.x下proxy_module、proxy_fcgi_module结合PHP-FPM解决内存不足问题

目录 一、背景 二、问题 三、解决方案 一、背景 最近对贫僧的个人页https://stackoperator.top进行升级&#xff0c;将Apache升级到2.4.46&#xff0c;PHP升级到7.4.15。升级后没啥问题&#xff0c;但是第二天却宕机了。经过查证是内存不足&#xff0c;导致mysql服务宕机了…

两分钟让你知道什么是“Java重载”

重载&#xff1a;顾名思义&#xff0c;就是重复负载的意思&#xff0c;具体到Java 就是方法名相同&#xff08;重复使用&#xff09;&#xff0c;但参数列表不同。 我们通过几个Java方法来具体说明&#xff1a;public class Addition{public int add(int a, int b){return ab;}…

C++ static

&#xff08;1&#xff09;static表示静态变量。局部变量存储在栈区&#xff0c;在{}结束时被释放。而static修饰的变量存储在全局区&#xff0c;在程序结束时被释放。 &#xff08;2&#xff09;static修饰的变量在外部文件中无法调用&#xff0c;即使使用extern也无法调用。其…

TCO 2015 1A Hard.Revmatching(Hall定理)

\(Description\) 给定一个\(n\)个点的二分图&#xff0c;每条边有边权。求一个边权最小的边集&#xff0c;使得删除该边集后不存在完备匹配。\(n\leq20\)。 \(Solution\) 设点集为\(S\)&#xff0c;与\(S\)中的点相邻的点的并集为\(N(S)\)。 由Hall定理&#xff0c;若存在点集\…

20169211 2016-2017-2 《移动平台开发实践》 第十周实验总结

实验一&#xff1a;简易计算器 实验要求 实现一个简易计算器Calc,支持 - * / 和%运算, 从命令行传入计算数据&#xff0c;比如&#xff1a;java Calc 2 3 结果为 2 3 5 java Calc 8 - 3 结果为 8 - 3 5 java Calc 2 * 3 结果为2 * 3 6 java Calc 10 / 2 结…

wordpress从apache迁移到nginx

目录 一、安装nginx 二、配置文件准备 2.1、进程运行用户 2.2、虚拟主机 2.3、重定向 三、迁移 庚子鼠年最后几天&#xff0c;贫僧发现了内存不足的问题&#xff0c;并在Apache2.4.x下proxy_module、proxy_fcgi_module结合PHP-FPM解决内存不足问题一文中阐述了解决方案。…

zabbix4.0搭建(基于CentOS6.8)

环境服务端&#xff1a;188.188.3.241&#xff0c;系统&#xff1a;centos6.8&#xff0c;mysql&#xff1a;5.7.3&#xff0c;php&#xff1a;5.4.9&#xff0c;nginx&#xff1a;1.12.0一、nginx编译安装NGINX_VERSION1.12.0yum -y install pcre-devel openssl-develcd /usr/…

[Ahoi2008]Meet 紧急集合

1787: [Ahoi2008]Meet 紧急集合 Time Limit: 20 Sec Memory Limit: 162 MBhttp://www.lydsy.com/JudgeOnline/problem.php?id1787Description Input Output Sample Input 6 4 1 2 2 3 2 4 4 5 5 6 4 5 6 6 3 1 2 4 4 6 6 6 Sample Output 5 2 2 5 4 1 6 0 HINT S…

C++ 回调函数

函数指针 函数指针是一种指针&#xff0c;具体来说是&#xff1a;指向函数入口地址的指针。 #include <iostream> using namespace std; double function_t(int val) {return val; } int main() {double (*ptr)(int); // 创建一个函数指针&#xff0c;返回值为doubl…

想知道什么是“成员变量”吗?

成员变量是直接定义在“类”中的量&#xff1b; 特点&#xff1a;成员变量有默认值,具体请看表格 数据类型默认值整型0浮点型0.0char’ ’booleanfalse其他类型null 成员变量的作用就是可以详细描述对象信息 我们来举个例子&#xff1a; public class UserInfo{int age;doubl…

Linux09-网络配置

目录 一、网络配置基础 1.1、网络接口 1.2、设置主机名 二、nmcli配置网络 2.1、配置固定的IP地址等 2.2、连接wifi 三、链路聚合等 一、网络配置基础 1.1、网络接口 先来对比一下RHEL6、RHEL7关于网络接口上的一些差别。 RHEL6 RHEL7 配置文件位置 /etc/sysconfig…

VScode配置ROS环境

创建一个文件夹 使用catkin_make编译工作空间的根目录 使用VScode打开 VScode 中编译 ros 快捷键 ctrl shift B 调用编译&#xff0c;选择:catkin_make:build 可以点击配置&#xff08;右边的小齿轮&#xff09;&#xff0c;修改.vscode/tasks.json 文件 { // 有关 tasks.j…

从Excel中导入数据时,提示“未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序”的解决办法...

注意&#xff0c;64位系统&#xff0c;用64位的补丁文件; https://www.cnblogs.com/A2008A/articles/2438962.html 操作系统&#xff1a;使用的是64位的Windows Server 2008 解决办法&#xff1a; 这是由于该计算机上没有安装Microsoft Access数据库引擎组件&#xff0c;该组件…

天兔(Lepus)监控系统慢查询分析平台安装配置

转http://suifu.blog.51cto.com/9167728/1770672 被监控端要安装pt工具 1234[rootHE1~]## yum -y install perl-IO-Socket-SSL[rootHE1~]## yum -y install perl-DBI[rootHE1~]## yum -y install perl-DBD-MySQL[rootHE1~]## yum -y install perl-Time-HiRes[rootHE1~]# tar xv…

五分钟让你搞懂什么是“构造方法”

构造方法的形式&#xff1a;类名([参数列表]){} 特点&#xff1a;- 构造方法没有返回值&#xff0c;就算void也不能有&#xff0c;这点与Java方法(或叫函数)不一样&#xff1b;- 一个类中默认无参构造方法&#xff0c;但是当定义了一个有参构造方法时&#xff0c;则默认无参构造…

Linux10-归档、系统间复制文件

目录 一、tar命令 二、scp、sftp命令 三、rsync命令 一、tar命令 tar命令可以归档文件、目录&#xff0c;提取创建的归档文件&#xff0c;同时进行压缩解压缩。使用tar选项时不需要加-&#xff0c;下面是常用的tar选项。 c&#xff0c;创建一个存档。x&#xff0c;提取一个…

pattern

常用的正则表达式 0-9 pattern"[0-9]*" 信用卡 [0-9]{13,16} 银联卡 ^62[0-5]\d{13,16}$ Visa: ^4[0-9]{12}(?:[0-9]{3})?$ 万事达&#xff1a;^5[1-5][0-9]{14}$ QQ号码&#xff1a; [1-9][0-9]{4,14} 手机号码&#xff1a;^(13[0-9]|14[5|7]|15[0|1|2|3|5…