SLAM中的三角测量

    技术2022-07-13  77

    三角测量是SLAM中,利用相机运动估计特征点空间位置的过程。

    本节旨在解决以下问题:

    三角测量的概念三角测量的过程及代码实现;三角测量有哪些不确定性如何提高三角测量的精度

    1. 概念

    在SLAM中,利用对极几何约束估计相机运动之后,我们还需要通过三角测量来估计地图点的深度。三角测量(三角化)指的是,通过在两处观测同一个点的夹角,从而确定该点的距离。

    SLAM中主要用三角化来估计像素点的距离。

    2. 过程

    按照对极几何的定义,设$x_1,x_2$为两个特征点的归一化坐标,那么他们满足:$$s_1x_1=s_2Rx_2+t.$$经过对极几何之后,已得到了运动$R,t$,需要求解两个特征点的深度$s_1$,$s_2$。

    两个深度可以分开算。若先算$s_2$,那么对上市两个做成一个$x_1$^,得:

    该式子左侧为0,右侧可看成是$s_2$的一个方程,可以根据它直接求$s_2$。有了$s_2$,$s_1$也非常容易求出。预测就可以得到两帧下的深度,即确定了空间坐标。

    3. 代码实现;

    void triangulation ( const vector<KeyPoint>& keypoint_1, const vector<KeyPoint>& keypoint_2, const std::vector< DMatch >& matches, const Mat& R, const Mat& t, vector<Point3d>& points);void triangulation ( const vector< KeyPoint >& keypoint_1, const vector< KeyPoint >& keypoint_2, const std::vector< DMatch >& matches, const Mat& R, const Mat& t, vector< Point3d >& points ){ Mat T1 = (Mat_<double> (3,4) << 1,0,0,0, 0,1,0,0, 0,0,1,0); Mat T2 = (Mat_<double> (3,4) << R.at<double>(0,0), R.at<double>(0,1), R.at<double>(0,2), t.at<double>(0,0), R.at<double>(1,0), R.at<double>(1,1), R.at<double>(1,2), t.at<double>(1,0), R.at<double>(2,0), R.at<double>(2,1), R.at<double>(2,2), t.at<double>(2,0)); Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 ); vector<Point2d> pts_1, pts_2; for ( DMatch m:matches ) { // 将像素坐标转换至相机坐标 pts_1.push_back ( pixel2cam( keypoint_1[m.queryIdx].pt, K) ); pts_2.push_back ( pixel2cam( keypoint_2[m.trainIdx].pt, K) ); } Mat pts_4d; cv::triangulatePoints( T1, T2, pts_1, pts_2, pts_4d ); // 转换成非齐次坐标 for ( int i=0; i<pts_4d.cols; i++ ) { Mat x = pts_4d.col(i); x /= x.at<float>(3,0); // 归一化 Point3d p (x.at<float>(0,0),x.at<float>(1,0),x.at<float>(2,0)); points.push_back( p ); }}

    同时,在main函数中增加三角测量部分,并验证重投影关系:

    int main (int argc, char∗∗ argv){ // ..... //􀀀􀀀 三角化 vector<Point3d> points; triangulation( keypoints_1, keypoints_2, matches, R, t, points ); //􀀀􀀀 验证三角化点与特征点的重投影关系 Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 ); for ( int i=0; i<matches.size(); i++ ) { Point2d pt1_cam = pixel2cam( keypoints_1[ matches[i].queryIdx ].pt, K ); Point2d pt1_cam_3d (points[i].x/points[i].z, points[i].y/points[i].z ); cout<<"point in the first camera frame: "<<pt1_cam<<endl; cout<<"point projected from 3D "<<pt1_cam_3d<<", d="<<points[i].z<<endl; // 第2幅图 Point2f pt2_cam = pixel2cam( keypoints_2[ matches[i].trainIdx ].pt, K ); Mat pt2_trans = R∗( Mat_<double>(3,1) << points[i].x, points[i].y, points[i].z ) + t; pt2_trans /= pt2_trans.at<double>(2,0); cout<<"point in the second camera frame: "<<pt2_cam<<endl; cout<<"point reprojected from second frame: "<<pt2_trans.t()<<endl; cout<<endl; }// ...}

    我们打印了每个空间点在两个相机坐标系下的投影坐标与像素坐标——相当于P 的投影位置与看到的特征点位置。由于误差的存在,它们会有一些微小的差异。以下是某一特征点的信息:

    point in the first camera frame: [0.0844072, 0.0734976]point projected from 3D [0.0843702, 0.0743606], d=14.9895point in the second camera frame: [0.0431343, 0.0459876]point reprojected from second frame: [0.04312769812378599, 0.04515455276163744, 1]

    可以看到,误差的量级大约在小数点后第3 位。可以看到,三角化特征点的距离大约为15。

    但由于尺度不确定性,我们并不知道这里的15 究竟是多少米。

    4. 不确定性因素

    由于噪声的存在,我们估得的运动R; t 不一定精确使(1)式为零,所以更常见的做法是求最小二乘解而不是零解。三角测量是由平移得到的,有平移才会有对极几何中的三角形,才谈得上三角测量。因此,纯旋转是无法使用三角测量的,因为对极约束将永远满足。在平移存在的情况下,我们还要关心三角测量的不确定性,这会引出一个三角测量的矛盾当平移很小时,像素上的不确定性将导致较大的深度不确定性。即若特征点运动一个像素x,使得视线角变化了一个角度,那么将测量到深度值有d 的变化。从几何关系可以看出,当t 较大时,d 将明显变小,这说明平移较大时,在同样的相机分辨率下,三角化测量将更精确。对该过程的定量分析可以使用正弦定理得到,不过这里先考虑定性分析。

    5. 如何提高精度

    要提高三角化的精度,主要有两种方法:

    提高特征点的提取精度,也就是提高图像分辨率——但这会导致图像变大,增加计算成本。使平移量增大。但是,这会导致图像的外观发生明显的变化,比如箱子原先被挡住的侧面显示出来,又比如反射光发生变化,等等。外观变化会使得特征提取与匹配变得困难。

    总而言之,增大平移,会导致匹配失效;而平移太小,则三角化精度不够——这就是三角化的矛盾。

    Processed: 0.030, SQL: 9