【深蓝学院】手写VIO第6章--视觉前端--作业(SVD分解部分复习)

news2025/1/11 11:06:48

0. 题目

在这里插入图片描述

T1.

奇异值分解需要补,看这篇博客,讲的很好。
总结一下,关于奇异值分解(Singular Value Decomposition,SVD )有以下内容摘抄自该博客,关于SDV分解的部分应该是摘自李航《统计学习方法里面的》:

1. 特征值分解

设 A 为 n 阶方阵,若存在数 λ 和非零向量 x,使得
A x = λ x ( x ≠ 0 ) Ax=\lambda x(x\neq 0) Ax=λx(x=0)
则称 λ \lambda λ是A的一个特征值。
求出了矩阵 A 的 n 个特征值 λ 1 ≤ λ 2 ≤ λ 3 . . . ≤ λ n \lambda_1 \leq \lambda_2 \leq \lambda_3 ...\leq\lambda_n λ1λ2λ3...λn,这n个特征值对应的特征向量分别为 p 1 , p 2 , p 3 . . . p n p_1,p_2,p_3...p_n p1,p2,p3...pn,如果这n个特征向量线性无关,则矩阵A即可以用下式的特征分解表示
A = P Λ P − 1 A=P\Lambda P^{-1} A=PΛP1 其中 P = { p 1 , p 2 , p 3 . . . p n } P=\{p_1,p_2,p_3...p_n\} P={p1,p2,p3...pn}是这 n 个特征向量张开成的 n*n 矩阵, Λ \Lambda Λ是以这n个特征值为主对角线的n*n维对角阵。
一般我们会把 P P P的 n 个特征向量标准化,此时,这 n 个特征向量为标准正交基,满足 P ∗ P T = I P*P^T=I PPT=I ,即 P T = P − 1 P^T=P^{-1} PT=P1,这样特征分解表达式可以写成: A = P Λ P T A=P\Lambda P^T A=PΛPT
注意,要进行特征分解,矩阵 A 必须为方阵。如果A不是方阵,即行和列不相同时,我们还可以对矩阵进行分解吗?答案是可以,此时我们的SVD登场了。

2. SVD分解

SVD 定义:矩阵的奇异值分解是指,将一个非零的 m × n 实矩阵 A,表示为以下三个实矩阵乘积形式的运算 (SVD 可以更一般地定义在复数矩阵上,但本文不涉及),即进行矩阵的因子分解:
A = U Σ V T A=U\Sigma V^T A=UΣVT
其中 U 是 m 阶正交矩阵,V 是 n 阶正交矩阵,Σ 是由降序排列的非负的对角线元素组成的 m × n 矩形对角矩阵。满足:
在这里插入图片描述
A = U Σ V T A=U\Sigma V^T A=UΣVT为矩阵 A A A的SVD分解, σ i \sigma_i σi A A A的奇异值, U U U的列项向量称为 A A A的左奇异向量,V的列向量称为 A A A的右奇异向量。
在这里插入图片描述

奇异值分解不要求矩阵 A 是方阵,事实上矩阵的奇异值分解可以看做是方阵的对角化的推广。

2.1 SVD 基本定理

若 A 为一 m × n 实矩阵 ,则 A 的奇异值分解存在
A = U Σ V T A=U\Sigma V^T A=UΣVT

其中 U 是 m 阶正交矩阵,V 是 n 阶正交矩阵,Σ 是 m × n 矩形对角矩阵,其对角线元素非负,且按降序排列。

这个定理表达的意思就是矩阵的奇异值分解是一定存在的 (但不唯一),这里就不具体证明了。

