1.如何运行示例代码

首先是如何运行示例代码,这里遇到了很多问题:

(1)首先要下载Kitti数据集,并在config/default.yaml文件内修改路径。

(2)安装Glog、GTest、GFlags库,这部分比较简单,可能遇到的问题可以参考以下几个教程:

Ubuntu 16.04 系统 gflags & glog 安装_calvinpaean的博客-CSDN博客

安装glog 执行bash./ autogen时报错“没有这个文件”_蓝雨飞扬7的博客-CSDN博客

(3)Opencv版本,我最初用的是Opencv4,遇到了一些奇怪的bug,后来安装的Opencv3.4.5就解决了一部分奇怪的bug。

(4)修改app/run_kitti_stereo.cpp源码,主函数第一行为:

gflags::ParseCommandLineFlags(&argc, &argv, true);

(5)即使解决了所有问题,我还是不能用Clion直接运行程序,只能运行bin文件下的二进制程序。即在bin文件路径下直接命令行./run_kitti_stereo.cpp,虽然没解决Clion的问题,但至少能成功运行了。

2.主要头文件

能够成功运行代码后再来关注是怎么实现的,对于工程框架即各个文件夹内容的作用在P347已经详细介绍了,这里主要看一下include/myslam文件下的一系列头文件:

(1)algorithm.h:三角化函数,与前文讲过的方法不同,使用了SVD算法,后续再介绍。

(2)backend.h:后端,使用滑动窗口法对固定数量帧优化。

(3)camera.h:相机类,主要包括相机内外参数及世界、相机和像素三个坐标系下的坐标变换。

(4)common_include.h:常用矩阵类型的定义。

(5)config.h:用于获取config/default.yaml配置文件信息,包括相机内参和数据集路径。

(6)dataset.h:用于读取数据集数据。

(7)feature.h:2D特征点类,主要包括持有帧、对应路标、2D坐标、是否异常等。

(8)frame.h:帧类,主要包括左右图像、位姿、是否为关键帧等。

(9)frontend.h:前端,估计当前帧的位姿,必要时触发后端优化。

(10)g2o_types.h:G2o图优化定义,两类节点(相机位姿和地图点),两类边(用于前端估计位姿的一元边和用于后端优化地图的二元边)。

(11)map.h:地图类,主要包括关键帧、地图点的添加和删除。

(12)mappoint.h:路标点类,三角化后的特征点三维坐标。

(13)viewer.h:负责整个过程的可视化。

(14)visual_odometry.h:整个视觉里程计的对外接口,可在主函数内直接调用。

3.主要算法框架图

程序可以分为数据结构+算法,这里我们不再讨论数据结构,重点关注一下算法的实现,这里主要包括前端(frontend)+后端(backend),书中也有简单的程序框图,但很难显示所有细节。因此这里简单整理一下:

这里只整理了前端的系统框图,没有后端,主要是因为我个人感觉后端的代码框架并不复杂,仍然是一个两类节点(相机位姿和路标节点)+多条二元边的优化问题(前文第九讲 后端优化(1)中详细分析过),并在此基础上增加了一步异常数据的筛选,代码复杂一点的部分都是在所构建的数据结构内读取相应信息的过程。整个过程使用滑动窗口法控制BA规模,都是前文有详细讲过的内容。

4.主要关注的问题

(1)三角化方法

前文提到过,三角化本质上也是一个最小二乘问题,并且在第12讲的应用中有使用最小二乘解的结论求解像素深度,但在此工程中又使用了另一种方法“基于SVD奇异值分解的三角化”,本质上是用SVD算法求解一个最小二乘问题。代码部分如下:

/*** 基于奇异值分解的线性三角测量* @param poses     poses,* @param points    points in normalized plane* @param pt_world  triangulated point in the world* @return true if success*/
inline bool triangulation(const std::vector<SE3> &poses,const std::vector<Vec3> points, Vec3 &pt_world) {MatXX A(2 * poses.size(), 4);//4*4VecX b(2 * poses.size());//4*1b.setZero();for (size_t i = 0; i < poses.size(); ++i) {Mat34 m = poses[i].matrix3x4();//转化为3*4的矩阵形式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);}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;
}

