三角测量,通俗一点讲就是通过在一个相机在两个不同的位置(或者双目相机)对一个目标点进行观测,依靠两个观测系间的绝对(或相对)位姿变换关系,算出该被观测点在两个观测系中的绝对(或相对)深度。进一步推广,当我们在n个不同的位置对同一个点进行观测,并已知这些观测系相对于一固定系的变换时,便可通过求解超定方程组,求出在该固定系下被观测点坐标的最优解。

构建超定方程组

设待观测点P在世界系的坐标为 P ( X , Y , Z ) P(X,Y,Z) P(X,Y,Z),我们对它进行多次观测。在产生第n次观测时,相机系与世界系之间的变换表示为 T c w _ n = { R , t } T_{cw\_n}=\{R,t\} Tcw_n​={R,t}(后面简写作 T n T_{n} Tn​),这个观测表示为该相机系下的其次坐标 p n ( x , y , 1 ) p_{n}(x,y,1) pn​(x,y,1)。此时我们可以得到 T n P ‾ = λ n ( x y 1 ) T_{n} \overline{P} =λ_{n} \left( \begin{array}{c} x \\ y \\ 1 \end{array} \right) Tn​P=λn​⎝⎛​xy1​⎠⎞​其中 λ n λ_{n} λn​是在当前相机系被观测点的深度, P ‾ \overline{P} P是P的齐次坐标。在此基础上,我们可以非常容易得出
T n 1 P ‾ − x T n 3 P ‾ = 0 T_{n1}\overline{P}-xT_{n3}\overline{P}=0 Tn1​P−xTn3​P=0 T n 2 P ‾ − y T n 3 P ‾ = 0 T_{n2}\overline{P}-yT_{n3}\overline{P}=0 Tn2​P−yTn3​P=0其中 T n i T_{ni} Tni​代表T矩阵的第i行。
将待求变量分离出来,改写为 ( T n 1 − x T n 3 T n 2 − y T n 3 ) P ‾ = 0 \left( \begin{array}{c} T_{n1}-xT_{n3} \\ T_{n2}-yT_{n3} \end{array} \right) \overline{P}=0 (Tn1​−xTn3​Tn2​−yTn3​​)P=0括号内是一个2x4的矩阵,当我们有了多个观测时,便可以构造出一个2n*4的方程组。

解的分析

下面我们来分析一下我们能够得到什么样的解,首先我们从几何角度上看:当我们若只有一个观测时,因为深度未知,所以P可能出现在沿当前观测系下深度方向上的任意一点;当有两个或两个以上不同位置的观测(这决定了方程组有效方程的个数)时,该点在给定系下的绝对位置便可以确定了,但由于观测误差的存在,我们这里一般通过两个以上的观测求得一个最优解。
从齐次方程组的性质上分析:对于Ax=0(其中A是m*n的矩阵),我们通常先构造 A T A x = 0 A^{T}Ax=0 ATAx=0。若 d e t ( A T A ) ≠ 0 det\left(A^{T}A\right)\not=0 det(ATA)​=0,也就是说矩阵 A T A A^{T}A ATA满秩,此时只有零解;当 d e t ( A T A ) = 0 det\left(A^{T}A\right)=0 det(ATA)=0时,有无数多组线性相关的解。因为待求解的向量空间只有三自由度(齐次坐标最后一维是1),所以我们只取一组即可,通常求取 ∣ x ∣ = 1 \left|x\right|=1 ∣x∣=1的解再进行归一化。
接下来我们谈一谈这两个角度的一些联系,但在往下继续讲之前,我们先再简单说一下零空间的问题:矩阵 A T A A^{T}A ATA的零空间,对应了方程 A T A x = 0 A^{T}Ax=0 ATAx=0的所有解的集合。因为前文已经提到了,齐次坐标的自由度是3,所以首先, A T A A^{T}A ATA的零空间维数至少为1,此时方程不会只有零解。为什么说至少为1呢? 此时对应我们前面在几何角度的分析,如果我们只拥有一个观测的话,那么在空间中便无法有多条射线形成一个交点,在其对应观测系下的深度我们是可以任意取值的。这就造成了系统状态空间不可观的维度(即零空间的维度)又加了1,唯一解退化成了无数个线性相关(与观测系下深度线性相关)的解。那么对于两个及以上的观测,零空间为1,求解齐次坐标可通过对 A T A A^{T}A ATA进行SVD,找出最小的那个奇异值(往往特别特别小和次小相比可忽略不计),所对应的特征向量便是我们所需要的解。

