视觉SLAM十四讲-高翔(学习笔记)第2讲 初识SLAM

初识SLAM

1. 经典视觉SLAM 框架

在这里插入图片描述
视觉SLAM流程分为以下几步:

  1. 传感器信息读取。在视觉SLAM 中主要为相机图像信息的读取和预处理。如果在机器人中,还可能有码盘、惯性传感器等信息的读取和同步。
  2. 视觉里程计(Visual Odometry, VO)。视觉里程计任务是估算相邻图像间相机的运动,以及局部地图的样子。VO 又称为前端(Front End)。
  3. 后端优化(Optimization)。后端接受不同时刻视觉里程计测量的相机位姿,以及回环检测的信息,对它们进行优化,得到全局一致的轨迹和地图。由于接在VO 之后,又称为后端(Back End)。
  4. 回环检测(Loop Closing)。回环检测判断机器人是否曾经到达过先前的位置。如果检测到回环,它会把信息提供给后端进行处理。
  5. 建图(Mapping)。它根据估计的轨迹,建立与任务要求对应的地图。

经典的视觉SLAM 框架是过去十几年内,研究者们总结的成果。这个框架本身,以及它所包含的算法已经基本定型,并且在许多视觉程序库和机器人程序库中已经提供。如果把工作环境限定在静态、刚体,光照变化不明显、没有人为干扰的场景,那么,这个SLAM 系统是相当成熟的了。

1.1 视觉里程计

视觉里程计关心相邻图像之间的相机运动,最简单的情况当然是两张图像之间的运动关系
在这里插入图片描述
比如从这两张图片中,我们用肉眼直觉可以感受到两张图的视角变化关系,但转动了多少度,我们就很难给出一个确切的答案了。在计算机中,为了定量的估计相机的运动,必须在了解相机与空间点的几何关系之后进行。

VO之所以叫里程计是因为是因为它和实际的里程计一样,只计算相邻时刻的运动,而和再往前的过去的信息没有关联。只要把相邻时刻的运动“串”起来,就构成了机器人的运动轨迹,从而解决了定位问题。另一方面,我们根据每个时刻的相机位置,计算出各像素对应的空间点的位置,就得到了地图。然而,仅通过视觉里程计来估计轨迹,将不可避免地出现累计漂移(Accumulating Drift)
我们知道,每次估计都带有一定的误差,而由于里程计的工作方式,先前时刻的误差将会传递到下一时刻,导致经过一段时间之后,估计的轨迹将不再准确。比方说,机器人先向左转90 度,再向右转了90 度。由于误差,我们把第一个90 度估计成了89 度。那我们就会尴尬地发现,向右转之后机器人的估计位置并没有回到原点。更糟糕的是,即使之后的估计再准确,与真实值相比,都会带上这-1 度的误差。
在这里插入图片描述
这也就是所谓的漂移(Drift)。它将导致我们无法建立一致的地图。你会发现原本直的走廊变成了斜的,而原本90 度的直角变成了歪的——这实在是一件很难令人忍受的事情!为了解决漂移问题,我们还需要两种技术:后端优化回环检测
回环检测负责把“机器人回到原始位置”的事情检测出来,而后端优化则根据该信息,校正整个轨迹的形状。

1.2 后端优化

后端优化主要指处理SLAM 过程中噪声的问题。。虽然我们很希望所有的数据都是准确的,然而现实中,再精确的传感器也带有一定的噪声。。后端优化要考虑的问题,就是如何从这些带有噪声的数据中,估计整个系统的状态,以及这个状态估计的不确定性有多大——这称为最大后验概率估计(Maximum-a-Posteriori,MAP)。这里的状态既包括机器人自身的轨迹,也包含地图。

相对的,视觉里程计部分,有时被称为“前端”。在SLAM 框架中,前端给后端提供
待优化的数据,以及这些数据的初始值。而后端负责整体的优化过程,它往往面对的只有
数据,不必关心这些数据到底来自什么传感器。在视觉SLAM 中,前端和计算机视觉研
究领域更为相关,比如图像的特征提取与匹配等,后端则主要是滤波与非线性优化算法。

1.3 回环检测

