【ROS】中级操作学习整理-激光SLAM

系列文章目录

·【ROS】中级操作学习整理-gazebo机器人仿真

·【ROS】中级操作学习整理-TF坐标变换

·【ROS】中级操作学习整理-传感器建模

·【ROS】中级操作学习整理-激光SLAM


前言

前面介绍了如何在gazebo中建立自己的移动机器人模型,并且我们为机器人建模了传感器,使得我们的机器人在仿真环境中也可以感知周围的环境,那么这篇文章就可以根据之前的工作,利用激光雷达来感知周围的环境,构建环境地图。

这篇文章要讲的SLAM(Simultaneous localization and mapping)-同步定位与建图是我感兴趣的方向,SLAM的问题可以描述为:将一个机器人放入未知环境中的未知位置,是否有办法让机器人一边移动一边逐步描绘出此环境完全的地图,在这个过程中机器人可以实现自身的定位和周围环境的感知,并为下一步的导航和路径规划做好准备。

SLAM分为激光SLAM(主要传感器是激光雷达)和视觉SLAM(主要传感器是相机),SLAM是1988年才提出的概念,现在理论发展基本饱和,SLAM的框架已经大概搭建完毕,现在正处于应用的巅峰期,SLAM领域门槛较高,亟需顶尖人才。

关于更多SLAM的知识可以参考高翔博士的《视觉SLAM十四讲》或者更多网络上的资源,这篇文章我们还是以ROS为主,讲解ROS中集成的一些激光SLAM算法,以及他们如何在我们的机器人上应用起来。

一、地图描述格式

在介绍激光SLAM算法之前,我们先介绍一下在ROS中地图是怎么存储和传递的。

在ROS中地图(map)其实也是一个话题,可以由建图算法发布,由路径规划节点订阅,或者由rviz节点订阅进行可视化显示,map的数据结构如下:

std_msgs/Header header          // 数据的消息头
  uint32 seq                    // 数据的序号
  time stamp                    // 数据的时间戳
  string frame_id               // 地图的坐标系
nav_msgs/MapMetaData info       // 地图的一些信息
  time map_load_time            // 加载地图的时间
  float32 resolution            // 地图的分辨率,一个格子代表着多少米,一般为0.05,[m/cell]
  uint32 width                  // 地图的宽度,像素的个数, [cells]
  uint32 height                 // 地图的高度,像素的个数, [cells]
  geometry_msgs/Pose origin     // 地图左下角的格子对应的物理世界的坐标,[m, m, rad]
    geometry_msgs/Point position
      float64 x
      float64 y
      float64 z
    geometry_msgs/Quaternion orientation
      float64 x
      float64 y
      float64 z
      float64 w
// 地图数据,优先累加行,从(0,0)开始。占用值的范围为[0,100],未知为-1。
int8[] data 

 比较重要的有frame_id用来描述地图的坐标系,width和height用来描述地图的高度和宽度,data这是一个变长度数组用来记录地图栅格的占用情况,ROS中地图的栅格占用情况是通过概率表示的,表示这个栅格有多大的概率被占用。

二、gmapping

Gmapping是一个基于2D激光雷达使用RBPF(Rao-Blackwellized Particle Filters)算法完成二维栅格地图构建的SLAM算法,算是性能较好且最常用的激光SLAM算法了,我们这里只谈gmapping的应用,而不具体深究它的算法实现。

优点:gmapping可以实时构建室内环境地图,在小场景中计算量少,且地图精度较高,对激光雷达扫描频率要求较低。

缺点:随着环境的增大,构建地图所需的内存和计算量就会变得巨大,所以gmapping不适合大场景构图。一个直观的感受是,对于200x200米的范围,如果栅格分辨率是5cm,每个栅格占用一个字节内存,那么每个粒子携带的地图都要16M的内存,如果是100粒子就是1.6G内存。

可以通过以下的命令安装gmapping算法

sudo apt-get install ros-noetic-gmapping

gmapping订阅和发布的话题

image-20200927095254706

 gmapping算法订阅tf话题和雷达数据话题scan,雷达数据不用说,由激光雷达发布,这里的tf需要注意,一定要包含机器人底盘坐标系(base_frame)和里程计坐标系(odom_frame)之间的坐标变换关系,因为SLAM解决的是定位问题,需要由这个tf经过算法,计算得出机器人(base_frame)与环境(map_frame)之间的相对位置,这个过程就是定位的过程。

