• 回环检测


    1.回环检测的必要性
    因为累积误差,最后会使地图出现漂移。比如之前位姿图优化,只给后端提供相邻帧之间的约束,x1-x2,x2-x3,x1的误差就会传到x3.而回环检测能够给出时隔更久远的约束,比如x1-x100,它做的事就是检测相机经过了同一个地方,把带有累积误差的边拉回到了正确的位置。
    回环检测提供了当前数据与历史数据之间的关联,一是可以保证轨迹和地图长时间准确,二是如果跟踪算法丢失,可以用它来做重定位。
    前端和局部后端只是VO,VO带回环检测和全局后端才是slam.
    2.实现方法
    2.1简单的思路
    2.1.1 任意匹配
    对任意两幅图像做一遍特征匹配,根据正确匹配的数量确定哪两幅图像存在关联。这是假设了任意两幅图像都有可能存在回环。
    有效,但检测数量太多,对于N个可能的回环,要检测Cn 2,是O(N*N)的复杂度。如果是图像的话,每秒就30帧呢,就算是关键帧,也不少。
    2.1.2随机抽取
    从N帧中随机抽取5帧与当前帧进行比较,之前有这么做来做特征匹配吧。运算量不高,但N帧数增长时,抽到回环的几率不高,检测的效率不高。
    2.2基于哪处可能出现回环的预计
    2.2.1基于里程计的几何关系
    有点倒果为因了,在累积误差较大时不能工作。
    2.2.2基于外观
    这个是视觉slam主流的做法,它能够有效地在不同场景下工作,并已经应用在实际的系统中了。它跟前端和后端都没有关系,仅靠两幅图像之间的相似性来确定回环检测关系,这样就摆脱了累积误差,这样回环检测就是slam中一个相对独立的模块(当然前端会给它提供特征点)。
    2.2.2.1计算图像之间的相似性
    这是基于外观的核心问题。比如图像A,B。相似性评分记为s(A,B).但是
    s(A,B)=||A-B||不行,因为
    (1)像素灰度不稳定,受环境光照和相机曝光的影响,即使相机未动,光照变化了,或者打开一个电灯,那么图像整体变亮,对于同样的数据,A和B的差异值却会很大。
    (2)相机视角微变,即使每个物体光度不变,它们的像素也会在图像中发生位移,差异值也会很大。
    主要的问题就是,对于非常相似的图像,A和B的差异值也会很大。
    评价s(A,B)的指标,准确率,召回率。
    2.2.2.2准确率和召回率
    对回环检测的结果进行分类
    算法是回环,事实是,真阳性。
    算法是,事实不是,假阳性,又称感知偏差(perceptual aliasing)
    算法不是,事实是,假阴性,又称感知变异(perceptual variability)
    算法不是,事实不是,真阴性。
    想象一个2*2矩阵,算法是都是阳性。阳阴性是根据算法是与不是来判断的。
    TP,TN,FP,FN.希望是TP,TN尽量高,FP,FN尽量低。
    准确率TP/(TP+FP),召回率 TP/(TP+FN),都是TP来除,一个是算法检测出来的回环总数,一个是真实的回环总数。
    假阳性:两幅图看起来很像,但不是同一个走廊,就是算法是,事实不是。
    假阴性:两幅图像是,但是由于光照,图片不一样。就是算法不是,事实是。
    准确率:算法提取的所有回环中确实是真实回环的概率
    召回率:所有真实回环被检测出来的概率。
    是一对矛盾来着。
    一个算法有很多设置参数,调整参数,比如提高,算法严格,检出更少回环,准确率高了,但是召回率下降。如果宽松,召回率高了,准确率下降。
    评价算法,就测试它在各种配置下的P和R值,画出Precision-Recall曲线,以召回率为横轴,准确率为纵轴,是一个向下的一个曲线。
    SLAM对准确率更严格,对召回率则宽松一点。因为假阳性(检测是而实际不是)回环会在pose graph中添加错误的边,优化算法可能会给出完全错误的结果。想象一下,slam程序把所有的办公桌当成同一张,则走廊不直了,墙壁被交错在一起了,最后整个地图失效。召回率低一些,则顶多有部分的回环没有检测到,地图可能会受一些累积误差的影响,然而仅需一两次回环就可以完全消除它们了。
    所以选择回环检测算法,倾向于把参数设置得更严格,或者在检测之后再加上回环验证。
    A-B的准确率和召回率都不高。所以不用。

    12.2词袋模型
    词袋,就是bag of words,就是BoW.
    词袋模型:首先由单词,单词组成字典,图像就可用字典描述,就有一个向量出现。字典是无序的,这个无序是指就管它出不出现,而不管它在哪出现。而且可以用单纯的0,1表示是否出现,也可以用数量表示出现了几次。这就可以称它为词袋模型。
    计算相似度就可以用这个向量。
    如果字典有W个单词,或者说a,b属于R W,它把相似度就表示为
    s(a,b)=1-1/W||a-b||
    这里范数用的是L1范数,所谓的L1范数是所有项的绝对值的和。
    12.3单词怎么来
    单词是聚类得到的,对图像特征进行聚类,现在聚类还是用的是orb特征,用orb特征的描述子聚的类。
    首先用k-means聚类
    (1)随机选取k个中心点,c1,c2,..,ck
    (2)对剩下来的点,或者说对所有的点,计算它们与中心的点的距离,把它归到与它距离最小的那个类里面;
    (3)重新计算每个类的中心点
    (4)如果这一次相比上一次中心点的变化很小,说明算法收敛,否则继续执行(2)(3)(4)。
    问题:K要指定,由于刚开始中心点是随机选取的,随机选取的结果不同,造成最后聚类的结果也不同。
    这里用的k叉树,是k-means聚类的扩展
    (1)根节点,用k-means得到第一层(实际中用k-means++,因为它聚类均匀,而k-means又可能不均匀),有没有可能会有一个类什么都没有呢?
    (2)对第一层的每个节点,再分成k类,得到下一层,直到达到深度d
    最后一层是叶节点,也就是单词了,单词总共有k的d次方个。
    查找的时候,只要与每个节点的聚类中心比较即可,总共比较d次就可以了。奥,比如在第一层,确定它在第1类,然后从第一类再往下找,就不用管后面的9类了。
    实践:生成字典相当容易,假设文件夹data里面放的是图像数据(彩色图),那么令
    string path="../data/"+to_string(i+1)+".png",然后用imread读取之后把这些图像矩阵都放到images里。
    提取的是ORB特征,提取之后生成关键点和描述子,描述子依次放入描述子矩阵descriptors.
    定义字典DBoW3::Vocabulary vocab;这里是默认了k=10,d=5,可以不使用默认,自己定义。
    使用DBoW3要先下载它,git clone https://github.com/rmsalinas/DBoW3,或者直接从书中的3rdparty文件中找到。编译它. mkdir build ,cd build,cmake ..,make,sudo make install
    在CMakeLists.txt文件夹里要加
    set( DBoW3_INCLUDE_DIRS "/usr/local/include" )
    set( DBoW3_LIBS "/usr/local/lib/libDBoW3.a" )
    可执行程序后面要添加它的库。
    add_executable( feature_training feature_training.cpp )
    target_link_libraries( feature_training ${OpenCV_LIBS} ${DBoW3_LIBS} )
    程序中要include它的h文件,即#include "DBoW3/DBoW3.h"
    只有定义了字典vocab,调用它的create函数,字典就创建好了。
    vocab.create(descriptors);
    然后把它保存成一个压缩文件,就可以在另外一个程序中直接使用这个字典了。
    12.4相似度计算
    TF_IDF

    IDF就是建立字典的时候,因为是按所有的特征分的,所以单词wi下的特征数量是不相同的,毕竟wi是一个类,所以wi下的特征数量和所有特征数量相比,也算是wi的一个特征。所以IDF这里定义为IDFi=log(n/ni),因为认为特征数量越多,对图像的区分度越不高。建立字典的时候IDFi就可以求出来了。
    TF就是对单幅图像A来说的,单词wi出现了ni次,而所有单词出现的次数为n,那么TF为
    TFi=ni/n
    而tf-idf里面所说的权重就是TFi*IDFi,记为gai.
    现在词袋模型就变成了
    vA=[ga1,ga2,..,gan],它是一个稀疏的向量。那么相似度这里是这么计算的
    单项2*(|vAi|+|vBi|-|vAi-vBi|)
    s(vA-vB)就为这些项的和。
    这里是对vA,vB的每一项,哪个小就取哪个,把这些值再加起来,就是相似度?
    实践:看回环
    上面已经创建好字典文件了,是一个.yml.gz文件,直接DBoW3::Vocabulary vocab("这个文件")就可以直接用这个字典了。然后还是读图保存到images,提取每个图像的描述子保存到descriptors.
    对于每个图像来说,定义词袋向量DBoW3::BowVector v1;然后字典可以直接把描述子的每一项转成为一个词袋向量。
    vocab.transform(descriptors[i],v1);
    得到v1,v2,用vocab的score函数就可以计算出它们的相似度了。
    double score=vocab.score(v1,v2);
    另一种就是先用字典和所有的描述子创建一个数据库,用数据库去比,而且会返回给跟每个图像最相似的几个图像。
    创建数据库DBoW3::Database db(vocab,false,0);
    数据库添加描述子的每一项就可以了
    db.add(descriptors[i]);
    然后比的时候直接db.query(descriptors[i],ret,4)就可以了,结果返回给ret,4是返回数量
    ret格式如下:
    DBoW3::QueryResults ret;
    问题:认为最相似的图1和图10,也只有0.0525.太低了。
    解决:创建大字典,然后用大字典来做。
    结果:无关图像的相似性变小,使有关图像的相似性变得更显著。能够更好地区分了,但score是都下降了。
    12.5.2相似性评分的处理
    只用score不太好,因为环境不一样,比如办公室环境往往用同款式的桌椅,而另一些环境则每个地方都不一样。所以这里可以取一个先验相似度,某时刻关键帧与它上一时刻的关键帧的相似度
    s(vt,vt-deltat),那vt的其他分值都参照这个来归一化,就是
    s(vt,vtj)'=s(vt,vtj)/s(vt,vt-deltat)
    如果s(vt,vtj)'>3,认为vt,vtj之间可能存在回环。
    12.5.4处理关键帧
    检测回环时,关键帧不能选择太近,否则检测出第n帧和n-1帧,n-2帧存在回环并没有什么意义。
    检测出第1帧和第n帧,有可能第一帧和n-2帧也存在回环,这时候要把相近的回环聚成一类,怎么聚没说。
    12.5.5验证回环
    因为词袋不在意单词顺序,只在乎有没有出现,所以感知偏差是很容易出现的。所以要验证。
    (1)回环缓存,单次检测到不认为它是,在一段时间内一直检测到认为它是。时间上的一致性。
    (2)把回环检测出来的帧进行特征匹配,估计运动,估计完运动后放入位姿图,看跟之前的估计是否有很大出入。是有算呢,还是没有算呢?
    12.5.6与机器学习的关系
    回环检测类似于分类,而且回环中类别数量大,每类的样本数量很少,每当机器运动,图像发生变化,就会产生新的类,回环检测相当于两幅图像落入同一类。图像间相似性概念的一个学习。
    改进方向
    (1)对机器学习的图像特征进行聚类,而不是人为设计的ORB特征;
    (2)用更好的方法进行聚类,而不是k叉树这种朴素的方式。
    词袋模型会被机器学习给替代的。


    1.回环检测的必要性
    因为累积误差,最后会使地图出现漂移。比如之前位姿图优化,只给后端提供相邻帧之间的约束,x1-x2,x2-x3,x1的误差就会传到x3.而回环检测能够给出时隔更久远的约束,比如x1-x100,它做的事就是检测相机经过了同一个地方,把带有累积误差的边拉回到了正确的位置。
    回环检测提供了当前数据与历史数据之间的关联,一是可以保证轨迹和地图长时间准确,二是如果跟踪算法丢失,可以用它来做重定位。
    前端和局部后端只是VO,VO带回环检测和全局后端才是slam.
    2.实现方法
    2.1简单的思路
    2.1.1 任意匹配
    对任意两幅图像做一遍特征匹配,根据正确匹配的数量确定哪两幅图像存在关联。这是假设了任意两幅图像都有可能存在回环。
    有效,但检测数量太多,对于N个可能的回环,要检测Cn 2,是O(N*N)的复杂度。如果是图像的话,每秒就30帧呢,就算是关键帧,也不少。
    2.1.2随机抽取
    从N帧中随机抽取5帧与当前帧进行比较,之前有这么做来做特征匹配吧。运算量不高,但N帧数增长时,抽到回环的几率不高,检测的效率不高。
    2.2基于哪处可能出现回环的预计
    2.2.1基于里程计的几何关系
    有点倒果为因了,在累积误差较大时不能工作。
    2.2.2基于外观
    这个是视觉slam主流的做法,它能够有效地在不同场景下工作,并已经应用在实际的系统中了。它跟前端和后端都没有关系,仅靠两幅图像之间的相似性来确定回环检测关系,这样就摆脱了累积误差,这样回环检测就是slam中一个相对独立的模块(当然前端会给它提供特征点)。
    2.2.2.1计算图像之间的相似性
    这是基于外观的核心问题。比如图像A,B。相似性评分记为s(A,B).但是
    s(A,B)=||A-B||不行,因为
    (1)像素灰度不稳定,受环境光照和相机曝光的影响,即使相机未动,光照变化了,或者打开一个电灯,那么图像整体变亮,对于同样的数据,A和B的差异值却会很大。
    (2)相机视角微变,即使每个物体光度不变,它们的像素也会在图像中发生位移,差异值也会很大。
    主要的问题就是,对于非常相似的图像,A和B的差异值也会很大。
    评价s(A,B)的指标,准确率,召回率。
    2.2.2.2准确率和召回率
    对回环检测的结果进行分类
    算法是回环,事实是,真阳性。
    算法是,事实不是,假阳性,又称感知偏差(perceptual aliasing)
    算法不是,事实是,假阴性,又称感知变异(perceptual variability)
    算法不是,事实不是,真阴性。
    想象一个2*2矩阵,算法是都是阳性。阳阴性是根据算法是与不是来判断的。
    TP,TN,FP,FN.希望是TP,TN尽量高,FP,FN尽量低。
    准确率TP/(TP+FP),召回率 TP/(TP+FN),都是TP来除,一个是算法检测出来的回环总数,一个是真实的回环总数。
    假阳性:两幅图看起来很像,但不是同一个走廊,就是算法是,事实不是。
    假阴性:两幅图像是,但是由于光照,图片不一样。就是算法不是,事实是。
    准确率:算法提取的所有回环中确实是真实回环的概率
    召回率:所有真实回环被检测出来的概率。
    是一对矛盾来着。
    一个算法有很多设置参数,调整参数,比如提高,算法严格,检出更少回环,准确率高了,但是召回率下降。如果宽松,召回率高了,准确率下降。
    评价算法,就测试它在各种配置下的P和R值,画出Precision-Recall曲线,以召回率为横轴,准确率为纵轴,是一个向下的一个曲线。
    SLAM对准确率更严格,对召回率则宽松一点。因为假阳性(检测是而实际不是)回环会在pose graph中添加错误的边,优化算法可能会给出完全错误的结果。想象一下,slam程序把所有的办公桌当成同一张,则走廊不直了,墙壁被交错在一起了,最后整个地图失效。召回率低一些,则顶多有部分的回环没有检测到,地图可能会受一些累积误差的影响,然而仅需一两次回环就可以完全消除它们了。
    所以选择回环检测算法,倾向于把参数设置得更严格,或者在检测之后再加上回环验证。
    A-B的准确率和召回率都不高。所以不用。

    12.2词袋模型
    词袋,就是bag of words,就是BoW.
    词袋模型:首先由单词,单词组成字典,图像就可用字典描述,就有一个向量出现。字典是无序的,这个无序是指就管它出不出现,而不管它在哪出现。而且可以用单纯的0,1表示是否出现,也可以用数量表示出现了几次。这就可以称它为词袋模型。
    计算相似度就可以用这个向量。
    如果字典有W个单词,或者说a,b属于R W,它把相似度就表示为
    s(a,b)=1-1/W||a-b||
    这里范数用的是L1范数,所谓的L1范数是所有项的绝对值的和。
    12.3单词怎么来
    单词是聚类得到的,对图像特征进行聚类,现在聚类还是用的是orb特征,用orb特征的描述子聚的类。
    首先用k-means聚类
    (1)随机选取k个中心点,c1,c2,..,ck
    (2)对剩下来的点,或者说对所有的点,计算它们与中心的点的距离,把它归到与它距离最小的那个类里面;
    (3)重新计算每个类的中心点
    (4)如果这一次相比上一次中心点的变化很小,说明算法收敛,否则继续执行(2)(3)(4)。
    问题:K要指定,由于刚开始中心点是随机选取的,随机选取的结果不同,造成最后聚类的结果也不同。
    这里用的k叉树,是k-means聚类的扩展
    (1)根节点,用k-means得到第一层(实际中用k-means++,因为它聚类均匀,而k-means又可能不均匀),有没有可能会有一个类什么都没有呢?
    (2)对第一层的每个节点,再分成k类,得到下一层,直到达到深度d
    最后一层是叶节点,也就是单词了,单词总共有k的d次方个。
    查找的时候,只要与每个节点的聚类中心比较即可,总共比较d次就可以了。奥,比如在第一层,确定它在第1类,然后从第一类再往下找,就不用管后面的9类了。
    实践:生成字典相当容易,假设文件夹data里面放的是图像数据(彩色图),那么令
    string path="../data/"+to_string(i+1)+".png",然后用imread读取之后把这些图像矩阵都放到images里。
    提取的是ORB特征,提取之后生成关键点和描述子,描述子依次放入描述子矩阵descriptors.
    定义字典DBoW3::Vocabulary vocab;这里是默认了k=10,d=5,可以不使用默认,自己定义。
    使用DBoW3要先下载它,git clone https://github.com/rmsalinas/DBoW3,或者直接从书中的3rdparty文件中找到。编译它. mkdir build ,cd build,cmake ..,make,sudo make install
    在CMakeLists.txt文件夹里要加
    set( DBoW3_INCLUDE_DIRS "/usr/local/include" )
    set( DBoW3_LIBS "/usr/local/lib/libDBoW3.a" )
    可执行程序后面要添加它的库。
    add_executable( feature_training feature_training.cpp )
    target_link_libraries( feature_training ${OpenCV_LIBS} ${DBoW3_LIBS} )
    程序中要include它的h文件,即#include "DBoW3/DBoW3.h"
    只有定义了字典vocab,调用它的create函数,字典就创建好了。
    vocab.create(descriptors);
    然后把它保存成一个压缩文件,就可以在另外一个程序中直接使用这个字典了。
    12.4相似度计算
    TF_IDF

    IDF就是建立字典的时候,因为是按所有的特征分的,所以单词wi下的特征数量是不相同的,毕竟wi是一个类,所以wi下的特征数量和所有特征数量相比,也算是wi的一个特征。所以IDF这里定义为IDFi=log(n/ni),因为认为特征数量越多,对图像的区分度越不高。建立字典的时候IDFi就可以求出来了。
    TF就是对单幅图像A来说的,单词wi出现了ni次,而所有单词出现的次数为n,那么TF为
    TFi=ni/n
    而tf-idf里面所说的权重就是TFi*IDFi,记为gai.
    现在词袋模型就变成了
    vA=[ga1,ga2,..,gan],它是一个稀疏的向量。那么相似度这里是这么计算的
    单项2*(|vAi|+|vBi|-|vAi-vBi|)
    s(vA-vB)就为这些项的和。
    这里是对vA,vB的每一项,哪个小就取哪个,把这些值再加起来,就是相似度?
    实践:看回环
    上面已经创建好字典文件了,是一个.yml.gz文件,直接DBoW3::Vocabulary vocab("这个文件")就可以直接用这个字典了。然后还是读图保存到images,提取每个图像的描述子保存到descriptors.
    对于每个图像来说,定义词袋向量DBoW3::BowVector v1;然后字典可以直接把描述子的每一项转成为一个词袋向量。
    vocab.transform(descriptors[i],v1);
    得到v1,v2,用vocab的score函数就可以计算出它们的相似度了。
    double score=vocab.score(v1,v2);
    另一种就是先用字典和所有的描述子创建一个数据库,用数据库去比,而且会返回给跟每个图像最相似的几个图像。
    创建数据库DBoW3::Database db(vocab,false,0);
    数据库添加描述子的每一项就可以了
    db.add(descriptors[i]);
    然后比的时候直接db.query(descriptors[i],ret,4)就可以了,结果返回给ret,4是返回数量
    ret格式如下:
    DBoW3::QueryResults ret;
    问题:认为最相似的图1和图10,也只有0.0525.太低了。
    解决:创建大字典,然后用大字典来做。
    结果:无关图像的相似性变小,使有关图像的相似性变得更显著。能够更好地区分了,但score是都下降了。
    12.5.2相似性评分的处理
    只用score不太好,因为环境不一样,比如办公室环境往往用同款式的桌椅,而另一些环境则每个地方都不一样。所以这里可以取一个先验相似度,某时刻关键帧与它上一时刻的关键帧的相似度
    s(vt,vt-deltat),那vt的其他分值都参照这个来归一化,就是
    s(vt,vtj)'=s(vt,vtj)/s(vt,vt-deltat)
    如果s(vt,vtj)'>3,认为vt,vtj之间可能存在回环。
    12.5.4处理关键帧
    检测回环时,关键帧不能选择太近,否则检测出第n帧和n-1帧,n-2帧存在回环并没有什么意义。
    检测出第1帧和第n帧,有可能第一帧和n-2帧也存在回环,这时候要把相近的回环聚成一类,怎么聚没说。
    12.5.5验证回环
    因为词袋不在意单词顺序,只在乎有没有出现,所以感知偏差是很容易出现的。所以要验证。
    (1)回环缓存,单次检测到不认为它是,在一段时间内一直检测到认为它是。时间上的一致性。
    (2)把回环检测出来的帧进行特征匹配,估计运动,估计完运动后放入位姿图,看跟之前的估计是否有很大出入。是有算呢,还是没有算呢?
    12.5.6与机器学习的关系
    回环检测类似于分类,而且回环中类别数量大,每类的样本数量很少,每当机器运动,图像发生变化,就会产生新的类,回环检测相当于两幅图像落入同一类。图像间相似性概念的一个学习。
    改进方向
    (1)对机器学习的图像特征进行聚类,而不是人为设计的ORB特征;
    (2)用更好的方法进行聚类,而不是k叉树这种朴素的方式。
    词袋模型会被机器学习给替代的。

  • 相关阅读:
    List接口之ArrayList
    锁定线程:同步方法
    锁定线程:同步块
    通过Lambda表达式实现多线程
    通过实现Runnable接口来实现多线程
    通过继承Thread类实现多线程
    super关键字的经典案例
    Merge Two Sorted Lists
    Remove Element
    Remove Duplicates from Sorted List
  • 原文地址:https://www.cnblogs.com/talugirl/p/7384091.html
Copyright © 2020-2023  润新知