回环检测,又称闭环检测(Loop Closure Detection),主要解决位置估计随时间漂移的问题。怎么解决呢?假设实际情况下,机器人经过一段时间运动后回到了原点,但是由于漂移,它的位置估计值却没有回到原点。怎么办呢?我们想,如果有某种手段,让机器人知道“回到了原点”这件事,或者把“原点”识别出来,我们再把位置估计值“拉”过去,就可以消除漂移了。这就是所谓的回环检测。

为了实现回环检测,我们需要让机器人具有识别曾到达过的场景的能力。例如,我们可以判断图像间的相似性,来完成回环检测。

在检测到回环之后,我们会把“A 与B 是同一个点”这样的信息告诉后端优化算法。然后,后端根据这些新的信息,把轨迹和地图调整到符合回环检测结果的样子。这样,如果我们有充分而且正确的回环检测,就可以消除累积误差,得到全局一致的轨迹和地图。

1.4 建图

建图(Mapping)是指构建地图的过程。地图是对环境的描述,但这个描述并不是固定的,需要视SLAM 的应用而定。
在这里插入图片描述
对于地图,我们有太多的想法和需求。因此,相比于前面提到的视觉里程计、回环检测和后端优化,建图并没有一个固定的形式和算法。一组空间点的集合也可以称为地图,一个漂亮的3D 模型亦是地图,一个标记着城市、村庄、铁路、河道的图片亦是地图。地图的形式随SLAM 的应用场合而定。大体上讲,它们可以分为度量地图与拓扑地图两种。

度量地图(Metric Map)
度量地图强调精确地表示地图中物体的位置关系,通常我们用稀疏(Sparse)与稠密(Dense)对它们进行分类。稀疏地图进行了一定程度的抽象,并不需要表达所有的物体。例如,我们选择一部分具有代表意义的东西,称之为路标(Landmark),那么一张稀疏地图就是由路标组成的地图,而不是路标的部分就可以忽略掉。相对的,稠密地图着重于建模所有看到的东西。对于定位来说,稀疏路标地图就足够了。而用于导航时,我们往往需要稠密的地图(否则撞上两个路标之间的墙怎么办?)。稠密地图通常按照某种分辨率,由许多个小块组成。二维度量地图是许多个小格子(Grid),三维则是许多小方块(Voxel)。一般地,一个小块含有占据、空闲、未知三种状态,以表达该格内是否有物体。当我们查询某个空间位置时,地图能够给出该位置是否可以通过的信息。

拓扑地图(Topological Map)
相比于度量地图的精确性,拓扑地图则更强调地图元素之间的关系。拓扑地图是一个图(Graph),由节点和边组成,只考虑节点间的连通性,例如A,B 点是连通的,而不考虑如何从A 点到达B 点的过程。它放松了地图对精确位置的需要,去掉地图的细节问题,是一种更为紧凑的表达方式。然而,拓扑地图不擅长表达具有复杂结构的地图。

2. SLAM 问题的数学表述

  先来看运动。通常,机器人会携带一个测量自身运动的传感器,比如说码盘或惯性传感器。这个传感器可以测量有关运动的读数,但不一定直接是位置之差,还可能是加速度、角速度等信息。然而,无论是什么传感器,我们都能使用一个通用的、抽象的数学模型:
x k = f ( x k − 1 , u k , w k ) x_{k}=f(x_{k-1},u_{k},w_{k}) xk=f(xk1,uk,wk)这里 u k u_{k} uk 是运动传感器的读数(有时也叫输入), w k w_{k} wk 为噪声。注意到,我们用一个一般函数 f f f 来描述这个过程,而不具体指明 f f f的作用方式。这使得整个函数可以指代任意的运动传感器,成为一个通用的方程,而不必限定于某个特殊的传感器上。我们把它称为运动方程
  与运动方程相对应,还有一个观测方程。观测方程描述的是,当小萝卜在 x k x_{k} xk 位置上看到某个路标点 y j y_{j} yj ,产生了一个观测数据 z k , j z_{k,j} zk,j 。同样,我们用一个抽象的函数 h h h 来描述这个关系: z k , j = h ( y j , x k , v k , j ) z_{k,j}=h(y_{j},x_{k},v_{k,j}) zk,j=h(yj,xk,vk,j)   这里 v k , j v_{k,j} vk,j 是这次观测里的噪声。由于观测所用的传感器形式更多,这里的观测数据 z z z以及观测方程 h h h也许多不同的形式。