因此gmapping发布了新的tf变换,并且发布三个话题,map就是创建的二维栅格地图,map_metadata主要包含地图的数据,还有机器人位姿估计的熵entropy(平时用不到,我认为是用来评估算法性能的)

虽然ROS中集成了gmapping算法,但是我们也需要创建一个launch文件来调整gmapping的一些参数,以获得我们需要的性能,具体的参数含义可以参考ros的官方说明,这里只对一些需要注意的参数进行说明。

<launch>
    <arg name="scan_topic" default="/scan" />                  <!-- 根据自己发布scan名称进行修改 -->
    <node pkg="gmapping" type="slam_gmapping" name="slam_gmapping" output="screen" clear_params="true">
        <param name="base_frame" value="/base_footprint"/>     <!-- 根据自己的基座标系名称进行修改 -->
        <param name="odom_frame" value="/odom"/>               <!-- 根据自己的里程计坐标系名称进行修改 -->
        <param name="map_update_interval" value="4.0"/>
        <!-- Set maxUrange < actual maximum range of the Laser -->
        <param name="maxRange" value="5.0"/>
        <param name="maxUrange" value="4.5"/>
        <param name="sigma" value="0.05"/>
        <param name="kernelSize" value="1"/>
        <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="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.5"/>
        <param name="angularUpdate" value="0.436"/>
        <param name="temporalUpdate" value="-1.0"/>
        <param name="resampleThreshold" value="0.5"/>
        <param name="particles" value="80"/>
        <param name="xmin" value="-1.0"/>
        <param name="ymin" value="-1.0"/>
        <param name="xmax" value="1.0"/>
        <param name="ymax" value="1.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>

代码段中通过注释,标注了三个一定需要我们根据自己机器人信息修改的参数,这几个参数需要通过rostopic list具体查看到底是哪个话题在发布,修改成对应的话题,构建了这个launch文件,我们就可以愉快的启动gmapping算法进行建图了,可以把gmapping与启动gazebo仿真环境的代码放在一个一个launch文件中启动。

<launch>
	<include file="$(find mbot_gazebo)/launch/view_mbot_with_laser_gazebo.launch"/>
	<include file="$(find my_mapping)/launch/my_gmapping.launch"/>
	<include file="$(find my_mapping)/launch/mbot_movebase.launch"/>
	
	<node pkg="rviz" type="rviz" name="rviz" args="-d $(find my_mapping)/config/my_gmapping.rviz"/>
</launch>

当然,这个文件中我还包含了movebase路径规划的配置文件,目前是我们不需要的;还包含了rviz的配置文件,当然你也可以打开一个rviz界面进行手动设置。 

 运行gmapping算法后,机器人的tf树上会出现图中红框的部分,这部分就是由gmapping算法发布的新tf变换,另外,使用rostopic工具也可以看到gmapping发布的话题。

 我们在gazebo仿真环境中添加一些能被雷达检测到的障碍物,然后启动机器人键盘控制器节点,让机器人在环境中走一圈,看看构建出的地图是什么样子。

 这里就大概完成了一张地图的创建,我们可以使用一下这行命令将地图保存到当前目录下。

rosrun map_server map_saver [-f filename]

保存的地图有两个文件,pgm格式的文件像是一张图像,yaml文件中记录了地图的信息,将地图保存下来后,在今后的仿真环境导航工作中就可以直接使用map_server节点直接加载地图了。

 加载地图的命令:

rosrun map_server map_server filename.yaml

下面是yaml文件中记录的信息

三、hector

hector_slam算法与gmapping一样都是建图算法,但是hector的特点是不需要里程计的信息即可完成建图,他只需要接收雷达的信息,使用高斯牛顿方法进行建图,下面来看一下hector订阅和发布的信息

在这里插入图片描述

 hector不需要odom与base之间的坐标系变换,但是需要知道laser坐标系和base坐标系之间的坐标变换,这一部分的坐标变换关系我们可以通过robot_state_publisher发布的机器人坐标变换关系获取,然后hector会发布map与odom之间的坐标变换,以确定机器人在环境中的位置。