2.2 主要性质

  1. 设矩阵A的AVD分解为 A = U Σ V T A=U\Sigma V^T A=UΣVT,则以下关系成立:
    A T A = ( U Σ V T ) T ( U Σ V T ) = V ( Σ T Σ ) V T A A T = ( U Σ V T ) ( U Σ V T ) T = U ( Σ T Σ ) U T \begin{align*} & A^TA=(U\Sigma V^T)^T(U\Sigma V^T)=V(\Sigma^T\Sigma)V^T\\ & AA^T=(U\Sigma V^T)(U\Sigma V^T)^T=U(\Sigma^T\Sigma)U^T \end{align*} ATA=(UΣVT)T(UΣVT)=V(ΣTΣ)VTAAT=(UΣVT)(UΣVT)T=U(ΣTΣ)UT
    也就是说,矩阵 A T A A^TA ATA A A T AA^T AAT 的特征分解存在,且可以由矩阵 A A A 的奇异值分解的矩阵表示。 V V V 的列向量是 A T A A^TA ATA 的特征向量, U U U 的列向量是 A A T AA^T AAT 的特征向量, Σ \Sigma Σ 的奇异值是 A T A A^TA ATA A A T AA^T AAT 的特征值的平方根。
  2. 在矩阵 A 的奇异值分解中,奇异值、左奇异向量和右奇异向量之间存在对应关系:
    A V = U Σ A v j = σ j u j , j = 1 , 2 , . . . , n AV=U\Sigma\\ Av_j=\sigma_ju_j, j=1,2,...,n AV=UΣAvj=σjuj,j=1,2,...,n
    类似的,奇异值、右奇异向量和左奇异向量之间存在对应关系:
    A T U = V Σ T A T u j = σ j v j , j = 1 , 2 , . . . , n A T u j = 0 , j = n + 1 , n + 2 , . . . , m A^TU=V\Sigma^T\\ A^Tu_j=\sigma_jv_j, j=1,2,...,n\\ A^Tu_j=0, j=n+1,n+2,...,m ATU=VΣTATuj=σjvj,j=1,2,...,nATuj=0,j=n+1,n+2,...,m
  3. 矩阵 A A A 的奇异值分解中,奇异值 σ 1 , σ 2 , . . . σ n \sigma_1,\sigma_2,... \sigma_n σ1,σ2,...σn是唯一的,而矩阵 U U U V V V 不是唯一的。
  4. 矩阵 A A A Σ \Sigma Σ 的秩相等,等于正奇异值 σ i \sigma_i σi 的个数 r r r (包含重复的奇异值)。

在这里插入图片描述

上述的公式 A T A A^TA ATA与Dan Simon的《Optimal State Estimation Kalman, H∞, and Nonlinear Approaches》P6—P7中的内容(公式(1.16))相结合,非常容易得出课件中的公式(15)。

3. 解题

用上面的知识铺垫可以来解决T1

助教答案
在这里插入图片描述
在这里插入图片描述
同时参考了博客,但是其证明过程感觉有问题,所以主要参考了助教的方法。

方法3的拉格朗日算子方法需要了解拉格朗日对偶,作为拓展:
在这里插入图片描述
以上就是求 m i n ∣ ∣ D y ∣ ∣ 2 2 min ||Dy||_2^2 min∣∣Dy22的最小二乘解的方法,求出y之后就可以求出观测点的深度值,即完成三角化。

T2.

在这里插入图片描述

代码:

typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> MatXX;
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); // 1/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);

    Eigen::Vector3d Pw(tx, ty, tz);
    // 这个特征从第三帧相机开始被观测,i=3
    int start_frame_id = 3;
    int end_frame_id = poseNums;
    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);//实际上是Rp+t,拆开来看就是Rwc^T * Pw +(-Rwc * twc)= Rcw * Pw + tcw

        double x = Pc.x();
        double y = Pc.y();
        double z = Pc.z();

        camera_pose[i].uv = Eigen::Vector2d(x/z,y/z);//因为camera内参为1,1,所以内参可以忽略
    }
    
    /// TODO::homework; 请完成三角化估计深度的代码
    // 遍历所有的观测数据,并三角化
    Eigen::Vector3d P_est;           // 结果保存到这个变量
    P_est.setZero();
    /* your code begin */
    //1.构建D
    int D_size = end_frame_id - start_frame_id;
    MatXX D(MatXX::Zero( 2 * D_size, 4));//D维度为2n*4
    for(int i=start_frame_id; i<end_frame_id; ++i) {
        //构建投影矩阵Pk
        MatXX Pi(MatXX::Zero(3,4));
        Eigen::Matrix3d Rcw = camera_pose[i].Rwc.transpose();
        Pi.block(0,0,3,3) = Rcw;
        Pi.block(0,3,3,1) = -Rcw * camera_pose[i].twc;//tcw,变换矩阵求逆
        cout << "i = " <<i <<",   Pi_block: \n" << Pi <<endl;
        //构建2*4的矩阵快
        MatXX tmp_mat = camera_pose[i].uv * Pi.block(2,0,1,4) - Pi.block(0,0,2,4);
        D.block((i-start_frame_id) * 2, 0, 2, 4) = camera_pose[i].uv * Pi.block(2,0,1,4) - Pi.block(0,0,2,4);
    }
    cout << "the whole D mat, size: " << D.size() << "\nMat is:\n" << D <<endl;
    //2.对D进行rescale
    MatrixXd::Index maxRow, maxCol;
    double max = D.maxCoeff(&maxRow,&maxCol);
//    max = 1;//取消scale
    cout << "max element of D is: " << max <<endl;
    printf("maxRow: %ld, maxCol: %ld\n", maxRow, maxCol);
    D /= max;
    //3.对D^TD进行SVD(参数有ComputeThinU | ComputeThinV 和 ComputeFullU | ComputeFullV 这个位置不传参就代表你只想计算特征值,不关注左右特征向量(UV矩阵),
    // 传参就代表你想计算出左右特征向量,而full就是告诉函数计算出来的UV方阵,也就是Matrix3d,计算出来的就是3*3的方阵,thin只在矩阵维度不知道时使用,即n*p的矩阵D,不知道n和p谁更小,假设m=min(n,p),
    // 那么计算结果: U:n*m, V:p*m, 其所代表的特征向量均不是对应实际的\sigma中的特征值的)
    JacobiSVD<MatrixXd> svd(D.transpose() * D, ComputeThinU | ComputeThinV);//D^T*D 进行SVD分解
    cout << "Its singular values are:\n" << svd.singularValues() << endl;
    cout << "Its left singular vectors are the columns of the thin U matrix:\n" << svd.matrixU() << endl;
    cout << "Its right singular vectors are the columns of the thin V matrix:\n" << svd.matrixV() << endl;
    //4.判断解的有效性(\sigma_4 / \sigma_3 < 1e-2 ?)
    double judge_value = std::abs(svd.singularValues()(3) / svd.singularValues()(2));
    if(judge_value < 1e-2) {
        Eigen::Vector4d u4 = max * svd.matrixU().rightCols(1);
        cout << "this Triangulation is valid, judge_value:" << judge_value << endl << "u4 is: \n" <<  u4 << endl;//最后一列(为什么是U不是V?)
        //5.对triangulation的结果(4维)进行归一化(最后一维变为1)
        P_est = (u4/u4(3)).head(3);
    }
    /* your code end */
    
    std::cout <<"ground truth: \n"<< Pw.transpose() <<std::endl;
    std::cout <<"your result: \n"<< P_est.transpose() <<std::endl;
    // TODO:: 请如课程讲解中提到的判断三角化结果好坏的方式,绘制奇异值比值变化曲线

    return 0;
}

