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时,可以用一个大的view去管理1个或者多个小view,控制器也是如…

值传递 和 地址传递 的区别(好多句话才可以说清)
要知道什么是**值传递**和**地址传递**,首先要知道什么是**基本数据类型**和**引用类型**。 Java的基本数据类型就八种(除了这八种之外都是引用类型):◆整型:byte(8位) short(16位) int(32位) long(64位)࿱…

日期类的时间从为什么是从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系统,而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 今天,贫僧的个人博客…

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

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

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

Java代码块总结(速读版)
**代码块必须直接定义在类中**,有两种:静态代码块 和 非静态代码块 静态代码块是在类(class文件)加载内存时执行,而非静态代码块是在创建对象(new 类名();)时执行。public class Test{static{ …

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

C++const关键字作用
修饰普通变量,表示不可修改(在定义的时候必须初始化) #include <iostream> using namespace std; const int a1 10; int main() {const int a3; // 错误,没有初始化const int a2 10;a1 10; // 错误,不可修…

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

五分钟让你了解 Java方法(或者叫函数)
Java方法(函数)的形式如下: 访问权限 [修饰符] 返回值类型 方法名([参数列表]){方法体 } 访问权限:控制方法(函数)能否在其他“类”中使用有public(可以在其他类中使用),protected,private(只能在该类中使用…

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

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

两分钟让你知道什么是“Java重载”
重载:顾名思义,就是重复负载的意思,具体到Java 就是方法名相同(重复使用),但参数列表不同。 我们通过几个Java方法来具体说明:public class Addition{public int add(int a, int b){return ab;}…

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

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

20169211 2016-2017-2 《移动平台开发实践》 第十周实验总结
实验一:简易计算器 实验要求 实现一个简易计算器Calc,支持 - * / 和%运算, 从命令行传入计算数据,比如: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、重定向 三、迁移 庚子鼠年最后几天,贫僧发现了内存不足的问题,并在Apache2.4.x下proxy_module、proxy_fcgi_module结合PHP-FPM解决内存不足问题一文中阐述了解决方案。…

zabbix4.0搭建(基于CentOS6.8)
环境服务端:188.188.3.241,系统:centos6.8,mysql:5.7.3,php:5.4.9,nginx: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++ 回调函数
函数指针 函数指针是一种指针,具体来说是:指向函数入口地址的指针。 #include <iostream> using namespace std; double function_t(int val) {return val; } int main() {double (*ptr)(int); // 创建一个函数指针,返回值为doubl…

想知道什么是“成员变量”吗?
成员变量是直接定义在“类”中的量; 特点:成员变量有默认值,具体请看表格 数据类型默认值整型0浮点型0.0char’ ’booleanfalse其他类型null 成员变量的作用就是可以详细描述对象信息 我们来举个例子: 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 调用编译,选择:catkin_make:build 可以点击配置(右边的小齿轮),修改.vscode/tasks.json 文件 { // 有关 tasks.j…

从Excel中导入数据时,提示“未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序”的解决办法...
注意,64位系统,用64位的补丁文件; https://www.cnblogs.com/A2008A/articles/2438962.html 操作系统:使用的是64位的Windows Server 2008 解决办法: 这是由于该计算机上没有安装Microsoft Access数据库引擎组件,该组件…

天兔(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…

五分钟让你搞懂什么是“构造方法”
构造方法的形式:类名([参数列表]){} 特点:- 构造方法没有返回值,就算void也不能有,这点与Java方法(或叫函数)不一样;- 一个类中默认无参构造方法,但是当定义了一个有参构造方法时,则默认无参构造…

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

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