观测者的位姿(位置+姿态)由两个位置和一个转角来描述,即 x k = [ x , y , θ ] k T \bm{x_{k}}=\begin{bmatrix}x , y , \theta \end{bmatrix}_{k}^{T} xk=[x,y,θ]kT ,记姿态的变化量为 u k = [ Δ x , Δ y , Δ θ ] k T \bm{u_{k}}=\begin{bmatrix}\Delta x , \Delta y ,\Delta \theta \end{bmatrix}_{k}^{T} uk=[Δx,Δy,Δθ]kT 那此时的运动方程可以具体化为: [ x y z ] k = [ x y θ ] k − 1 + [ Δ x Δ y Δ θ ] k − 1 + w k \begin{bmatrix}x\\ y\\ z\end{bmatrix}_{k} =\begin{bmatrix}x\\ y\\ \theta\end{bmatrix}_{k-1} +\begin{bmatrix}\Delta x\\ \Delta y\\ \Delta \theta\end{bmatrix}_{k-1} +\bm{w}_{k} xyz k= xyθ k1+ ΔxΔyΔθ k1+wk这是简单的线性关系。不过,并不是所有的传感器都直接能测量出位移和角度变化,所以也存在着其他形式更加复杂的运动方程,那时我们可能需要进行动力学分析。SLAM过程可以总结为两个基本方程: { x k = f ( x k − 1 , u k , w k ) z k , j = h ( y j , x k , v k , j ) \left\{\begin{matrix} x_{k}=f(x_{k-1},u_{k},w_{k})\\ z_{k,j}=h(y_{j},x_{k},v_{k,j}) \end{matrix}\right. {xk=f(xk1,uk,wk)zk,j=h(yj,xk,vk,j)这两个方程描述了最基本的SLAM 问题:当我们知道运动测量的读数 u u u,以及传感器的读数 z z z 时,如何求解定位问题(估计 x \bm{x} x)和建图问题(估计 y \bm{y} y)?这时,我们把SLAM问题建模成了一个状态估计问题:如何通过带有噪声的测量数据,估计内部的、隐藏着的状态变量?

3. 编程基础

直接写习惯的编程方法示例

在文件夹下创建helloSLAM.cpp和CmakeLists.txt

// helloSLAM.cpp
#include<iostream>
using namespace std;

int main()
{
	cout<<"Hello SLAM!"<<endl;
	return 0;
}
// CmakeLists.txt
cmake_minimum_required(VERSION 2.8)

project(HelloSLAM)

add_executable(helloSLAM helloSLAM.cpp)

在这里插入图片描述
依次执行

mkdir build
cd build
cmake ..
make

在这里插入图片描述

使用库的情况
首选创建一个 libHelloSLAM.cpp

#include<iostream>
using namespace std;

void printHello()
{
	cout<<"Hello SLAM!"<<endl;
}

创建头文件libHelloSLAM.h

#ifndef LIBHELLOSLAM_H_
#define LIBHELLOSLAM_H_
void printHello();
#endif

创建主文件useHello.cpp

#include "libHelloSLAM.h"

int main()
{
	printHello();
	return 0;
}

创建CMakeLists.txt
这里使用的是共享库(没有使用静态库,因为静态库每次运行都会生成一个副本,共享库只有一个副本)

cmake_minimum_required(VERSION 2.8)

project(HelloSLAM)

add_library(hello_shared SHARED libHelloSLAM.cpp)

add_executable(useHello useHello.cpp)
target_link_libraries(useHello hello_shared)

按照之前的方法执行一遍
在这里插入图片描述

使用IDE
如何用集成开发环境(Integrated Development Environment, IDE)

sudo apt-get install kdevelop

kdevelop 具体使用参考链接:
https://my.oschina.net/u/4337224/blog/3355903

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玛卡巴卡_qin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值