其中对svd的参数进行稍微解释:

  1. 构造参数:MatrixXd: 计算的特征向量的维度(n维方阵就是Matrixnd,n*m非方阵就填MatrixXd,不确定)
  2. 传参:ComputeThinU | ComputeThinV 和 ComputeFullU | ComputeFullV 这个位置不传参就代表你只想计算特征值,不关注左右特征向量(UV矩阵),传参就代表你想计算出左右特征向量,而full就是告诉函数计算出来的UV方阵,也就是Matrixnd,计算出来的就是nn的方阵,thin只在矩阵维度不知道时使用,即np的矩阵D,不知道n和p谁更小,假设m=min(n,p), 那么计算结果: U:nm, V:pm, 其所代表的特征向量均不是对应实际的 Σ \Sigma Σ中的特征值的

参考博客:
在这里插入图片描述
最终的结果,

  1. σ 4 σ 3 < < 1 e − 2 \frac{\sigma_4}{\sigma_3}<<1e-2 σ3σ4<<1e2,满足有效性阈值,所以triangulation有效,
  2. 进而改变max=1取消scale,发现结果仍然正确,说明D的数值偏小(按照课上来说几十万算是大的)时,对SVD的结果没太大影响。
    在这里插入图片描述
    在这里插入图片描述

提升T1.

Ch5作业中TestMonoBA.cpp给的观测添加噪声的方法

    // 随机数生成三维特征点
    std::default_random_engine generator;
    std::normal_distribution<double> noise_pdf(0., 1. / 1000.);  // 2pixel / focal
    for (int j = 0; j < featureNums; ++j) {
        std::uniform_real_distribution<double> xy_rand(-4, 4.0);
        std::uniform_real_distribution<double> z_rand(4., 8.);

        Eigen::Vector3d Pw(xy_rand(generator), xy_rand(generator), z_rand(generator));
        points.push_back(Pw);

        // 在每一帧上的观测量
        for (int i = 0; i < poseNums; ++i) {
            Eigen::Vector3d Pc = cameraPoses[i].Rwc.transpose() * (Pw - cameraPoses[i].twc);
            Pc = Pc / Pc.z();  // 归一化图像平面
            Pc[0] += noise_pdf(generator);
            Pc[1] += noise_pdf(generator);
            cameraPoses[i].featurePerId.insert(make_pair(j, Pc));
        }
    }

如下添加观测noise

    for(int j=1; j<50; ++j) {
        std::normal_distribution<double> noise_pdf(0., (double)j / 1000.);  // 2pixel / focal,修改var可改变噪声大小
        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);//实际上是Rp+t,拆开来看就是Rwc^T * Pw +(-Rwc * twc)= Rcw * Pw + tcw

            double x = Pc.x();
            double y = Pc.y();
            double z = Pc.z();

            camera_pose[i].uv = Eigen::Vector2d(x/z + noise_pdf(generator),y/z + noise_pdf(generator));//因为camera内参为1,1,所以内参可以忽略
        }
    //else balabala
}

Gaussian distribution的参数1 / 1000表示2个pixel对应到归一化平面的误差参数,也是cov

不同参数对应的结果如下:
在这里插入图片描述
曲线如下,使用gnuplot绘制,执行以下指令生成绘制曲线图:

cat muban.conf | gnuplot

在这里插入图片描述
gnuplot配置文件muban.conf

set terminal gif small size 900,780
set output "var_judge.gif" #指定输出gif图片的文件名
set autoscale
#set xdata time
#set timefmt "%s"
#set format x "%S"
set title "sigma_4/sigma_3 under different noise var" #图片标题
set style data lines #显示网格
set xlabel "noise_var" #X轴标题
set ylabel "sigma_4/sigma_3" #Y轴标题
set grid #显示网格
plot "data.txt" using 1:4 title "sigma_4/sigma_3"

随着噪声varince的上升,比值逐渐增大,到5e-2时已经不valid了,证明噪声越大,Triangulation误差越大。

提升T2.

固定noise var为5/1000,改变end_point,从1帧到7帧,随着观测数量的增多,三角化的误差越来越小。
在这里插入图片描述

在这里插入图片描述
本章完。

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

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

相关文章