理论推导过程可以参考下面这边博客:

https://blog.csdn.net/qq_42995327/article/details/118917141

(2)左右视角的使用:

        此工程是一个使用双目相机的视觉SLAM,但如何使用双目相机的图像信息呢?事实上,只有较少的状态下使用右侧图像信息:初始化地图、更新关键帧并三角化更新路标点;而左侧图像信息,是从始至终都在使用的,所有的跟踪过程都是用左侧图像来估计当前相机位姿的初始值。

(3)LK光流:

前端使用的是LK光流来匹配关键点,这在第八讲 视觉里程计内有详细讨论,“本质上是替代图像间特征点匹配的过程,使用“灰度不变”的假设确定关键点的对应关系,因此使用时仅需要对第一幅图像提取关键点,不需要计算描述子,显著缩短运算时间。在完成匹配后,使用特征点法相同的方式估计摄像机运动参数。至于使用哪种方法,要根据是否已知图像点的深度信息来选择。”代码中则是直接使用Opencv自带的LK光流算法实现的。

(4)图优化后去除异常数据:

G2o图优化我们都非常熟悉了,但这一次相比于之前的应用,在完成优化后进行了一步异常数据的判断,无论在前端(两帧图像间的位姿估计)还是后端(求解BA问题)都有标记异常数据的过程。筛选方法比较简单,设定一个阈值(固定的或者动态的)根据各个边的损失函数判断数据是否异常,这样有助于提高系统的稳定性,减少累计误差。

(5)如何控制BA规模:

第十讲 后端(2)设计到两种控制BA规模的方法,这里使用的是“滑动窗口法”。但是滑动窗口的选择较为简单,大致可以理解为保存最近的7帧(这里的“近”我认为指的是距离上的近),但实际上在剔除旧关键帧时还是有一定的依据的,在map.cpp文件下,先找到距离当前帧最近和最远的两帧,若最近的小于阈值,则优先剔除,否则剔除最远的:

void Map::RemoveOldKeyframe() {if (current_frame_ == nullptr) return;// 寻找与当前帧最近与最远的两个关键帧double max_dis = 0, min_dis = 9999;double max_kf_id = 0, min_kf_id = 0;auto Twc = current_frame_->Pose().inverse();for (auto& kf : active_keyframes_) {if (kf.second == current_frame_) continue;auto dis = (kf.second->Pose() * Twc).log().norm();if (dis > max_dis) {max_dis = dis;max_kf_id = kf.first;}if (dis < min_dis) {min_dis = dis;min_kf_id = kf.first;}}const double min_dis_th = 0.2;  // 最近阈值Frame::Ptr frame_to_remove = nullptr;if (min_dis < min_dis_th) {// 如果存在很近的帧,优先删掉最近的frame_to_remove = keyframes_.at(min_kf_id);} else {// 删掉最远的frame_to_remove = keyframes_.at(max_kf_id);}LOG(INFO) << "remove keyframe " << frame_to_remove->keyframe_id_;// remove keyframe and landmark observationactive_keyframes_.erase(frame_to_remove->keyframe_id_);for (auto feat : frame_to_remove->features_left_) {auto mp = feat->map_point_.lock();if (mp) {mp->RemoveObservation(feat);}}for (auto feat : frame_to_remove->features_right_) {if (feat == nullptr) continue;auto mp = feat->map_point_.lock();if (mp) {mp->RemoveObservation(feat);}}CleanMap();
}

(6)其他:

除了这些重要的算法外,高博还设计了一套完备的数据结构,并在必要的地方添加了“锁”,防止数据访问时出错;单独为后端开了一个线程,在需要时开始线程运行后端的优化程序;此外还有可视化数据界面、数据文件读取以及一系列编程技巧等等。可以说哪怕只是实现一个简单可靠的视觉SLAM系统仍是一个十分复杂的问题,这里笔者也只是重点关注了书中曾讲到过的重要的SLAM算法,其他部分的内容由于能力有限没能仔细研究清楚。下一篇再分享前端和后端两个主要的源代码的注释。

