资料:SLAM草稿
ECCV 2018 视觉定位综述
以下是今年ECCV上几位大牛介绍SLAM技术的tutorial pdfs,涉及基于特征点以及基于学习的SLAM算法介绍,并在最后探究了SLAM领域的主要问题以及未来的发展趋势。
- Feature-based vs. Learned Approaches
- Current State of Feature-based Localization
- Learning-based Localization
- Failure Cases of Feature-based and Learning-based Methods
- Long-term Localization: Towards Higher-level Scene Understanding
- Open Problems of Learning-based Methods
整理还未完备,先祭出这几本书&博客,方便随时查看。
教程
- slambook
- slambook code
- OpenCV3.0
- SLAM前世今生
- g2o Github
- g2o details
- g2o译文
- 深入理解图优化与g2o:g2o篇
- 深入理解图优化与g2o:g2o篇 code
- 白巧克力亦唯心的博客
- Graph slam学习
g2o漫谈
g2o里面有各种各样的求解器,而它的顶点、边的类型多种多样。通过自定义顶点和边,事实上,只要一个优化问题能够表达成图,就可以用g2o去求解它。常见的,比如bundle adjustment,ICP,数据拟合等。g2o是一个C++项目,其中矩阵数据结构多来自Eigen。
先看上部分,SparseOptimizer是我们需要维护的东西,是一个Optimizable Graph,也是一个Hyper Graph。一个SparseOptimizer含有很多个顶点(继承与Base Vertex)和多条边(继承自BaseUnaryEdge,BaseBinaryEdge或BaseMultiEdge)。这些Base Vertex和Base Edge都是抽象的基类,而实际用的顶点和边,都是它们的派生类。
我们用SparseOptimizer.addVertex 和 SparseOptimizer.addEdge 向图中添加顶点和边,然后调用SpaseOptimizer.optimize来优化。
在优化前,需要指定我们用的求解器和迭代算法。从图下半部分来看,一个SparseOptimization拥有一个Optimization
Algorithm,继承自Gusss-Newton,Levernberg-Marquardt,Powell’s dogleg
三者之一,同时拥有一个Solver,含有俩个部分。一个是SparseBlockMatrix,用于计算稀疏的雅克比和海塞;一个用于计算
- 选择一个线性方程求解器,从 PCG, CSparse, Choldmod中选
- 选择一个 BlockSolver
- 选择一个迭代策略,从GN, LM, Doglog中选
BlockSolver,块求解器
块求解器是包含线性求解器的存在,之所以是包含,是因为块求解器会构建好线性求解器所需要的矩阵块(也就是
这里再记录下一个比较容易混淆的问题,也就是在初始化块求解器的时候的参数问题。大部分的例程在初始化块求解器的时候都会使用如下的程序代码:
1 | std::unique_ptr<g2o::BlockSolver_6_3::LinearSolverType> linearSolver = g2o::make_unique<g2o::LinearSolverCholmod<g2o::BlockSolver_6_3::PoseMatrixType>>(); |
1 | template<int p, int l> |
g2o的顶点(Vertex)
g2o::BaseVertex< D, T >
其中int D, typename T
首先记录一下定义模板的两个参数D和T,两个类型分别是int和typename的类型,D表示的是维度,g2o源码里面是这个注释的:
1 | static const int Dimension = D; //< dimension of the estimate (minimal) in the manifold space |
可以看到这个D并非是顶点(更确切的说是状态变量)的维度,而是其在流形空间(manifold)的最小表示,这里一定要区别开;之后是T,源码里面也给出了T的作用:
1 | typedef T EstimateType; |
可以看到,这里T就是顶点(状态变量)的类型。在顶点的继承中,这两个参数是直接面向我们的,所以务必要定义妥当。
g2o的边(Edge)
g2o::BaseBinaryEdge< D, E, VertexXi, VertexXj >
其中int D, typename E
首先还是介绍这两个参数,还是从源码上来看:
1 | static const int Dimension = D; |
可以看到,D决定了误差的维度,从映射的角度讲,三维情况下就是2维的,二维的情况下是1维的;然后E是measurement的类型,也就是测量值是什么类型的,这里E就是什么类型的(一般都是Eigen::VectorN表示的,N是自然数)。
typename VertexXi
,typename VertexXj
这两个参数就是边连接的两个顶点的类型,这里特别注意一下,这两个必须一定是顶点的类型,也就是继承自BaseVertex
等基础类的类!不是顶点的数据类!例如必须是VertexSE3Expmap
而不是VertexSE3Expmap
的数据类型类SE3Quat
。原因的话源码里面也很清楚,因为后面会用到一系列顶点的维度等等的属性,这些属性是数据类型类里面没有的。
_jacobianOplusXi
,_jacobianOplusXj
在成员函数linearizeOplus()
(线性化函数)中维护着这两个量,_jacobianOplusXi
和_jacobianOplusXj
就是所谓的雅可比矩阵,如果是二元边的话二者都有;若为一元边,只有_jacobianOplusXi
。
这两个变量本质上是Eigen::Matrix类型的,具体定义在这里:
1 | typedef typename Eigen::Matrix<number_t, D, Di, D==1?Eigen::RowMajor:Eigen::ColMajor>::AlignedMapType JacobianXiOplusType; |