互联网性能和可用性优化CDN和DNS

当涉及到互联网性能和可用性优化时&#xff0c;DNS&#xff08;Domain Name System&#xff09;和CDN&#xff08;Content Delivery Network&#xff09;是两个至关重要的元素。它们各自发挥着关键作用&#xff0c;以确保用户能够快速、可靠地访问网站和应用程序。在本文中&…

项目管理之常见七大问题挑战

在当今复杂多变的市场环境下&#xff0c;企业为了生存和发展&#xff0c;必须不断应对和解决各种挑战。其中&#xff0c;项目管理作为企业运营及项目交付等的重要组成部分&#xff0c;也面临着七大问题挑战。这些挑战不仅影响着项目的成功实施&#xff0c;也对企业的发展产生着…

[Spring] SpringMVC 简介(一)

目录 一、SpringMVC 简介 1、什么是 MVC 2、什么是 SpringMVC 3、SpringMVC 实现原理 4、SpringMVC 的特点 二、简单案例 1、引入依赖 2、在 web.xml 中配置前端控制器 DispatcherServlet 3、创建 SpringMVC 的配置文件 4、创建请求控制器 5、测试页面 6、访问不到 …

Go 存储系列:B+树存储引擎 boltdb

boltdb 介绍 boltdb是一个纯go编写的支持事务的文件型单机kv数据库 支持事务&#xff1a; boltdb数据库支持两类事务&#xff1a;读写事务、只读事务。这一点就和其他kv数据库有很大区别文件型&#xff1a; boltdb所有的数据都是存储在磁盘上的&#xff0c;所以它属于文件型数…

信号与系统第一章

文章目录 1.1连续信号与离散信号1.1.2信号能量与功率能量讨论无穷区间内功率和能量&#xff1a;无限区间内的平均功率&#xff1a;利用上述定义区分三种重要信号 1.2自变量的变换1.2.1举例基本变换1.2.2周期信号1.2.3偶信号与奇信号 1.3指数信号与正弦信号1.3.1连续时间复指数信…

LeetCode(力扣)416. 分割等和子集Python

LeetCode416. 分割等和子集 题目链接代码 题目链接 https://leetcode.cn/problems/partition-equal-subset-sum/ 代码 class Solution:def canPartition(self, nums: List[int]) -> bool:sum 0dp [0]*10001for num in nums:sum numif sum % 2 1:return Falsetarget …

python之计算市场技术指标

1、MA MA指标是一种常用的技术指标&#xff0c;它是通过计算一定时间内的股价平均值来反映股价趋势的指标。通常&#xff0c;MA指标越平滑&#xff0c;就能更好地反映出股价的长期趋势。 MA指标的作用是帮助投资者识别股票价格的趋势。当股票价格的MA指标向上运动时&#xff…

分类网络的评价指标

之前一直是做目标检测的研究&#xff0c;在目标检测中主要有两个任务&#xff0c;一个是分类回归&#xff0c;一个是位置回归&#xff0c;所用的评价指标有&#xff1a;AP&#xff0c;mAP&#xff0c;Recall&#xff0c;Precision&#xff0c;F1值&#xff0c;前两个用的一般最…

论文学习记录--零样本学习(zero-shot learning)

Socher R, Ganjoo M, Manning C D, et al. Zero-shot learning through cross-modal transfer[J]. Advances in neural information processing systems, 2013, 26. 注&#xff1a;中文为机翻 zero-shot learning&#xff1a;通过学习类别之间的关系和属性&#xff0c;使得模型…

每日leetcode_LCP01猜数字

每日leetcode_LCP01猜数字 记录自己的成长&#xff0c;加油。 题目出处&#xff1a;LCP 01. 猜数字 - 力扣&#xff08;LeetCode&#xff09; 题目 解题 class Solution {public int game(int[] guess, int[] answer) {int count 0;for (int i 0 ; i< guess.length; i){…

