本书概要
《视觉SLAM十四讲》的出版日期 2017.3
Ubuntu 14.04
C++
Kdevelop
本书框架
部分/章节 | 内容 |
---|---|
第1部分:数学基础篇 第1~6讲 |
⑴前言 ⑵SLAM系统概述 ⑶三维空间运动 ⑷李群和李代数 ⑸针孔相机模型以及图像在计算机中的表达 ⑹非线性优化 |
第2部分:SLAM技术篇 第7~14讲 |
⑺特征点法的视觉里程计 ⑻直接法的视觉里程计 ⑼视觉里程计的实践 ⑽后端优化 ⑾后端优化中的位姿图 ⑿回环检测 ⒀地图构建 ⒁当前的开源SLAM项目以及未来的发展方向 |
实例索引
主题 | 内容 | 备注 |
---|---|---|
CH2:编程基础 | g++编译、cmake编译、Kdevelop集成开发环境 |
CH1 预备知识
SLAM(Simultaneous Localization and Mapping),同时定位与地图构建
搭载特定传感器的主体,在没有环境先验信息的情况下,于运动过程中建立环境的模型,同时估计自己的运行
传感器主要为相机,称为“视觉SLAM”
两个问题:
- 定位:估计传感器自身的位置
- 建图:建立周围环境的模型
要求:
- 实时地
- 没有先验知识
CH2 初识SLAM
引入
感知的“内外之分”:
功能 | 解释 | 备注 |
---|---|---|
定位 | 我在什么地方? | 自身的状态(位置) |
建图 | 周围环境是什么样? | 外在的环境(地图) |
两类传感器:
传感器 | 举例 | 优劣 |
---|---|---|
携带于机器人本体上 | 机器人的轮式编码器、相机、激光传感器 | 测量的是间接的物理量而不是直接的位置数据,可通过间接手段推算位置,好处是没有对环境提出任何要求,使定位方案可适用于未知环境 |
安装于环境中 | 导轨、二维码标志 | 测量的是直接的位置信息,简单可靠,但传感器约束了外部环境,无法提供普遍、通用的解决方案 |
视觉SLAM:以一定速率拍摄周围的环境,形成一个连续的视频流
三大类相机:
相机 | 说明 | 缺点 |
---|---|---|
单目相机(Monocular) | 一个摄像头 | ①平移之后才能计算深度 ②无法确定真实尺度(尺度不确定性) |
双目相机(Stereo) | 两个摄像头 | 计算量大 |
深度相机(RGB-D) | 红外结构光或Tof(Time-of-Flight) 彩色图片 + 每个像素与相机之间的距离 |
①测量范围窄、视野小 ②噪声大、易受日光干扰(主要用于室内) ③无法测量透射材质 |
照片本质上是拍照时的场景(Scene)在相机的成像平面上留下的一个投影,以二维的形式反映了三维的世界
单目相机拍摄的图像只是三维空间的二维投影。这个过程丢掉了深度(或距离)
- 在单目相机中,无法通过单张图片来计算场景中物体与我们之间的距离(远近)
- 在单张图像里,无法确定一个物体的真实大小,可能是一个很大但很远的物体,也可能是很近但很小的物体
经典视觉SLAM框架
模块 | 内容 | 备注 |
---|---|---|
传感器信息读取 | 视觉SLAM中主要为相机图像信息的读取和预处理 在机器人中,还有码盘、惯性传感器等信息的读取和同步 |
|
视觉里程计 (Visual Odometry,VO) |
估算相邻图像间相机的运动,以及局部地图的样子 又称为前端(Front End) |
会不可避免地出现累积漂移(Accumulating Drift),故需要后端优化和回环检测 |
后端优化 (Optimization) |
接受不同时刻视觉里程计测量的相机位姿,以及回环检测的信息,对它们进行优化,得到全局一致的轨迹和地图 又称为后端(Back End) |
从带有噪声的数据中估计整个系统的状态,以及这个状态估计的不确定性有多大(最大后验概率估计,MAP,Maximum-a-Posteriori) |
回环检测 (Loop Closing) |
判断机器人是否到达过先前的位置。如果检测到回环,会把信息提供给后端进行处理 | 解决位置估计随时间漂移的问题 视觉回环检测本质是一种计算图像数据相似性的算法 |
建图 (Mapping) |
根据估计的轨迹,建立与任务要求对应的地图 | 地图是对环境的描述,描述不固定,视SLAM的应用而定 建图没有固定的形式和算法 |
前端与后端:
模块 | 作用 | 领域 |
---|---|---|
前端 | 给后端提供待优化的数据、以及这些数据的初始值 | 视觉SLAM中前端和计算机视觉研究领域相关 |
后端 | 负责整体的优化过程,不关心数据来自什么传感器 | 主要是滤波与非线性优化算法 |
地图的种类:
地图 | 特点 | 备注 |
---|---|---|
度量地图 | 精确地表示地图中物体的位置关系 | 稀疏(Sparse)地图——定位:不需要表达所有的物体,表达一部分具有代表意义的东西(路标,Landmark) 稠密(Dense)地图——导航:建模所有看到的东西,按照某种分辨率,由许多小块组成 |
拓扑地图 | 更强调地图元素之间的关系 | 是图(Graph),由节点和边组成,只考虑节点间的连通性 不擅长表达具有复杂结构的地图 |
SLAM问题的本质:对运动主体自身和周围环境空间不确定性的估计
如果把工作环境限定在静态、刚体、光照变换不明显、没有人为干扰的场景,SLAM系统是相当成熟的了
SLAM问题的数学表述
两个基本方程:
方程 | 直观含义 |
---|---|
运动方程 | 从时刻到时刻,机器人的位置是如何变化的 |
观测方程 | 机器人在位置上看到某个路标点,产生了一个观测数据 |
符号解释:
- 相机采集数据的离散时刻
- 各时刻机器人的位置记为,构成了机器人的轨迹
- 设路标点共有个,用表示
- 运动方程中,是运动传感器的读数(不一定是位置之差,还可能是加速度、角速度等)
- 运动方程中,是噪声
- 观测方程中,是噪声
- 观测方程中,是观测数据
这两个方程描述了最基本的SLAM问题:
当知道运动测量的数据、传感器的读数时,如何求解定位问题(估计)和建图问题(估计)
把SLAM问题建模成了一个状态估计问题:如何通过带有噪声的观测数据,估计内部的、隐藏着的状态变量
状态估计问题的求解,与两个方程的具体形式(线性/非线性)、噪声服从哪种分布(高斯/非高斯)有关
线性高斯系统(Linear Gaussian,LG系统)最简单,它的无偏的最优估计可以由卡尔曼滤波器(Kalman Filter,KF)给出
非线性非高斯系统(Non-Linear Non-Gaussian,NLNG系统)较为复杂,使用扩展卡尔曼滤波器(Extended Kalman Filter,EKF)和非线性优化两大类方法求解
时至今日,主流视觉SLAM使用以图优化(Graph Optimization)为代表的优化方法
实践:编程基础
Linux C++程序
使用g++编译
目录结构:
├─ ch2
│ └─ helloSLAM.cpp
在helloSLAM.cpp中输入以下代码:1
2
3
4
5
6
7
8
using namespace std;
int main( int argc, char** argv )
{
cout<<"Hello SLAM!"<<endl;
return 0;
}
用g++将其编译成可执行文件,并运行可执行文件:1
2g++ helloSLAM.cpp
./a.out
“command not found”的解决方案:安装g++:1
sudo apt-get install g++
使用cmake编译
目录结构:
├─ ch2
│ ├─ helloSLAM.cpp
│ └─ CMakeLists.txt
在一个cmake工程中,用cmake命令生成一个makefile文件,然后用make命令根据makefile文件的内容编译整个工程
- cmake过程:处理工程文件之间的依赖关系
- make过程:实际调用g++来编译程序
新建CMakeLists.txt,内容如下:1
2
3
4
5
6
7
8
9# 声明要求的 cmake 最低版本
cmake_minimum_required( VERSION 2.8 )
# 声明一个 cmake 工程
project( HelloSLAM )
# 添加一个可执行程序
# 语法:add_executable( 程序名 源代码文件 )
add_executable( helloSLAM helloSLAM.cpp )
在当前目录下,调用cmake对CMakeLists.txt进行分析,并用make命令编译,再运行程序:1
2
3cmake .
make
./helloSLAM
删除中间文件的版本的命令组合:1
2
3
4
5mkdir build
cd build
cmake ..
make
./helloSLAM
使用库
步骤:
- 程序代码由头文件和源文件组成
- 带有main函数的源文件编译成可执行程序
其他的编译成库文件 - 如果可执行程序想调用库文件中的函数,需要参考该库提供的头文件,以明白调用的格式
同时,要把可执行程序链接到库文件上
Linux中,库文件分为两种:
库文件 | 后缀 | 区别 |
---|---|---|
静态库 | .a | 每次被调用都会生成一个副本 |
共享库 | .so | 只有一个副本 |
目录结构:
├─ ch2
│ ├─ CMakeLists.txt
│ ├─ libHelloSLAM.cpp
│ ├─ libHelloSLAM.h
│ └─ useHello.cpp
CMakeLists.txt:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22# 声明要求的 cmake 最低版本
cmake_minimum_required( VERSION 2.8 )
# 声明一个 cmake 工程
project( HelloSLAM )
# 设置编译模式
set( CMAKE_BUILD_TYPE "Debug" )
# 添加一个库
# 静态库
# 把libHelloSLAM.cpp编译成一个叫作“hello”的库
add_library( hello libHelloSLAM.cpp )
# 共享库
add_library( hello_shared SHARED libHelloSLAM.cpp )
# 添加一个可执行程序
# 语法:add_executable( 程序名 源代码文件 )
add_executable( useHello useHello.cpp )
# 将库文件链接到可执行程序上
target_link_libraries( useHello hello_shared )
libHelloSLAM.cpp:1
2
3
4
5
6
7
8//这是一个库文件
using namespace std;
void printHello()
{
cout<<"Hello SLAM"<<endl;
}
libHelloSLAM.h:1
2
3
4
5
6
7
// 上面的宏定义是为了防止重复引用这个头文件而引起的重定义错误
void printHello();
useHello.cpp:1
2
3
4
5
6
7
8
// 使用 libHelloSLAM.h 中的 printHello() 函数
int main( int argc, char** argv )
{
printHello();
return 0;
}
使用IDE
安装Kdevelop:1
sudo apt-get install kdevelop
断点调试功能
Project → Open/Import Project
Run → Configure Launches
快捷键:
快捷键 | 功能 | 备注 |
---|---|---|
注释 | Ctrl + D | |
取消注释 | Ctrl + Shift + D | |
单步运行 | F10 | 单步调试 |
单步跟进 | F11 | 单步调试 |
单步跳出 | F12 | 单步调试 |
CH3 三维空间刚体运动
旋转矩阵
刚体:位置、姿态
- 位置:相机在空间中的哪个地方
- 姿态:相机的朝向
实践:Eigen
旋转向量和欧拉角
四元数
*相似、仿射、射影变换
实践:Eigen几何模块
可视化演示
CH4 李群与李代数
李群与李代数基础
指数与对数映射
李代数求导与扰动模型
实践:Sophus
*相似变换群与李代数
CH5 相机与图像
相机模型
图像
实践:图像的存取与访问
实践:拼接点云
CH6 非线性优化
状态估计问题
非线性最小二乘
实践:Ceres
实践:g2o
CH7 视觉里程计1
特征点法
实践:特征提取和匹配
2D-2D:对极几何
实践:对极约束求解相机运动
三角测量
实践:三角测量
3D-2D:PnP
实践:求解PnP
3D-3D:ICP
实践:求解ICP
参考
参考内容 | 参考方面 |
---|---|
《视觉SLAM十四讲》 | 主体内容 |
《视觉SLAM十四讲》的代码 | 主体代码参考 |