#include <iostream>
#include <vector>
#include <random>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <Eigen/Eigenvalues>
#include <fstream>struct Pose
{Pose(Eigen::Matrix3d R, Eigen::Vector3d t):Rwc(R),qwc(R),twc(t) {};Eigen::Matrix3d Rwc;Eigen::Quaterniond qwc;Eigen::Vector3d twc;Eigen::Vector2d uv;
};
int main()
{int poseNums = 10;double radius = 8;double fx = 1.;double fy = 1.;std::vector<Pose> camera_pose;for(int n = 0; n < poseNums; ++n ) {double theta = n * 2 * M_PI / ( poseNums * 4); // 绕 z轴 旋转Eigen::Matrix3d R;R = Eigen::AngleAxisd(theta, Eigen::Vector3d::UnitZ());Eigen::Vector3d t = Eigen::Vector3d(radius * cos(theta) - radius, radius * sin(theta), 1 * sin(2 * theta));camera_pose.push_back(Pose(R,t));}// 随机数生成 1 个 三维特征点std::default_random_engine generator;std::uniform_real_distribution<double> xy_rand(-4, 4.0);std::uniform_real_distribution<double> z_rand(8., 10.);double tx = xy_rand(generator);double ty = xy_rand(generator);double tz = z_rand(generator);std::cout << tx << " " << ty << " " << tz << std::endl;std::default_random_engine noise_generator;double variance;variance = 0.0;std::normal_distribution<double> noise_pdf(0, variance);double noise;Eigen::Vector3d Pw(tx, ty, tz);std::cout << "generate 3D point finished" << std::endl;int start_frame_id = 0;int end_frame_id = 1;for (int i = start_frame_id; i < end_frame_id; ++i) {Eigen::Matrix3d Rcw = camera_pose[i].Rwc.transpose();Eigen::Vector3d Pc = Rcw * (Pw - camera_pose[i].twc);noise = noise_pdf(noise_generator);std::cout << noise << std::endl;double x = Pc.x() + noise;noise = noise_pdf(noise_generator);std::cout << noise << std::endl;double y = Pc.y() + noise;noise = noise_pdf(noise_generator);std::cout << noise << std::endl << std::endl;double z = Pc.z() + noise;camera_pose[i].uv = Eigen::Vector2d(x/z,y/z);}std::cout << "measurement generated" << std::endl;Eigen::Vector3d P_est;         int D_row = 2 * (end_frame_id - start_frame_id);Eigen::MatrixXd D;D.resize(D_row, 4);for(int i = 0; i < D_row/2; i++){//对应camera_pose[start_frame+i]Eigen::MatrixXd T(3,4);Eigen::Matrix3d rotation = camera_pose[i+start_frame_id].Rwc.transpose();Eigen::Vector3d trans = -1 * rotation * camera_pose[i+start_frame_id].twc;for(int j = 0; j < 3; j++)T.block(j, 0, 1, 4) << rotation(j,0), rotation(j,1), rotation(j,2), trans(j);std::cout << "T:" << std::endl << T << std::endl;D.block(i*2, 0, 1, 4) = camera_pose[i+start_frame_id].uv(0) * T.block(2, 0, 1, 4) - T.block(0, 0, 1, 4);D.block(i*2+1,0,1, 4) = camera_pose[i+start_frame_id].uv(1) * T.block(2, 0, 1, 4) - T.block(1, 0, 1, 4);}std::cout << "D:" << std::endl << D << std::endl;Eigen::MatrixXd square(4,4);square = D.transpose() * D;Eigen::JacobiSVD<Eigen::MatrixXd> svd(square, Eigen::ComputeThinU | Eigen::ComputeThinV);std::cout << "Singular values: " << std::endl << svd.singularValues() << std::endl;std::cout << "matrix U: " << std::endl << svd.matrixU() << std::endl;std::cout << "matrix v: " << std::endl << svd.matrixV() << std::endl;std::cout <<"ground truth: \n"<< Pw.transpose() <<std::endl;double ddd = svd.matrixU()(1,3);P_est << svd.matrixU()(0,3)/svd.matrixU()(3,3), svd.matrixU()(1,3)/svd.matrixU()(3,3), svd.matrixU()(2,3)/svd.matrixU()(3,3); std::cout <<"my result: \n"<< P_est.transpose() <<std::endl;return 0;
}

以上代码提供了一个简单的三角化例程,在生成了一个三维点并只用一个相机对他进行观测。可以发现四个奇异值两个都极小,接近于零,这也印证了我们上文的分析,这种状况下待求解的变量空间的不可观维数是2。

(如有需要转发,请注明出处~)

三角测量的一些基础理论相关推荐

  1. Lambda 表达式基础理论与示例

    Lambda 表达式基础理论与示例 Lambda 表达式,也可称为闭包,推动 Java 8 发布的最重要新特性. Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中). 使用 Lam ...

  2. 三角测量计算三维坐标的代码_浅谈三维扫描仪的由来

    随着人类的发展,每一款新型产品的由来都是工业革命的产物,人们发明的任何一个产品都是要服务于人类并创造出更多的价值:其中三维扫描仪的出现也是为了满足于人类的需要而产生的,在传统的测量中接触式测量是出现最 ...

  3. ORB-SLAM2从理论到代码实现(四):相机成像原理、基本矩阵、本质矩阵、单应矩阵、三角测量详解

    由于ORBmatcher.cc中有三角化和重投影等内容,所有我先写相机成像等多视图几何内容. 1. 相机的成像原理 假设空间中有一点P,它在世界坐标系中的坐标为,在相机坐标系中的坐标为,在图片中的像素 ...

  4. 一文详解非线性优化算法:保姆级教程-基础理论

    不论是刚入门SLAM的小白,还是导航相关专业的同学,都对"非线性优化"这个词不陌生,如果你说你没听过这个词,那"因子图"一词总该略有耳闻吧,如果还是不知道,那就 ...

  5. 发展第三代AI:清华AI研究院基础理论研究中心成立,朱军任主任

    https://www.toutiao.com/a6687778128141484552/ 人工智能正处在高速发展时期,而清华的研究人员却早已意识到了目前方法的局限,并放眼于下一代技术上了.5 月 6 ...

  6. 宇宙是一个图网络?「全球最聪明的人」刚刚为物理基础理论指出了全新道路...

    来源:机器学习研究组订阅号 图片来源:机器之心 「物理学已经很长一段时间没有出现任何显著进展了.探测引力波或许算是一个,」斯蒂芬·沃尔夫勒姆表示.「我非常希望在纯技术层面上,我们所做的一切能够使理论物 ...

  7. 基础理论研究是人工智能持续发展的保证

    来源:图灵人工智能 摘要: 人工智能的主流技术的发展大致经历了三个重要的历程. 人工智能的主流技术的发展大致经历了三个重要的历程.1956-1965年,人工智能的形成期,强调推理的作用.一般认为只要机 ...

  8. 展望:共融机器人的基础理论与关键技术

    来源:<国家科学评论> 概要:自1959年工业机器人诞生以来,机器人在机械制造.国防安全.健康服务.科考与医疗等方面发挥出越来越重要的作用. 自1959年工业机器人诞生以来,机器人在机械制 ...

  9. Lesson 6.5Lesson 6.6.1Lesson 6.6.2 机器学习调参基础理论与网格搜索多分类评估指标的macro与weighted过程GridSearchCV的进阶使用方法

    Lesson 6.5 机器学习调参基础理论与网格搜索 在上一小节执行完手动调参之后,接下来我们重点讨论关于机器学习调参的理论基础,并且介绍sklearn中调参的核心工具--GridSearchCV. ...

