算法思想
在Local SLAM中,通过在子图中的Scan-to-map匹配过程可以得到了一个比较理想的机器人位姿估计。 但由于Local SLAM只使用了一段时间内的局部信息,所以定位误差还会随着时间累积。为了能够进一步的降低局部误差累积的影响,Cartographer还需要通过Pixel-accurate扫描匹配来进行闭环检测, 进一步的优化机器人的位姿估计。 分支定界时,地图一定要完整
记 为雷达扫描到的 个hit点集合, 是第k个hit点在scan frame下的坐标,在地图坐标系下的坐标表示为
其中 ,用表示位姿估计描述的坐标变换。
Pixel-accurate扫描匹配问题可以用下式来描述:
其中 是一个搜索窗口, 是离最近的栅格单元的占用概率。也就是在搜索窗口中找到一个最优的位姿,使得hit点集合出现的概率最大化。
这里和前端的实时相关性匹配有部分相似,但是多分辨率地图和分支定界是新加入的。机器人的位姿是用表示,对于暴力匹配法来说,该方法的搜索步长为1,搜索窗口的栅格索引集合 可以通过笛卡尔积枚举:
其中 和 是x 和 y方向上的最大搜索索引,是搜索角度的最大索引。搜索窗口就可以用集合 表示。 是搜索中心,也是机器人位姿的初始估计, 和 分别是位移和角度的搜索步长。
如果直接用暴力搜索就太复杂了,所以使用深度优先的分支定界法。使用一个满四叉树的概念,这跟二叉树是类似的。
其根节点代表整个搜索窗口 。树中的每一个节点的孩子都是对该节点所代表的搜索空间的一个划分, 每个叶子节点都对应着一个解。
树共有8层,分别表示从顶层()到底层/叶子(),树的深度depth每增加1,搜索步长step就减半,表示查找的分辨率越高。每次对x和y都减半操作,像分田一样。如果栅格地图分辨率是0.05m(一个栅格的物理长度),step与depth的关系如下:
比如在树的最底层,, 此时 且 ,显然只有1个解。
对于一个candidate,可构建四个子Candidate。C层搜索空间:
搜索树上的每个节点的上界可以通过下式计算得到:
Cartographer仍然采取了一种空间换时间的方法来进一步的提高搜索效率。预算图(precomputed grids)又叫膨胀图,从上面我们知道了树的每一层都对应一个步长,根据这个步长,生成一个对应的膨胀图。步长越大,膨胀级别越高。 根节点的分辨率是最粗的。Cartographer还为每个子图预先计算了不同尺度下的占用概率,以后的搜索过程只需要简单的查表就可以完成了。
需要注意的是膨胀不会改变栅格图的分辨率,改变的仅仅是栅格图中像素值而已。这个膨胀的过程比较复杂,源码里是用一种滑动窗口的机制实现的。
整个搜索过程借助了一个栈{C}来完成,首先将整个搜索空间分割,得到初始的子空间节点集合 。 然后对集合 中的节点根据它们的上界进行排序,并依次入栈{C}保证得分最高的节点在栈顶的位置。这样可以优先搜索最有可能的解空间。
最底层的节点是所有候选的评分, 这是以两个叶子节点做示范。 父节点的贪心得分永远大于子节点的贪心得分。从而保证,一旦父节点的贪心得分小于best_score,那么这个父节点的子树全部被剪枝,因为其子树的叶子节点的得分肯定也小于best_score。
源码
接上一篇的分支定界部分
1 | const Candidate2D best_candidate = BranchAndBound( |
1 | // 初始传入的candidates为最上层的候选解 |
第一步应先求取顶层的解及其对应评分(即可能位置和对应匹配置信度)。每层的当前节点的对应的评分均大于等于其所有下层枝叶节点,即上边界。由于不同分辨率地图存储格式,显然满足上边界条件。低分辨地图下的匹配置信度显然高于下层的高分辨地图下的匹配,然后采用迭代方法裁剪枝叶,直到遍历所有叶子节点。
在地图预处理时,其分辨率按照2的层数次方进行压缩的,由于地图有x和y两个方向,因此此层的一个节点,在下层会分为4个节点,即分辨率会放大2倍
参考:分枝定界图解