ORB-SLAM2中帧分为两个部分,帧和关键帧。相机每次接收到一帧图像(双目或RGB-D接收两张图像)构成一帧,关键帧是来自于普通帧,普通帧只能解决当前帧的相机位姿和跟踪状态,只有被选为关键帧,才能决定回环检测、局部建图等工作。普通帧只能解决当前帧的情况,而关键帧可以对整个系统的后续产生影响,了解了普通帧和关键帧的区别能让我们更好的理解ORB-SLAM2算法的代码。
普通帧的几乎所有成员属性和变量关键帧都是具有的,但是ORB-SLAM2作者尚待改进的就是普通帧的很多代码,在关键帧部分又重新定义了复写了一套,其实使用继承逻辑上也不合适,但是作者其实完全可以使用组合的方式,在关键帧中加入成员变量是普通帧,也可能作者设计的时候还有其他原因我们不得而知,在ORB-SLAM2中普通帧和关键帧是完全没有任何联系的两个类,但是他们之间有很多成员变量和方法是重合的,关键帧包含了普通帧几乎所有的属性。
普通帧中包含相机相关信息、特征点的信息、对图像做畸变矫正和特征点分配。普通帧对对双目/RGBD特征点的预处理。
ORB-SLAM2对双目相机,分为左目和右目,每一目中有图像特征点,两组图像特征点进行立体匹配,通过立体匹配和视差公式可以得到每个特征点的深度,那就能得到双目特征点,如果有一些特征点计算的深度过大,带来的结果是误差也特别大,那个点就是不准确的,那么这个点就把它视作单目特征点,就是以左目为基准创建的,而不把它当做双目特征点,判断这个点的深度过大以至于只能视为单目还是这个特征点的深度足够小,判断的标准在于TUM1.yaml
中定义的ThDepth
如果通过双目匹配得到的帧计算后的特征点深度是双目相机基线的40倍或更多,就认为这个深度已经不准确了(误差已经很大了),就把它当做一个单目点来判断,这个点的深度信息是错的,就不考虑这个点的深度信息了,如果通过双目匹配是双目相机基线的40倍以内,认为这个点就在误差在接受范围内,得到深度信息。
notes:双目相机的基线就是双目相机两个摄像头之间的距离
Frame类与相机相关的参数大部分设为static类型,整个系统内的所有Frame对象共享同一份相机参数
成员函数/变量 | 访问控制 | 意义 |
---|---|---|
mbInitialComputations | public static | 是否需要为Frame类的相机参数赋值 初始化为false,第一次为相机参数赋值后变为false |
float fx, float fy float cx, float cy float invfx, float invfy | public static | 相机内参矩阵 设为static是否更好? |
cv::Mat mK | public | 相机基线,相机双目间的距离 |
float mbf | public | 相机基线与焦距的乘积 |
跟踪对象首先从配置文件TUM1读入这些参数yaml,然后传递给Frame类的构造函数。在第一次调用Frame构造函数时,将值赋给这些成员变量。
Tracking::Tracking(const string &strSettingPath, ...) {
// 从配置文件中读取相机参数并构造内参矩阵
cv::FileStorage fSettings(strSettingPath, cv::FileStorage::READ);
float fx = fSettings["Camera.fx"];
float fy = fSettings["Camera.fy"];
float cx = fSettings["Camera.cx"];
float cy = fSettings["Camera.cy"];
cv::Mat K = cv::Mat::eye(3, 3, CV_32F);
K.at<float>(0, 0) = fx;
K.at<float>(1, 1) = fy;
K.at<float>(0, 2) = cx;
K.at<float>(1, 2) = cy;
K.copyTo(mK);
// ...
}
// 每传来一帧图像,就调用一次该函数
cv::Mat Tracking::GrabImageStereo(..., const cv::Mat &imRectLeft, const cv::Mat &imRectRight, const double ×tamp) {
mCurrentFrame = Frame(mImGray, mK, mDistCoef, mbf, mThDepth);
Track();
// ...
}
// Frame构造函数
Frame::Frame(cv::Mat &K, cv::Mat &distCoef, const float &bf, const float &thDepth)
: mK(K.clone()), mDistCoef(distCoef.clone()), mbf(bf), mThDepth(thDepth) {
// 中间省略...
// 第一次调用Frame()构造函数时为所有static变量赋值
if (mbInitialComputations) {
fx = K.at<float>(0, 0);
fy = K.at<float>(1, 1);
cx = K.at<float>(0, 2);
cy = K.at<float>(1, 2);
invfx = 1.0f / fx;
invfy = 1.0f / fy;
// ...
mbInitialComputations = false; // 赋值完毕后将mbInitialComputations复位
}
mb = mbf / fx;
}
成员变量mpORBextractorLeft和mpORBextractorRight的()操作符在Frame类构造函数中被调用,用于特征点提取
(1)其畸变矫正前的左目特征点是mvKeys[i].
(2)其畸变矫正后的左目特征点是mvKeysUn[i].
(3)其在右目图片中对应特征点的横坐标为mvuRight[i],纵坐标与mvKeys[i]的纵坐标相同.
(4)特征点的深度是mvDepth[i].