也可以通过以下的代码安装hector_slam

sudo apt-get install ros-noetic-hector-slam

然后也可以像启动gmapping一样,通过配置hector的launch文件,配置它的参数

<launch>
    <include file="$(find mbot_gazebo)/launch/view_mbot_with_laser_gazebo.launch"/>
    <node pkg="hector_mapping" type="hector_mapping" name="hector_height_mapping" output="screen">
      <!--Frame names-->
    <param name="pub_map_odom_transform" value="true"/>
    <param name ="map_frame" value ="map"/>
    <param name="base_frame" value="base_link" />
    <param name="odom_frame" value="odom" />

    <!--TF use-->
    <param name="use_tf_scan_transformation" value="true"/>
    <param name="use_tf_pose_start_estimate" value="false"/>

    <!--mapsize /start point-->
    <param name="map_resolution" value="0.05"/>
    <param name="map_size" value="1024"/>
    <param name="map_start_x" value="0.5"/>
    <param name="map_start_y" value="0.5"/>
    <param name="laser_z_min_value" value="-1.0"/>
    <param name="laser_z_max_value" value="1.0"/>
    <param name="map_multi_res_levels" value="2"/>

    <param name="map_pub_period" value="1"/>
    <param name="laser_min_dist" value="0.4"/>
    <param name="laser_max_dist" value="5.5"/>
    <param name="output_timing" value="false"/>
    <param name="pub_map_scanmatch_transform"  value="true"/>

    <!--map update parameter-->
    <param name="update_factor_free" value="0.45"/>
    <param name="update_factor_occupied" value="0.7"/>
    <param name="map_update_distance_thresh" value="0.1"/>
    <param name="map_update_angle_thresh" value="0.05"/>

    <!--Advertising  config-->
    <param name="scan_topic" value="scan" />
    <param name="advertise_map_service" value="true"/>
    <param name="map_with_known_poses" value="false"/>
    <param name="scan_subscriber_queue_size" value="5"/>

  </node>

  <!-- Move base -->
 <include file="$(find my_mapping)/launch/mbot_movebase.launch"/>

</launch>

其中需要注意的地方也是frame的名字,需要与你的机器人坐标系匹配,更多关于参数的含义可以参考官网的说明

hector可以代替gmapping算法进行建图,在上面的launch文件中,我也添加了机器人仿真环境的启动和movebase框架的启动,下面就运行一下launch文件一起看一下它的效果。

 然后也可以使用tf工具,可视化其中的坐标变换关系

 当然也可以使用map_server节点将地图保存下来

另外,hector提供了另一个节点也可以用来保存地图及机器人的地理位置信息-geotiff_node,可以通过这行代码,将地图保存为一个包含图像数据的 .tif 文件和一个包含地理参考信息的 .tfw 文件。

rostopic pub syscommand std_msgs/String "savegeotiff"

最后,这个包提供了一个节点hector_trajectory_server,可以在给定目标和源框架的情况下保存基于tf的轨迹数据,轨迹在内部保存为nav_msgs/Path,可以使用服务或主题获取。在内部,一个 tf::Transformlistener 监听tf,执行必要的转换并将生成的姿势推送到保存的轨迹。更新和发布速率可以通过参数进行修改。

优缺点:不需要里程计,但对于雷达帧率要求很高40Hz,估计6自由度位姿,可以适应空中或者地面不平坦的情况。初值的选择对结果影响很大,所以要求雷达帧率较高

四、cartograph

cartographer是google开发的实时室内SLAM项目,cartographer采用基于google自家开发的ceres非线性优化的方法,cartographer的亮点在于代码规范与工程化,非常适合于商业应用和再开发。并且cartographer基于submap子图构建全局地图的思想,能有效的避免建图过程中环境中移动物体的干扰。并且cartographer支持多传感器数据(odometry、IMU、LaserScan等)建图,支持2D_SLAM和3D_SLAM建图。

这部分使用的是google的开源库,具体我没用过,可以参考这篇博客

五、karto

