ROS提供了现成的各类建图算法实现。如果只是应用的话不需要了解详细算法原理,只需要了解其需要的输入输出即可。
1 Gmapping
Gmapping使用粒子滤波算法进行建图,在小场景下准确度高,但是在大场地中会导致较大计算量和内存需求
Gmapping需要机器人提供深度信息,IMU信息,和里程计信息这三个中至少两个。利用这些输入信息gmapping算法可以输出栅格地图即小车在地图中定位
Gmapping订阅话题tf和scan。tf话题包含激光雷达和机器人基坐标系位置关系,scan包含激光雷达信息和IMU加速度信息。Gmapping可以通过话题和服务两种方式发布地图信息。其中map话题发布实时地图栅格数据,而服务dynamic_map只有在客户端发布请求是才会发布最新地图,相对于话题可以节省通信开支
对于odom里程计信息,Gmapping不用话题获取,而是通过TF树进行维护。其中 -> base_link为激光雷达相对base_link(默认的机器人基坐标系)位置,这一值一般为静态,在小车模型文件中定义好。
base_link ->odom为机器人位置相对于里程计原点坐标。其中odom坐标系位置为小车开始运行时里程计位置。通过速度积分得到base_link和odom的距离即可得到小车里程信息
map->odom 为地图中机器人位置关于里程计坐标。这里map和odom都为1不同的参考坐标系,odom是里程计测得的位置坐标,依靠小车自身移动速度积分得到,map为激光雷达测得的小车在地图上位置坐标。两者坐标系差距即为里程计的偏移
小车源码:
1 mapping.launch 启动建图的launch文件(只保留和gmapping相关内容)
<launch>
<arg name="mapping_mode" default="gmapping" doc="opt: gmapping,hector,cartographer,karto"/>
<!-- turn on lidar开启雷达 -->
<include file="$(find turn_on_wheeltec_robot)/launch/wheeltec_lidar.launch" />
<!-- 开启gmapping建图算法 -->
<group if="$(eval mapping_mode == 'gmapping')">
<include file="$(find turn_on_wheeltec_robot)/launch/include/algorithm_gmapping.launch" />
<!-- 开启机器人底层相关节点 -->
<include file="$(find turn_on_wheeltec_robot)/launch/turn_on_wheeltec_robot.launch">
<arg name="navigation" value="$(arg navigation)"/>
<arg name="is_cartographer" value="false"/>
<arg name="odom_frame_id" value="$(arg odom_frame_id)"/>
</include>
</group>
</launch>
这里我们可以看到在开启gmapping 建图算法中,我们启动了文件turn_on_wheeltec_robot/launch/include/algorithm_gmapping.launch
该文件内容如下:
<launch>
<arg name="scan_topic" default="scan" />
<arg name="base_frame" default="base_footprint"/>
<arg name="odom_frame" default="odom_combined"/>
<node pkg="gmapping" type="slam_gmapping" name="slam_gmapping" output="screen">
<param name="base_frame" value="$(arg base_frame)"/>
<param name="odom_frame" value="$(arg odom_frame)"/>
<param name="map_update_interval" value="0.01"/>
<param name="maxUrange" value="9.0"/>
<param name="maxRange" value="10.0"/>
<param name="sigma" value="0.05"/>
<param name="kernelSize" value="3"/>
<param name="lstep" value="0.05"/>
<param name="astep" value="0.05"/>
<param name="iterations" value="5"/>
<param name="lsigma" value="0.075"/>
<param name="ogain" value="3.0"/>
<param name="lskip" value="0"/>
<param name="minimumScore" value="30"/>
<param name="srr" value="0.01"/>
<param name="srt" value="0.02"/>
<param name="str" value="0.01"/>
<param name="stt" value="0.02"/>
<param name="linearUpdate" value="0.02"/>
<param name="angularUpdate" value="0.02"/>
<param name="temporalUpdate" value="-1.0"/>
<param name="resampleThreshold" value="0.25"/>
<param name="particles" value="8"/>
<param name="xmin" value="-5.0"/>
<param name="ymin" value="-4.0"/>
<param name="xmax" value="5.0"/>
<param name="ymax" value="4.0"/>
<param name="delta" value="0.05"/>
<param name="llsamplerange" value="0.01"/>
<param name="llsamplestep" value="0.01"/>
<param name="lasamplerange" value="0.005"/>
<param name="lasamplestep" value="0.005"/>
<remap from="scan" to="$(arg scan_topic)"/>
</node>
</launch>
这里有大量和gmapping算法本身的参数,如果不了解算法底层原理可以直接使用默认值。我们可以对这些参数进行调优以使用不同应用场景
里程计tf坐标发布
(代码位置/turn_on_wheeltec_robot/launch/turn_on_wheeltec_robot.launch)
<!-- 发布用于建图、导航的TF关系与小车外形可视化 -->
<include file="$(find turn_on_wheeltec_robot)/launch/robot_model_visualization.launch" unless="$(arg repeat)">
<arg name="car_mode" value="$(arg car_mode)"/>
<arg name="if_voice" value="$(arg if_voice)"/>
</include>
<!-- 扩张卡尔曼滤波 发布odom_combined到footprint的TF,即小车定位 使用cartographer算法时不使用该滤波算法-->
<include file="$(find turn_on_wheeltec_robot)/launch/include/robot_pose_ekf.launch" unless="$(arg repeat)">
<arg name="is_cartographer" value="$(arg is_cartographer)"/>
</include>
</launch>
这里涉及到的robot_pose_ekf节点是ros中常用的卡尔曼滤波算法,用于对里程计坐标信息进去预处理以提高精度
2 Cartographer
Cartographer相比于Gmapping更适用于大场地的建图。其包含回环检测可以防止累积误差。并且Cartographer只依靠雷达建图,不需要里程计
Cartographer检测步骤分为两步,local scan和global scan。其中local scan为雷达实时检测,而global scan将local scan的检测结果汇总,进行回环检测,以减小地图误差