• 【slam十四讲第二版】【课本例题代码向】【第二讲初识SLAM】【SLAM基础知识】【linux下C++编译】【cmake基础使用】


    0 前言

    经典开始

    1 SLAM是什么

    • 自主运动的两大基本问题(p14)
    1. 我在什么地方?-定位
    2. 周围环境是什么样子?-建图
    3. 定位与建图=内外兼修 定位侧重对自身的了解,建图侧重对外在的了解
    • 相互关联
    1. 准确的定位需要精确的地图
    2. 精确的地图来自准确的定位

    2 传感器

    1. 机器人感知外界环境的手段
    2. 种类:内置的/外置的
      内置:感受机器人本体信息;IMU、激光、相机
      外置:安装于环境中的;二维码Marker、GPS、导轨
    • 环境限制了传感器的形式
    1. GPS:需要接受到卫星信号的环境
    2. Marker、导轨:需要环境允许安装
    • 相比之下,激光、相机等携带式传感器更加自由,没有对环境提出任何要求,从而使得这种定位方案可适用于未知环境。
    1. 轮式编码器:测量轮子转动的角度
    2. IMU:测量运动的角速度和加速度
    3. 相机和激光传感器:读取外部环境的某种观测数据

    3 相机

    • 相机
      以一定速率采集图像、形成视频
    • 分类
    1. 单目相机 Monocular:仅一个摄像头
    2. 双目相机(立体相机)Stereo:两个摄像头
    3. 深度相机 RGB-D:多个摄像头,除了采集到彩色照片,还可以读出每个像素与相机之间的距离。
    4. 其他 全景、Event Camera
    • 相机的特点
    1. 以二维投影形式记录了三维世界的信息
    2. 该过程丢掉了一个维度:距离
    • 各类相机的区别
    1. 单目:无深度 深度需要其他手段估计
    2. 双目:通过视差计算深度
    3. RGB-D:通过物理方法测量深度

    3.1 单目相机 Monocular

    • 特点:结构简单、成本低
    • 以二维的形式记录了三维的世界,这个过程中丢掉了深度(距离)信息。
    • 单目SLAM中,必须移动相机,才能估计它的运动(motion),同时估计场景中物体的远近和大小。
    • 视差(Disparity):物体在图像上的运动形成了视差,通过视差判断物体的远近,此为一个相对的值。
    • 尺度(Scale):单目SLAM估计的轨迹和地图将与真实的轨迹和地图相差一个因子,也就是所谓的尺度。
    • 尺度不确定性(Scale Ambiguity):单目SLAN无法仅凭图像确定真实尺度的性质。
    • 缺点:平移之后才可以计算深度,以及无法确定真实尺度;根本原因是通过单张图片无法确定深度。

    3.2 双目相机 Stereo

    • 使用目的:通过某种手段测量物体与相机之间的距离,克服单目相机无法知道距离的缺点;一旦知道距离,场景的三维结构就可以通过单个图像恢复,同时消除尺度不确定性。
    • 基线(Baseline):双目相机的两个单目相机之间的距离,通过这个距离可以估计每个像素的空间位置。
    • 双目相机测量到的深度范围与基线相关;基线距离越大,能够测量到的物体就越远,这也是无人车搭载的双目相机很大的原因。
    • 缺点:配置与标定较为复杂;其深度量程和精度受双目的基线与分辨率所限,而且视差的计算非常消耗资源,需要GPU和FPGA加速,才可以实时输出整张图象的距离信息。

    3.3 深度相机(RGB-D相机)

    • 2010年前后兴起
      特点:通过红外结构光或Time-of-Flight(ToF)原理,像激光传感器那样,通过主动向物体发射光并接受返回的光,测出物体与相机之间的距离。硬件解决距离,节省计算资源。
      缺点:测量范围窄,噪声大、视野小、易受日光干扰、无法测量透射材质等诸多问题
      应用:主要用于室内,室外较难应用。

    4 经典视觉SLAM框架(p19)

    在这里插入图片描述

    • 整个SLAM流程包括:
    1. 传感器信息读取:在视觉SLAM中主要为相机图像信息的读取和预处理;机器人中,还可能有码盘、惯性传感器等信息的读取和同步。
    2. **前端视觉里程计(Visual odometry,VO):**视觉里程计的任务是估算相邻图像间相机的运动,以及局部地图的样子。VO又称为前端(Front End)。
    3. 后端(非线性)优化(Optimization):后端接受不同时刻视觉里程计测量的相机位姿,以及回环检测的信息,对他们进行优化,得到全局一致的轨迹和地图。由于接在VO之后,又称为后端(Back)
    4. **回环检测(Loop Closure Detection):**回环检测判断机器人是否到达过先前的位置,如果检测到回环,它会把信息提供给后端进行处理。
    5. **建图(Mapping):**根据估计的轨迹,建立与任务要求对应的地图。
    • 如果把环境限定在静态、刚体、光照变换不明显、没有人为干扰的场景,那么这种场景下SLAM技术已经相当成熟。

    4.1 视觉里程计

    • 仅凭视觉里程计估计轨迹会有累积漂移(Accumulating Drift),为了解决漂移,需要后端优化和回环检测。回环检测负责把“机器人回到原始位置”的事情检测出来,而后端优化则根据该信息,校正整个轨迹的形状。

    4.2 后端优化

    • 笼统地说,后端优化主要指处理SLAM过程中的噪声问题。
    • 最大后验概率估计(Maximum-a-Posteriori,MAP):后端优化要考虑的问题,就是如何从这些带有噪声的数据中估计整个系统的状态,以及这个状态估计的不确定性有多大。
    • 后端负责整体的优化过程,它往往面对的只有数据,不必关心这些数据到底来自什么传感器。
    • 在视觉SLAM中,前端和计算机视觉研究领域更为相关,比如图像的特征提取与匹配等,后端则主要是滤波与非线性优化算法。
    • SLAM问题的本质:对运动主题自身和周围环境空间不确定性的估计。

    4.3 回环检测

    • 通过二维码、判断图像间的相似性来完成回环检测,检测到回环之后,会把“A与B是同一个点”这样的信息告诉后端优化算法,然后后端根据这些新的信息,把轨迹和地图调整到符合回环检测结果的样子,这样就可以消除累积误差,得到全局一致的轨迹和地图

    4.5 建图

    4.5.1 度量地图(Metric Map)

    • 度量地图强调精确地表示地图中物体的位置关系,通常用稀疏(Sparse)与稠密(Dense)对其分类。
    1. 稀疏地图(Sparse)进行了一定程度的抽象,并不需要表达全部的物体。选择一部分具有代表意义的东西,称之为路标(Landmark),那么一张稀疏地图就是由路标组成的地图,而不是路标的部分就可以忽略。
    2. 稠密地图(Dense)着重于建模所有可以看到的东西。二位度量地图中的小格子(Grid),三维度量地图中的小方块(Voxel),一个小块有占据、空闲、未知三种状态,以表示该格内是否有物体,当查询某个空间位置时,地图能够给出该位置是否可以通过的信息。可用于各种导航算法,如A*、D*等。
      缺点是:但是会消耗大量的存储空间,而且多数信息都是无用的;在大规模建图的时候,会出现一致性问题,很小的一点转向误差,可能会导致两间屋子的墙出现重叠,使地图失效。
    • 定位时用稀疏地图就足够了;但是导航时需要稠密地图

    4.5.2 拓扑地图(Topological Map)

    • 相比于度量地图的精确性,拓扑地图更强调地图元素之间的关系。拓扑地图是一个图(Graph),由节点和边组成,只考虑节点间的连通性。放松了地图对精度位置的要求,去掉了地图的细节,是一种更为紧凑的表达方式。
    • 然而拓扑地图不擅长表达具有复杂结构的地图,如何对地图进行分割,形成节点与边,又如何使用拓扑地图进行导航与路径规划,仍是待研究的问题。

    5 SLAM问题的数学表述(p24)

    • 运动方程
    • 观测方程

    6 实践:编程基础

    6.1 安装linux操作系统

    虚拟机参考:【第一天】【ROS操作系统】【1】Ubuntu安装方法(虚拟机-18.04)
    双系统参考:Windows + Ubuntu 双系统(超详细图文教程)

    6.2 Hello SLAM

    1. 首先安装g++,也可以用clang,但这里没有用,比g++好一点
    sudo apt-get install g++
    
    • 1
    1. 创建并打开一个文件main.cpp
    touch main.cpp
    
    vim main.cpp
    
    • 1
    • 2
    • 3
    1. 然后写入代码,如下:
    #include <iostream>
    using namespace std;
    
    
    int main(int argc,char argv)
    {       
            cout << "Hello SLAM!" << endl;
           return 0;
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 编译
    g++ main.cpp
    
    g++ -o main main.cpp
    
    • 1
    • 2
    • 3

    会生成a.out文件(默认命令,指定main),其具有执行权限(终端里面颜色不同,深色)

    1. 运行
    bupo@bupo-vpc:~/shenlan/slam14_my/cap1/c_bian_yi$ ./a.out 
    Hello SLAM!
    
    
    • 1
    • 2
    • 3

    6.3 使用cmake

    1. 安装cmake
    sudo apt-get install cmake
    
    • 1
    1. 创建CMakeLists.txt
    touch CMakeLists.txt
    
    • 1
    1. CMakeLists.txt中写入内容,如下:
    #声明要求的cmake最低版本
    cmake_minimum_required(VERSION 2.8)
    
    # 声明一个cmake工程
    project(HelloSLAM)
    
    # 添加一个可执行程序
    # 语法:add_executable(程序名 源代码文件)
    add_executable(helloSLAM main.cpp)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 编译
    • 进行编译,并指定所生成中间文件的位置为当前目录下,其中MakeFile最重要,但不必修改它;cmake处理了工程文件之间的关系
    cmake .
    
    • 1
    • 编译生成可执行文件helloSLAM,实际上调用了g++来编译程序
    make
    
    • 1
    1. 运行
    ./helloSLAM 
    Hello SLAM!
    
    • 1
    • 2
    • 通常为了优化,我们会把中间文件放进build文件夹中,则上述过程就变味了从第四步开始为4.
    mkdir build
    cd build
    cmake ..
    make
    
    • 1
    • 2
    • 3
    • 4

    6.4 使用库

    • 只有带main函数的文件会生成可执行文件
    • 而库(library),是被打包为一个东西,供其他程序调用。

    6.4.1 编写自己的库(纯学习,无用处)

    • 下面演示如何自己编写一个库
    1. 创建主文件夹
    mkdir hello_ku
    cd hello_ku
    mdkir src
    
    • 1
    • 2
    • 3
    1. 创建libHelloSLAM.cpp
    cd src
    touch libHelloSLAM.cpp
    
    • 1
    • 2

    内容如下:

    //这是一个库文件
    #include <iostream>
    using namespace std;
    
    void printHello()
    {
            cout << "Hello SLAM !"<< endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 创建CMakeLists.txt文件
    touch CMakeLists.txt
    
    • 1

    内容如下:

    #静态库
    add_library(hello src/libHelloSLAM.cpp)
    
    #动态库
    #add_library(hello_shared SHARED src/libHelloSLAM.cpp)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    静态库:静态库以.a作为后缀名,每次被调用都会生成一个副本
    共享库:共享库以.so结尾,只有一个副本,更省空间

    1. 编译库
    mkdir build
    cd build
    cmake ..
    make
    
    • 1
    • 2
    • 3
    • 4
    1. 为了更好的使用,提供头文件libHelloSLAM.h
    cd src
    touch libHelloSLAM.h
    
    • 1
    • 2

    内容如下:

    #ifndef LIBHELLOSLAM_H_
    #define LIBHELLOSLAM_H_
    //上面的宏定义是为了防止重复引用这个头文件而引起的重定义错>误
    //打印一句hello的函数
    void printHello();
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    6.4.2 调用自己的库(在工程include中的,不是下载到系统中的)

    1. 创建文件夹
    mkdir hello_diaoyong_ku
    cd hello_diaoyong_ku
    mkdir src
    
    • 1
    • 2
    • 3
    1. 创建main文件
    cd src
    mkdir useHello.cpp
    
    • 1
    • 2

    内容如下:

    printHello()函数
    int main(int argc,char ** argv)
    {
    	printHello();
    	return 0;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 在include文件夹中创建库
    mkdir include
    cd include
    touch libHelloSLAM.cpp
    
    • 1
    • 2
    • 3

    内容如下:

    
    #include "libHelloSLAM.h"
    #include <iostream>
    using namespace std;
    
    void printHello()
    {
    	cout << "Hello SLAM !"<< endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    创建头文件:

    touch libHelloSLAM.h
    
    • 1

    内容如下:

    #ifndef LIBHELLOSLAM_H_
    #define LIBHELLOSLAM_H_
    //上面的宏定义是为了防止重复引用这个头文件而引起的重定义错误
    //打印一句hello的函数
    void printHello();
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 创建CMakeLists.txt文件
    mkdir CMakeLists.txt
    
    • 1

    内容如下:

    cmake_minimum_required(VERSION 2.8)
    project(useHello)
    
    include_directories("include")#说明include是作为一个引用头文件的地方
    add_library(hello include/libHelloSLAM.cpp)
    
    add_executable(useHello src/useHello.cpp)
    target_link_libraries(useHello hello)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 编译
    mkdir build
    cd build
    cmake ..
    make
    
    • 1
    • 2
    • 3
    • 4
    1. 运行
    cd build
    ./useHello
    
    • 1
    • 2
  • 相关阅读:
    欢乐钓鱼大师一键钓鱼,解放双手!
    SpringBoot SpringBoot 运维实用篇 3 多环境开发 3.2 多环境开发多文件版【yaml版】
    Jmeter之连接数据库的使用步骤有哪些?
    树形dp实例-Orgrimmar
    ADB 安装 + 打驱动全教程
    论文学习:RT-DETR
    计算机毕业设计django基于python的高校奖学金管理系统(源码+系统+mysql数据库+Lw文档)
    基于Python实现的银行信息处理系统
    什么是物联网阀控水表?
    SpringBoot:MVC自动配置原理
  • 原文地址:https://blog.csdn.net/qq_45954434/article/details/125473893