算法调试过程演示
Cartographer 是一个复杂的系统,而且调试它的前提是需要对内部的工作原理有很深刻的理解。我们这里先尝试给出Cartograher各个子系统的一个感官上总结,同时介绍各个模块使用到的参数。如果你的兴趣不止于介绍,我还是建议你去读读Cartographer的论文。我们这里只介绍2D SLAM,但是它包含了绝大部分Cartographer中应该包含的概念。这些概念在3D SLAM中依然是通用的。
Cartographer可以被看作是2个分开但是又关联的子系统组成的。第一个是局部SLAM(有时候又叫做前端或者局部路径创建者)。它的工作是创建连续的子图。所以这就意味着每个子图在局部是相连的,虽然相连但是局部的SLAM会随着时间增加而累积漂移误差。大部分的局部SLAM配置参数可以在这两个文件中找到trajectory_builder_2d.lua(2D SLAM)和trajectory_builder_3d.lua(3D SLAM),后面我们会统一用TRAJECTORY_BUILDER_nD来表示。
另一个子系统是全局SLAM (有时候又叫做后端),它在后端线程中运行,它的主要工作是寻找回环检测约束。它通过一帧帧激光数据和子图做比较来寻找回环检测约束。由于后端也会整合其他传感器数据,所以在高层的视图架构来看,后端对多传感器数据进行了融合处理,以期获得更加一致的全局结果。在3D SLAM中,它还试图找到重力的方向。和后端有关的参数可以在这个文件中找到pose_graph.lua。
更抽象一些,局部SLAM的工作就是生成良好的子图,全局SLAM的工作就是把各个子图更加连贯的串起来。
输入
距离检测传感器(例如激光雷达)提供了各个方向的距离信息。但是,有些信息却和SLAM是无关的。如果传感器表面覆盖了灰尘或者它直接扫描到了机器人的本体上,那么这些信息对于SLAM是无用的,可以被看作为噪声点。另一方面,过远的测量点也可能包含不理想的信息(强反光,仪器噪声等)。为了解决这个问题,Cartographer使用了一个带通滤波器,这样它只保留在一个合适的最大最小距离范围的激光数据。这个最大最小值取决于你自己使用的传感器。
TRAJECTORY_BUILDER_nD.min_range
TRAJECTORY_BUILDER_nD.max_range
注意:在2D SALM中如果距离超过了所设置的最大值,则使用TRAJECTORY_BUILDER_2D.missing_data_ray_length来代替。在3D SLAM中,也同样使用max_z和min_z两个参数使得3D点云固定在一定高度范围。
注意:在所有Cartographer的配置文件中,单位都是米。
距离测量是在固定间隔数据获得的,而与此同时机器人正在运动。然而距离数据以“捆绑”的方式封装为ros数据后,才被传感器传递过来。考虑到机器人的运动引起的数据畸变,Cartographer需要独立的利用每个激光点的时间戳信息来进行数据的畸变矫正。更快的获取数据,就可以更好的将一帧有畸变的激光数据转换为一帧连续的没有畸变且即时的数据。因此强烈推荐一帧提供更尽可能多的激光数据。那跟下面的这个参数又有什么关系呢?
TRAJECTORY_BUILDER_nD.num_accumulated_range_data
说到这个参数,我们这里要说另外一个参数
TRAJECTORY_BUILDER_nD.num_subdivisions_per_laser_scan
如果你的激光发布频率比较慢,那样的话机器人的运行会引起比较大的运动畸变,那怎么办呢?我们看看cartographer作者是怎么处理的,他把num_subdivisions_per_laser_scan这个参数设置为10,什么意思呢?意思就是把一帧激光分成10份,然后分别发送,这样的目的是为什么呢?可能要阅读Cartograher数据处理的代码才会知道,Cartograher处理数据的方式是:只有当同时接收到所有的传感器数据之后,才会发布其中最早的一个传感器数据,所以如果激光数据频率比较低,会影响到其他数据的快速发布,所以分成10份是为了加速其他穿插在其中的传感器数据的使用。但是激光数据由于之前分成了10份,还是要累积成10份才能进行正确的匹配,所以默认的参数num_accumulated_range_data也是等于10。
这里加一个小插曲,如果你使用了2个激光雷达建图,那这个里num_accumulated_range_data就是num_subdivisions_per_laser_scan的2倍,可以理解吗?
典型的距离数据是从机器人的一个点出发,然后会有多个角度的测量。这就意味着近距离的点云比较密集,而相反,远距离的物体的点云比较稀疏。为了降低点云的计算量,我们通常会对点云进行降采样,然而一种简单随机的采样会使得远处稀疏的点云丢失,而近处的点云仍然冗余。为了设法解决这个密度问题,我们可以使用一种体素滤波器,这中滤波器的采样间隔是固定的,而且只会保留每个体素中间的点云数据。
小一些的体素可能会得到密集的数据,这会带来更多的计算量,而大一些的体素会造成数据丢失但是计算却很快。
TRAJECTORY_BUILDER_nD.voxel_filter_size
在使用固定体素滤波器后,Cartographer也继续使用了一种自适应的体素滤波器,这种滤波器尝试找到满足一定数量点云时的最佳体素间隔(这个间隔的最大值为max_length)。就是通过自适应调整体素间隔,来满足一定点云数量要求的一种滤波器算法。在3D SLAM种,2种自适应滤波器被用来分别生成高分辨率和低分辨率的点云数据,他们的用法将在Local SLAM中进行阐述。
TRAJECTORY_BUILDER_nD.*adaptive_voxel_filter.max_length
TRAJECTORY_BUILDER_nD.*adaptive_voxel_filter.min_num_points
陀螺仪可以作为SLAM的一种重要的数据来源,因为它提供了准确的重力方向,同时提供了一种含有噪声但是可以判断机器人转向的有效指引数据(角加速度和线加速度)。为了过滤IMU的噪声,重力数据在一定间隔就会被监测。如果你使用2D SLAM,距离数据可以实时处理不需要其他数据,所以你可以选择是否使用IMU。但是在3D SLAM中,你需要提供IMU数据,因为它被用来为激光数据提供初始姿态的猜测,这样可以大大降低激光匹配的计算量。(其实在2D中使用IMU,也会大大降低激光匹配的计算量)。
TRAJECTORY_BUILDER_2D.use_imu_data
TRAJECTORY_BUILDER_nD.imu_gravity_time_constant
注意:所有配置文件中的时间单位都是秒。