最新文章

  1. kickstart中ks.cfg指定目标机ip的小备忘
  2. 面向对象的三大特征之多态(第三个必要条件)
  3. 如何降低遮挡对人脸识别的影响
  4. 政策表达式截取json_json格式数据如何提取指定中文字符串。
  5. python爬虫基本知识_爬虫 (十三) 学习 python 基础知识点的正确姿势 (六)
  6. IOS-C语言第8天,Struct (结构体)
  7. mysql可连接_mysql开启远程可连接
  8. C语言里的几个拷贝函数memcpy、memset、strcpy、strncpy
  9. mysql浮点数据怎么_MySQL数据浮点类型的实际应用操作
  10. HDU2504 又见GCD
  11. 安装了一下WinZip,感觉很难用
  12. 2016 计算机控制技术试题,计算机控制技术试题
  13. 2018年计算机网络考研真题
  14. 路由器DNS根域名解析失败
  15. c语言单片机程序int,单片机睡眠-外中断INT0 INT1唤醒(汇编+C语言程序)
  16. Spring学习(二)IOC
  17. python求一个数所有因数
  18. 反函数法生成服从特定概率密度函数的随机数
  19. 苹果cms模板_苹果CMS建站的一些心得
  20. VC++深入详解学习笔记

热门文章

  1. 高响应比优先算法实现进程调度模拟
  2. 监控摄像头的测试方法
  3. DBA的职业发展机会
  4. 01_Go语言基础学习_Golang语言特性、环境搭建、第一个Go程序、包
  5. 雨听 | 英语学习笔记(十一)~作文范文:公园的免费入口
  6. 扣扣浏览器mini java_WebQQ Mini各种浏览器试用
  7. 《人性的弱点》良句收录和读后感想
  8. linux使用usermod修改用户主目录
  9. 分布式消息中间件 MetaQ 作者庄晓丹专访
  10. 【IP协议(一)】——IP数据报格式及其含义,IP数据报的切分