视觉SLAM十四讲学习笔记——第十三讲 实践:设计SLAM系统相关推荐

  1. 视觉SLAM十四讲学习笔记——第十二讲 建图

    SLAM实际上是一种底层技术,往往用来给上层应用提供信息.在前文已实现的部分,我们最多得到的仅是一个稀疏的地图,在需要实现导航.避障.重建等应用时,稀疏地图很难提供足够的信息,需要考虑如何重建稠密地图 ...

  2. 视觉SLAM十四讲学习笔记-第七讲-视觉里程计-三角测量和实践

     专栏汇总 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第 ...

  3. 视觉SLAM十四讲学习笔记-第七讲-视觉里程计-对极几何和对极约束、本质矩阵、基础矩阵

    专栏系列文章如下:  专栏汇总 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-CSDN博客 视觉SLA ...

  4. 视觉SLAM十四讲学习笔记-第六讲学习笔记总结(1)---非线性优化原理

    第六讲学习笔记如下: 视觉SLAM十四讲学习笔记-第六讲-非线性优化的状态估计问题_goldqiu的博客-CSDN博客 ​​​​​​视觉SLAM十四讲学习笔记-第六讲-非线性优化的非线性最小二乘问题_ ...

  5. 视觉SLAM十四讲学习笔记-第四讲---第五讲学习笔记总结---李群和李代数、相机

    第四讲---第五讲学习笔记如下: 视觉SLAM十四讲学习笔记-第四讲-李群与李代数基础和定义.指数和对数映射_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第四讲-李代数求导与扰动模 ...

  6. 视觉SLAM十四讲学习笔记---前三讲学习笔记总结之SLAM的作用、变换和位姿表示

    经过半年学习SLAM相关知识,对SLAM系统有了一些新的认识,故回看以前的学习记录,做总结和校正. 前三讲学习笔记如下: 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客 视觉S ...

  7. 视觉SLAM十四讲学习笔记-第七讲-视觉里程计-特征点法和特征提取和匹配实践

    专栏系列文章如下: 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习 ...

  8. 视觉SLAM十四讲学习笔记-第六讲-非线性优化的实践-高斯牛顿法和曲线拟合

    专栏系列文章如下: 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习 ...

  9. 视觉SLAM十四讲学习笔记-第六讲-非线性优化的非线性最小二乘问题

    专栏系列文章如下: 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习 ...

最新文章

  1. Jquery Ajax自定义无刷新提交表单Form
  2. 控制C++的类只能在堆分配或只能在栈分配
  3. Java获取当前线程的名字以及为线程命名
  4. 创建react应用程序_如何使用React创建一个三层应用程序
  5. Java容器 | 基于源码分析List集合体系
  6. 指针与引用的混合使用总结
  7. 吴恩达深度学习4.1笔记_Convolutional Neural Networks_卷积神经网络基础
  8. Oracle管理文件OMF (oracle managed files)
  9. zabbix配置飞信报警
  10. y是x的平方的反比例函数_反比例函数的图像和性质 x不能为0y也不能为0所以反...
  11. 中大计算机研究生华为,考上中山大学很厉害吗?毕业后有机会去华为吗?本文有答案...
  12. android透明主题引起的快速点击activity触发退到桌面
  13. CHAPTER 2 Docker镜像
  14. Unity 水体效果模拟
  15. java计算机毕业设计物联网实验课程考勤网站源码+lw文档+系统+数据库
  16. 关于Unity 5.x Lightmap Shader 编译过慢的分析
  17. Ubuntu 20.04 系统最快安装WRF软件手册
  18. 在几何画板中如何制作圆柱的侧面展开动画_几何画板画圆柱体的的两种动画制作方法...
  19. 实测 | 海纳百川,华为OceanStor Pacific分布式存储为多元算力应用带来更优选择...
  20. 电路图的电阻值部分是DNP什么意思??

热门文章

  1. float 精度探究
  2. Encoder-decoder模型及Attention机制
  3. python判断是否为中文、中文符号、英文、英文符号
  4. Python 小白学习之:linux 基础和 python 入门
  5. GO语言:zorm介绍
  6. 网易云音乐api歌单数据获取
  7. 【文件监控】之一:理解 ReadDirectoryChangesW part1
  8. Objective-C类别(catagory)
  9. 回顾公司系统与微信绑定流程
  10. APP服务器与Web服务器的区别是什么?