ROS2目前对移动机器人的的支持较为丰富,SLAM和导航这些基础功能包都已经具备了,今天我们就以turtlebot3为例,试一试在Gazebo中如何把SLAM跑起来。

一、依赖包安装

首先需要安装Gazebo11,默认安装完的ROS2是不包含的:
$ sudo apt-get install ros-foxy-gazebo-*
后续我们会使用Cartographer进行SLAM,所以还需要安装Cartographer相关的功能包:
$ sudo apt install ros-foxy-cartographer
$ sudo apt install ros-foxy-cartographer-ros
接下来安装Turtlebot3相关的功能包:
$ sudo apt install ros-foxy-turtlebot3
$ sudo apt install ros-foxy-turtlebot3-simulations

二、启动仿真环境

先在终端中配置一下仿真模型的路径:
$ export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:'ros2 pkg \prefix turtlebot3_gazebo \'/share/turtlebot3_gazebo/models/
接下来使用如下命令启动仿真环境:
$ export TURTLEBOT3_MODEL=burger
$ ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
可以看到启动成功的仿真环境如下:

三、运行SLAM功能

运行SLAM功能节点:
$ export TURTLEBOT3_MODEL=burger
$ ros2 launch turtlebot3_cartographer cartographer.launch.py use_sim_time:=True
在打开的rvoz中可以看到SLAM实时效果:
运行键盘控制节点:
$ export TURTLEBOT3_MODEL=burger
$ ros2 run turtlebot3_teleop teleop_keyboard
接下来控制机器人在仿真环境中移动,会看到如下建图过程:
SLAM完成后,可以通过如下命令保存建立好的地图:
$ ros2 run nav2_map_server map_saver_cli -f ~/map
点击保存好的地图,就可以看到结果啦:

四、Launch文件分析

在以上SLAM建图过程中,我们用到了一个重要的Launch文件cartographer.launch.py,里边内容是什么样的呢?我们一起来看下:
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch_ros.actions import Node
from launch.substitutions import LaunchConfiguration
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import ThisLaunchFileDir

def generate_launch_description():
    use_sim_time = LaunchConfiguration('use_sim_time', default='false')
    turtlebot3_cartographer_prefix = get_package_share_directory('turtlebot3_cartographer')
    cartographer_config_dir = LaunchConfiguration('cartographer_config_dir',
                                                    default=os.path.join(turtlebot3_cartographer_prefix, 'config'))
    configuration_basename = LaunchConfiguration('configuration_basename', default='turtlebot3_lds_2d.lua')  //激光雷达与建图参数配置文件

    resolution = LaunchConfiguration('resolution', default='0.05')  ## 地图分辨率
    publish_period_sec = LaunchConfiguration('publish_period_sec', default='1.0') ##地图更新频率

    rviz_config_dir = os.path.join(get_package_share_directory('turtlebot3_cartographer'), 'rviz', 'tb3_cartographer.rviz') ## rviz的配置文件

    return LaunchDescription([
        DeclareLaunchArgument(
            'cartographer_config_dir',
            default_value=cartographer_config_dir,
            description='Full path to config file to load'),
        DeclareLaunchArgument(
            'configuration_basename',
            default_value=configuration_basename,
            description='Name of lua file for cartographer'),
        DeclareLaunchArgument(
            'use_sim_time',
            default_value='false',
            description='Use simulation (Gazebo) clock if true'),

        Node(
            package='cartographer_ros',
            node_executable='cartographer_node', ## cartographer建图节点
            node_name='cartographer_node',
            output='screen',
            parameters=[{'use_sim_time': use_sim_time}],
            arguments=['-configuration_directory', cartographer_config_dir, '-configuration_basename', configuration_basename]),

        DeclareLaunchArgument(
            'resolution',
            default_value=resolution,
            description='Resolution of a grid cell in the published occupancy grid'),

        DeclareLaunchArgument(
            'publish_period_sec',
            default_value=publish_period_sec,
            description='OccupancyGrid publishing period'),

        IncludeLaunchDescription(
            PythonLaunchDescriptionSource([ThisLaunchFileDir(), '/occupancy_grid.launch.py']),  ## 占据地图的相关配置
            launch_arguments={'use_sim_time': use_sim_time, 'resolution': resolution, 'publish_period_sec': publish_period_sec}.items(),
        ),

        Node(
            package='rviz2',
            node_executable='rviz2', ## rviz2节点
            node_name='rviz2',
            arguments=['-d', rviz_config_dir],
            parameters=[{'use_sim_time': use_sim_time}],
            output='screen'),
    ])
在以上Launch文件中,还调用到了另外一个occupancy_grid.launch.py文件,我们也来看下:
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch_ros.actions import Node
from launch.substitutions import LaunchConfiguration

def generate_launch_description():
    use_sim_time = LaunchConfiguration('use_sim_time', default='false')
    resolution = LaunchConfiguration('resolution', default='0.05')
    publish_period_sec = LaunchConfiguration('publish_period_sec', default='1.0')

    return LaunchDescription([
        DeclareLaunchArgument(
            'resolution',
            default_value=resolution,
            description='Resolution of a grid cell in the published occupancy grid'),

        DeclareLaunchArgument(
            'publish_period_sec',
            default_value=publish_period_sec,
            description='OccupancyGrid publishing period'),

        DeclareLaunchArgument(
            'use_sim_time',
            default_value='false',
            description='Use simulation (Gazebo) clock if true'),

        Node(
            package='cartographer_ros',
            node_executable='occupancy_grid_node', ##占据地图构建节点
            node_name='occupancy_grid_node',
            output='screen',
            parameters=[{'use_sim_time': use_sim_time}],
            arguments=['-resolution', resolution, '-publish_period_sec', publish_period_sec]),
    ])
参考链接: