从零开始手写VIO第六章作业(含公式推导依据及编程思路)

前言·与同主题博文的不同

延续该系列博文的风格,本博文站在前人优秀博文的基础上,尽量补充同主题博文所没有涉及到的关键点。之前同主题优秀博文已经讲解非常清楚的部分,本博文中将直接给出链接,对于之前博文没有涉及到的点,本博文会重点阐述。欢迎有兴趣的朋友讨论交流。
对于第六章,所补充的关键点包含以下几个方面:第一题给出了公式推导中应用到的一些定理和矩阵运算技巧,并说明了课件中的公式(15)是如何得到的,第二题结合课件中的理论给出了编程思路,第三题参考第五章代码给出了噪声参数设置的标准。

1.证明式(15)

题目如下图:在这里插入图片描述
公式(15)如下图:
在这里插入图片描述

1.1对公式(15)的说明

小编看了同主题的优秀博文,虽然给出了SVD的详细介绍,但是对于如何由SVD定义得到公式(15)却没有任何提及。
为了便于说明这里先给出李航的《统计学习方法第2版》P271—P272中引出的SVD:
在这里插入图片描述
下面小编结合李航的《统计学习方法第2版》P280—P281中的内容来说明公式(15)是如何得到的。该书中的矩阵A也就是课件中的矩阵D。课件中的公式(15)最原始的表达式是下图中的公式(15.20),容易知道其中ΣTΣ是σi2所构成的对角线矩阵。
在这里插入图片描述
上述的公式(15.20)与Dan Simon的《Optimal State Estimation Kalman, H∞, and Nonlinear Approaches》P6—P7中的内容(公式(1.16))相结合,非常容易得出课件中的公式(15)。
在这里插入图片描述
另外,下面1.2中的推导过程也用到了上述李航的《统计学习方法第2版》P280—P281公式(15.22),即矩阵 A 的右奇异向量和奇异值、左奇异向量的关系。

1.2推导过程

请结合1.1中的内容参考博文从零手写VIO(六)中的相关内容。

2.请依据本节课公式,完成特征点三角化代码,并通过仿真测试

2.1理论依据

先给出课件中对应的理论内容,然后整理为对应的编程思路。
课件中对应的理论内容在P26—P29,如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从后往前整理分析,最后求解的目标是P29中的y,所以需要对DTD做SVD分解(公式15),那么首先需要构造出D。公式(13)给出了矩阵D的具体表达式,最后结合公式(10-12)和P26中的具体内容可知,需要知道观测量和投影矩阵就可以求出D。
需要注意的是,这里没有涉及P29中的Rescale和y投影正负号判断。

2.2代码及仿真结果

下面是原代码中相机pose的结构体:

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;    // 这帧图像观测到的特征坐标
};

这里已知的是Rwc和twc,而2.1中所用的的pose是Rcw和tcw,所以还需要做一步转换,转换公式参考《视觉SLAM十四讲》P46公式(3-13),如下图:
在这里插入图片描述
具体代码如下:

    /* your code begin */
    Eigen::Matrix<double, Eigen::Dynamic, 4> D(2 * (poseNums - start_frame_id), 4);
    Eigen::RowVector4d P_1 = Eigen::RowVector4d::Zero(4);
    Eigen::RowVector4d P_2 = Eigen::RowVector4d::Zero(4);
    Eigen::RowVector4d P_3 = Eigen::RowVector4d::Zero(4);
    int k=0;
    for (int i = start_frame_id; i < end_frame_id; ++i) { 
	Eigen::Matrix3d Rcw = camera_pose[i].Rwc.transpose();
	Eigen::Vector3d tcw = -Rcw*camera_pose[i].twc;
	P_1 << Rcw.block<1, 3>(0, 0), tcw.x();
	P_2 << Rcw.block<1, 3>(1, 0), tcw.y();
	P_3 << Rcw.block<1, 3>(2, 0), tcw.z();
	D.block<1, 4>(k, 0) = camera_pose[i].uv.x()*P_3-P_1;
	D.block<1, 4>(k+1, 0) = camera_pose[i].uv.y()*P_3-P_2;
	k+=2;
    }
    Eigen::MatrixXd DTD = D.transpose() * D;
    Eigen::JacobiSVD<Eigen::MatrixXd> svd(DTD, Eigen::ComputeThinU | Eigen::ComputeThinV);
    Eigen::MatrixXd U = svd.matrixU();
    P_est = U.block<3, 1>(0, 3) / U(3, 3);//取齐次坐标的前三维,并同时除以第四维坐标
    Eigen::Vector4d Singular_values=svd.singularValues();
    std::cout<<"Singular values:\n"<<Singular_values<<std::endl;
    std::cout<<"sigma4/sigma3:\n"<<Singular_values[3]/Singular_values[2]<<std::endl;
    /* your code end */

另外,关于函数.block的说明如下:

.block(i, j, p, q)    //起点(i, j),块大小(p, q),构建一个动态尺寸的block
.block<p, q>(i, j)  // 构建一个固定尺寸的block

仿真结果如下图:
在这里插入图片描述
可以看出,σ4/σ3(最小奇异值和第二小奇异值之间的比例)小于10-4,三角化有效,在不加任何噪声的情况下三角化求得的空间点 P_est 与原空间点 Pw 完全相同。

3.请对测量值加上不同噪声(增大测量噪声方差),观察最小奇异值和第二小奇异值之间的比例变化,并绘制比例值的变化曲线

3.1噪声参数设置

首先需要做的事情是选择合理的噪声参数,下面是第五章中老师所给代码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));
    }
}

关于第二行,小编的理解如下,1. / 1000.表示2个像素误差对应到归一化平面的误差参数。小编以此作为噪声参数设定的标准。

3.2代码及仿真结果

参考3.1中的代码,修改代码如下:

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);

    std::normal_distribution<double> noise_pdf(0., ???. / 2000.);

    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));
}

只需要修改std::normal_distribution noise_pdf(0., ???. / 2000.);中???部分。下面是仿真结果,噪声方差依次对应1—10个像素误差。
在这里插入图片描述
曲线图如下:
在这里插入图片描述
上图中所有σ4/σ3(最小奇异值和第二小奇异值之间的比例)在10-4量级,三角化都有效,随着方差的增大,σ4/σ3也在不断增大,说明三角化误差越来越大。

4.固定噪声方差参数,将观测图像帧扩成多帧(如3,4,5帧等)观察最小奇异值和第二小奇异值之间的比例变化,并绘制比例值的变化曲线

通过修改代码中的变量start_frame_id来设置观测帧数。噪声参数固定为10个像素误差(10. / 2000.),观测帧数设置为1—10,仿真结果如下,其中观测帧数为1时,无法完成三角化,σ4/σ3为0.48,三角化无效。
在这里插入图片描述
曲线图如下(舍弃观测帧数为1时的情况,从观测帧数为2时开始绘制):
在这里插入图片描述
从图中可以看出,所有σ4/σ3(最小奇异值和第二小奇异值之间的比例)都在10-4量级,三角化都有效,随着观测帧数的增加,σ4/σ3整体呈减小趋势,中间略有波动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值