修正两个shapefile之间的数字化错误

目录 一、数据输入 二、测量拓扑容差 三、启用编辑 四、使用拓扑编辑工具 五、建立图层拓扑关系 六、待修改的图层设置为唯一可选图层 七、编辑折点 八、拖动折点至land图层折点 一、数据输入 二、测量拓扑容差 拓扑容差约为4~5m 三、启用编辑 四、使用拓扑编辑工具 五…

系统移植--前言

移植 不同架构的处理器指令集不兼容&#xff0c;即便是相同的处理器架构&#xff0c;板卡不同驱动代码也不兼容 Linux是一个通用的内核并不是为某一个特定的处理器架构或板卡设计的&#xff0c;所以从官方获取Linux源码后我们要先经过相应的配置使其与我们当前的硬件平台相匹配…

Java数据结构第十七章、手撕位图

给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中。【腾讯】 1. 遍历&#xff0c;时间复杂度O(N) 2. 排序(O(NlogN))&#xff0c;利用二分查找: logN 3. 位图解决 数据是否在给定的整形数据中&#xff0c;结…

重置Mac电脑的SMC怎么操作,重置SMC方法分享~

SMC 负责管理 Mac 上的电源。重置 SMC 可以解决一些与电源或散热管理相关的不常见问题。今天重置SMC教程给大家分享一下&#xff0c;需要的小伙伴看过来&#xff01; 如何判断您是不是需要重置 SMC 若出现以下症状&#xff0c;则表明可能需要重置 SMC&#xff1a; 电池无法充电…

Exim 中的漏洞!

我们将继续分享有关流行网络安全漏洞的最新信息。 Exim 中的 CVE-2023-42115漏洞&#xff01; CVE-2023-42115是开源 Exim 4 SMTP 邮件服务器项目中的一个远程代码执行漏洞。问题的严重性不容小觑。据我们的分析师称&#xff0c;目前网络中存在漏洞的 Exim 服务器超过 350 万台…

18基于matlab的二阶动态系统的滑膜控制,程序已调通,可直接运行。标价为程序价格,不包含售后。程序保证可直接运行。

基于matlab的二阶动态系统的滑膜控制&#xff0c;程序已调通&#xff0c;可直接运行。标价为程序价格&#xff0c;不包含售后。程序保证可直接运行。 18matlab滑膜控制 (xiaohongshu.com)

【C++入门】命名空间详解(从零开始,冲击蓝桥杯)

C入门 命名空间 南喵小鸡汤程序员可以让步&#xff0c;却不可以退缩&#xff0c;可以羞涩&#xff0c;却不可以软弱&#xff0c;总之&#xff0c;程序员必须是勇敢的。一 . 命名空间的介绍二.命名空间的实际应用1.为什么要有命名空间我们在使用变量时,通常会为他定义一个名字,在…

graphviz 绘制森林

代码 digraph Forest {node [fontname"Arial", shapecircle, stylefilled, color"#ffffff", fillcolor"#0077be", fontsize12, width0.5, height0.5];edge [fontname"Arial", fontsize10, color"#333333", arrowsize0.5];/…

Node基础概念,先了解一下

Nodejs是基于Chrome V8引擎的服务器端JavaScript运行环境&#xff0c;也就是说可以在浏览器之外的主机上运行JavaScript。 NodeJS Nodejs有三部分组成&#xff1a;标准库、中间层和底层库。 标准库&#xff1a;是给开发人员直接调用的API&#xff0c;比如HTTP模块&#xff1b…

Vulnhub Me-and-My-Girlfriend

一、信息收集 1.nmap 端口扫描 发现开发了22、80 2.访问ip&#xff0c;右击查看源代码 发现需要利用X-Forwarded-For 插件&#xff1a;X-Forwarded-For Header 挂上代理后&#xff1a; 出现以下页面&#xff1a; 先注册一个账户&#xff0c;然后再登录 发现有参数进行传…