karto是基于图优化的SLAM算法,用高度优化和非迭代cholesky矩阵进行稀疏系统解耦作为解。图优化方法利用图的均值表示地图,每个节点表示机器人轨迹的一个位置点和传感器测量数据集,箭头的指向的连接表示连续机器人位置点的运动,每个新节点加入,地图就会依据空间中的节点箭头的约束进行计算更新。路标landmark越多,内存需求越大,然而图优化方式相比其他方法在大环境下制图优势更大

karto采取的是spa(karto_slam)或g2o(nav2d), karto的前端与后端采取的是单线程进行

先运行下面的代码,安装karto_slam:

sudo apt-get install ros-kinetic-slam-karto

下面我们还是从应用的角度看看hector_slam需要那些话题的输入和输出

在这里插入图片描述

 hector_slam的输入是激光雷达的数据scan和系统命令,另外还有一个必须要求的tf变换,就是odom与base之间的坐标变换(hector_slam需要里程计信息),然后它发布地图map、地图信息map_metadata、估计的机器人位姿slam_out_pose,然后像gmapping一样发布odom与map之间的坐标变换,另外他也提供了一个用来获取地图数据的服务。

然后我们将hector_slam的参数配置单独放置在一个yaml文件中,然后通过launch文件将参数加载到参数服务器上,下面是yaml文件

# General Parameters
use_scan_matching: true
use_scan_barycenter: true
minimum_travel_distance: 0.2 
minimum_travel_heading: 0.174                  #in radians
scan_buffer_size: 70
scan_buffer_maximum_scan_distance: 20.0
link_match_minimum_response_fine: 0.8
link_scan_maximum_distance: 10.0
loop_search_maximum_distance: 4.0
do_loop_closing: true
loop_match_minimum_chain_size: 10
loop_match_maximum_variance_coarse: 0.4     # gets squared later
loop_match_minimum_response_coarse: 0.8
loop_match_minimum_response_fine: 0.8

# Correlation Parameters - Correlation Parameters
correlation_search_space_dimension: 0.3
correlation_search_space_resolution: 0.01
correlation_search_space_smear_deviation: 0.03

# Correlation Parameters - Loop Closure Parameters
loop_search_space_dimension: 8.0
loop_search_space_resolution: 0.05
loop_search_space_smear_deviation: 0.03

# Scan Matcher Parameters
distance_variance_penalty: 0.3              # gets squared later
angle_variance_penalty: 0.349                # in degrees (gets converted to radians then squared)
fine_search_angle_offset: 0.00349               # in degrees (gets converted to radians)
coarse_search_angle_offset: 0.349            # in degrees (gets converted to radians)
coarse_angle_resolution: 0.0349                # in degrees (gets converted to radians)
minimum_angle_penalty: 0.9
minimum_distance_penalty: 0.5
use_response_expansion: false

具体的参数含义也可以参考官网说明,下面是启动的launch文件。

<launch>

  <node pkg="slam_karto" type="slam_karto" name="slam_karto" output="screen">
    <rosparam command="load" file="$(find slam_sim_demo)/param/karto_params.yaml" />
    <param name="map_update_interval" value="1"/>
    <param name="resolution" value="0.05"/>
  </node>

  <!-- Move base -->
 <include file="$(find navigation_sim_demo)/launch/include/move_base.launch.xml"/>
 
</launch>

 这是启动后的情况,我在launch文件中一并启动了movebase节点,因此我们可以通过rviz的插件2D Nav Goal设定目标点,来控制机器人移动,建立一张完整的地图。

 创建完地图后,也可以使用map_server节点将地图保存下来,以便下次使用。

另外我们还可以在建图过程中启动rosbag,记录我们建图的过程,方便以后我们进行复盘

rosbag record -a

总结

本文介绍了5中ROS中常见的激光SLAM建图方法,其中最为常用的是gmapping_slam,另外,我们在激光SLAM使用讲解过程中,一并对如何保存地图、加载地图、地图数据格式进行了简要说明,这涉及到一个重要节点map_server。

另外,虽然我们对这些算法的原理和参数没有进行详尽的说明,但我们在每个建图算法中都附上了相关的官网和参考链接,以便读者进行查阅。

最后,我们对于每个激光slam算法都进行了仿真,通过图片向读者展现了他们的效果。

下一篇,我们将对视觉slam中的代表-orbslam进行说明

  • 5
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值