VINS-mono中的三角化方法(SVD)

1 引言

最近在跟着高博的《视觉SLAM十四讲》,自己手写一个双目里程计的一个项目,进行到了特征点的三角化部分时,发现其用的和VINS-mono一个方法。他并没有直接调用OpenCV中的现成函数,而是用了SVD分解的方法。所以心想写一个关于SVD分解进行特征点三角化的博客,加深一下印象。

2 三角化(14讲方法)

三角化又叫三角测量,本质是用相机的运动估计特征点的空间位置,发生在估计得到帧间运动之后(单目)。三角测量是指,通过在两处观察同一个点的夹角,确定该点的距离。由于双目本身就能得到视角不同的两帧图像,所以我们找到左右目相机的图像对应的特征点就可以直接进行三角测量;RGB-D相机就不用三角化了,直接通过物理手段得到深度值了。
在这里插入图片描述
如上图所示,如果我们知道了两帧图像中某对特征点的匹配关系,就能通过三角关系得出这对特征点对应的空间点坐标。
在高博的《视觉SLAM十四讲》中,是这样算的:

按照对极几何中的定义,设 x 1 , x 2 x1, x2 x1,x2 为两个特征点的归一化坐标,那么它们满足:
在这里插入图片描述
已知两帧的相对位姿关系 R , t R,t Rt,想要求解的是两个特征点的深度 s 1 和 s 2 s_1和s_2 s1s2。他是两个值分开求的,比如:先算 s 2 s_2 s2
在这里插入图片描述
求出 s 2 ,那么 s 1 s_2,那么s_1 s2,那么s1也就好求了。于是,就得到了两个帧下的点的深度,确定了它们的空间坐标。

3 三角化(SVD方法)

问题描述:已知左右两目相机的位姿以及特征点(相机归一化坐标)的匹配关系,求解特征点的世界坐标点。下面是具体推导过程:
在这里插入图片描述
我们接着把变换矩阵写成以下形式:
在这里插入图片描述
然后我们用到一个性质,如果两个向量可以写成 a ⃗ = k b ⃗ \vec{a}=k\vec{b} a =kb ,则 a ⃗ 平行于 b ⃗ \vec{a}平行于\vec{b} a 平行于b ,而我们知道两个向量平行,那么他们的叉积就是 0 0 0,所以就有了下面的式子:
在这里插入图片描述
在这里插入图片描述
这样一帧图像对应这么一个方程组,那么加上右目就是再有这样的两个式子拼成一个方程组(因为他们要求的未知数是一样的,都是世界坐标点),我们还发现,它实际上就是求解 A x = 0 A x=0 Ax=0的问题。
在这里插入图片描述
很容易得到 A A A是一个 4 × 4 4×4 4×4的矩阵, p w p^w pw是一个 4 × 1 4×1 4×1的向量,解决这样一个 A x = 0 A x=0 Ax=0的问题,当然是可以用 S V D SVD SVD分解的方法来做。

关于 S V D SVD SVD分解的最优解求解问题,可以看我的另一篇博客

4 两套代码示例

了解了方法之后再来看代码,
1、高博SVD三角化代码如下:

bool triangulation(const std::vector<SE3> &poses,
                   const std::vector<Vec3> points, Vec3 &pt_world) {
    MatXX A(2 * poses.size(), 4);//这里当然是4x4的系数阵
    VecX b(2 * poses.size());
    b.setZero();//b=0
    // 填充稀疏矩阵块
    for (size_t i = 0; i < poses.size(); ++i) {
        Mat34 m = poses[i].matrix3x4();
        A.block<1, 4>(2 * i, 0) = points[i][0] * m.row(2) - m.row(0);
        A.block<1, 4>(2 * i + 1, 0) = points[i][1] * m.row(2) - m.row(1);
    }
    //进行SVD分解
    auto svd = A.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV);
    pt_world = (svd.matrixV().col(3) / svd.matrixV()(3, 3)).head<3>();//取前三维作为世界坐标(这里面包括了齐次向量归一化)
	// 这是保证最后一个奇异值相比上一个奇异值有较大的差距,才是有效解
    if (svd.singularValues()[3] / svd.singularValues()[2] < 1e-2) {
        return true;
    }
    return false;// 解质量不好,放弃
}

2、VINS-mono的代码在这:

void GlobalSFM::triangulatePoint(Eigen::Matrix<double, 3, 4> &Pose0, Eigen::Matrix<double, 3, 4> &Pose1,
						Vector2d &point0, Vector2d &point1, Vector3d &point_3d)
{
	// 通过奇异值分解求解一个Ax = 0得到
	Matrix4d design_matrix = Matrix4d::Zero();
	design_matrix.row(0) = point0[0] * Pose0.row(2) - Pose0.row(0);
	design_matrix.row(1) = point0[1] * Pose0.row(2) - Pose0.row(1);
	design_matrix.row(2) = point1[0] * Pose1.row(2) - Pose1.row(0);
	design_matrix.row(3) = point1[1] * Pose1.row(2) - Pose1.row(1);
	Vector4d triangulated_point;
	triangulated_point =
		      design_matrix.jacobiSvd(Eigen::ComputeFullV).matrixV().rightCols<1>();
	// 齐次向量归一化
	point_3d(0) = triangulated_point(0) / triangulated_point(3);
	point_3d(1) = triangulated_point(1) / triangulated_point(3);
	point_3d(2) = triangulated_point(2) / triangulated_point(3);
}

4 非常感谢您的阅读

  • 12
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宛如新生

转发即鼓励,打赏价更高!哈哈。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值