设置传感器

在本指南中,我们将讨论传感器在安全导航机器人方面的重要性以及如何使用 Nav2 设置传感器。在本教程的前半部分,我们将简要介绍 Nav2 中常用的传感器和常见传感器消息。接下来,我们将在之前构建的模拟机器人 sam_bot 上添加一个基本的传感器设置。最后,我们将通过在 RViz 中可视化 sam_bot 的模拟传感器消息来验证它们。

在机器人上设置传感器后,它们的读数可用于测绘、定位和感知任务。在本指南的后半部分,我们将首先讨论测绘和定位如何使用传感器数据。然后,我们还将介绍 Nav2 的一个包 nav2_costmap_2d ,它会生成最终将用于 Nav2 路径规划的成本地图。我们将为该包设置基本配置参数,以便它正确地从 sam_bot 获取传感器信息。最后,我们在 RViz 中可视化生成的成本地图以验证其接收到的数据。

传感器介绍

移动机器人配备了大量传感器,使它们能够看到和感知周围环境。这些传感器获取的信息可用于构建和维护环境地图、在地图上定位机器人以及查看环境中的障碍物。这些任务对于安全高效地引导机器人穿越动态环境至关重要。

常用传感器的示例包括激光雷达、雷达、RGB 摄像头、深度摄像头、IMU 和 GPS。为了标准化这些传感器的消息格式并允许供应商之间更轻松地进行互操作,ROS 提供了 sensor_msgs 包来定义通用传感器接口。这也允许用户使用任何传感器供应商,只要它遵循 sensor_msgs 中的标准格式即可。在下一小节中,我们将介绍导航中常用的一些消息,即 sensor_msgs/LaserScansensor_msgs/PointCloud2sensor_msgs/Rangesensor_msgs/Image

除了 sensor_msgs 包之外,您还应该了解 radar_msgsvision_msgs 标准接口。 radar_msgs 定义雷达专用传感器的消息,而 vision_msgs 包定义计算机视觉中使用的消息,例如对象检测、分割和其他机器学习模型。该包支持的消息包括 vision_msgs/Classification2Dvision_msgs/Classification3Dvision_msgs/Detection2Dvision_msgs/Detection3D ,等等。

See also

有关更多信息,请参阅 sensor_msgs, radar_msgs, 和 vision_msgs.

您的物理机器人的传感器可能具有为其编写的 ROS 驱动程序(例如,连接到传感器、将数据填充到消息中并发布它们以供您的机器人使用的 ROS 节点),这些驱动程序遵循 sensor_msgs 包中的标准接口。 sensor_msgs 包使您可以轻松使用来自不同制造商的许多不同传感器。然后,Nav2 等通用软件包可以读取这些标准化消息并执行独立于传感器硬件的任务。在模拟机器人(例如 sam_bot)上,Gazebo 具有传感器插件,它们也会按照 sensor_msgs 包发布其信息。

常见传感器消息

在本小节中,我们将讨论在设置 Nav2 时可能遇到的一些常见类型的 sensor_msgs。我们将为每个传感器提供简要说明、在 Gazebo 中模拟的图像以及 RViz 中相应的传感器读数可视化。

Note

除了下面列出的类型外,还有其他类型的 sensor_msgs 。完整的消息列表及其定义可在 sensor_msgs 文档 中找到。

sensor_msgs/LaserScan

此消息表示来自平面激光测距仪的单次扫描。此消息在 slam_toolboxnav2_amcl 中用于定位和映射,或在 nav2_costmap_2d 中用于感知。

../../_images/sensor_laserscan.png

sensor_msgs/PointCloud2

此消息包含 3D 点的集合,以及有关每个点的可选附加信息。这些信息可以来自 3D 激光雷达、2D 激光雷达、深度摄像头等。

../../_images/sensor_pointcloud2.png

sensor_msgs/Range

这是来自活动测距仪的单个测距读数,该测距仪发射能量并报告沿测量距离的弧线有效的一个测距读数。声纳、红外传感器或 1D 测距仪是使用此消息的传感器的示例。

../../_images/sensor_range.png

sensor_msgs/Image

这代表来自 RGB 或深度摄像头的传感器读数,对应 RGB 或范围值。

../../_images/sensor_image.png

使用 Gazebo 模拟传感器

为了让您更好地了解如何在模拟机器人上设置传感器,我们将在之前的教程的基础上,将传感器连接到我们的模拟机器人 sam_bot 。与之前的教程类似,我们使用 Gazebo 插件将里程计传感器添加到 sam_bot ,我们将使用 Gazebo 插件在 sam_bot 上模拟激光雷达传感器和深度摄像头。如果您使用的是真正的机器人,则大部分这些步骤对于设置您的 URDF 框架仍然是必需的,并且添加 gazebo 插件以供以后使用也不会有什么坏处。

为了能够遵循本节的其余部分,请确保您已正确安装 Gazebo。您可以按照上一教程中的 Setup and Prerequisites 中的说明来设置 Gazebo。

将 Gazebo 插件添加到 URDF

首先,我们为 sam_bot 添加一个激光雷达传感器。打开 URDF 文件 src/description/sam_bot_description.urdf 并将以下几行粘贴到 </robot> 标签前。

251<link name="lidar_link">
252  <inertial>
253    <origin xyz="0 0 0" rpy="0 0 0"/>
254    <mass value="0.125"/>
255    <inertia ixx="0.001"  ixy="0"  ixz="0" iyy="0.001" iyz="0" izz="0.001" />
256  </inertial>
257
258  <collision>
259    <origin xyz="0 0 0" rpy="0 0 0"/>
260    <geometry>
261       <cylinder radius="0.0508" length="0.055"/>
262    </geometry>
263  </collision>
264
265  <visual>
266    <origin xyz="0 0 0" rpy="0 0 0"/>
267    <geometry>
268       <cylinder radius="0.0508" length="0.055"/>
269    </geometry>
270  </visual>
271</link>
272
273<joint name="lidar_joint" type="fixed">
274  <parent link="base_link"/>
275  <child link="lidar_link"/>
276  <origin xyz="0 0 0.12" rpy="0 0 0"/>
277</joint>
278
279<gazebo reference="lidar_link">
280  <sensor name="lidar" type="ray">
281    <always_on>true</always_on>
282    <visualize>true</visualize>
283    <update_rate>5</update_rate>
284    <ray>
285      <scan>
286        <horizontal>
287          <samples>360</samples>
288          <resolution>1.000000</resolution>
289          <min_angle>0.000000</min_angle>
290          <max_angle>6.280000</max_angle>
291        </horizontal>
292      </scan>
293      <range>
294        <min>0.120000</min>
295        <max>3.5</max>
296        <resolution>0.015000</resolution>
297      </range>
298      <noise>
299        <type>gaussian</type>
300        <mean>0.0</mean>
301        <stddev>0.01</stddev>
302      </noise>
303    </ray>
304    <plugin name="scan" filename="libgazebo_ros_ray_sensor.so">
305      <ros>
306        <remapping>~/out:=scan</remapping>
307      </ros>
308      <output_type>sensor_msgs/LaserScan</output_type>
309      <frame_name>lidar_link</frame_name>
310    </plugin>
311  </sensor>
312</gazebo>

在上面的代码片段中,我们创建了一个 lidar_link ,它将被 gazebo_ros_ray_sensor 插件引用为连接传感器的位置。我们还将值设置为模拟激光雷达的扫描和范围属性。最后,我们将 /scan 设置为它将发布 sensor_msgs/LaserScan 消息的主题。

接下来,让我们为 sam_bot 添加一个深度摄像头。将以下几行粘贴到激光雷达传感器的 </gazebo> 标签后。

314<link name="camera_link">
315  <visual>
316    <origin xyz="0 0 0" rpy="0 0 0"/>
317    <geometry>
318      <box size="0.015 0.130 0.022"/>
319    </geometry>
320  </visual>
321
322  <collision>
323    <origin xyz="0 0 0" rpy="0 0 0"/>
324    <geometry>
325      <box size="0.015 0.130 0.022"/>
326    </geometry>
327  </collision>
328
329  <inertial>
330    <origin xyz="0 0 0" rpy="0 0 0"/>
331    <mass value="0.035"/>
332    <inertia ixx="0.001"  ixy="0"  ixz="0" iyy="0.001" iyz="0" izz="0.001" />
333  </inertial>
334</link>
335
336<joint name="camera_joint" type="fixed">
337  <parent link="base_link"/>
338  <child link="camera_link"/>
339  <origin xyz="0.215 0 0.05" rpy="0 0 0"/>
340</joint>
341
342<link name="camera_depth_frame"/>
343
344<joint name="camera_depth_joint" type="fixed">
345  <origin xyz="0 0 0" rpy="${-pi/2} 0 ${-pi/2}"/>
346  <parent link="camera_link"/>
347  <child link="camera_depth_frame"/>
348</joint>
349
350<gazebo reference="camera_link">
351  <sensor name="depth_camera" type="depth">
352    <visualize>true</visualize>
353    <update_rate>30.0</update_rate>
354    <camera name="camera">
355      <horizontal_fov>1.047198</horizontal_fov>
356      <image>
357        <width>640</width>
358        <height>480</height>
359        <format>R8G8B8</format>
360      </image>
361      <clip>
362        <near>0.05</near>
363        <far>3</far>
364      </clip>
365    </camera>
366    <plugin name="depth_camera_controller" filename="libgazebo_ros_camera.so">
367      <baseline>0.2</baseline>
368      <alwaysOn>true</alwaysOn>
369      <updateRate>0.0</updateRate>
370      <frame_name>camera_depth_frame</frame_name>
371      <pointCloudCutoff>0.5</pointCloudCutoff>
372      <pointCloudCutoffMax>3.0</pointCloudCutoffMax>
373      <distortionK1>0</distortionK1>
374      <distortionK2>0</distortionK2>
375      <distortionK3>0</distortionK3>
376      <distortionT1>0</distortionT1>
377      <distortionT2>0</distortionT2>
378      <CxPrime>0</CxPrime>
379      <Cx>0</Cx>
380      <Cy>0</Cy>
381      <focalLength>0</focalLength>
382      <hackBaseline>0</hackBaseline>
383    </plugin>
384  </sensor>
385</gazebo>

与激光雷达传感器类似,我们创建了 camera_link ,它将被 gazebo_ros_camera 插件引用为传感器连接位置。我们还创建了一个 camera_depth_frame ,它连接到 camera_link ,并将设置为深度相机插件的 <frame_name> 。我们还配置了插件,使其分别将 sensor_msgs/Imagesensor_msgs/PointCloud2 消息发布到 /depth_camera/image_raw/depth_camera/points 主题。最后,我们还为深度相机设置了其他基本配置属性。

启动和构建文件

为了验证传感器是否设置正确,以及它们是否可以看到我们环境中的物体,让我们在有物体的 Gazebo 世界中启动 sam_bot 。 让我们创建一个 Gazebo 世界,其中包含一个立方体和一个球体,它们在 sam_bot 传感器的范围内,以便我们验证它是否可以正确看到物体。

要创建世界,请在项目的根目录中创建一个名为 world 的目录,并在“world”文件夹内创建一个名为 my_world.sdf 的文件。然后复制 world/my_world.sdf 的内容并将其粘贴到“my_world.sdf”中。

现在,让我们编辑启动文件 launch/display.launch.py​​,以使用我们刚刚创建的世界启动 Gazebo。首先,通过在 generate_launch_description() 中添加以下几行来添加 my_world.sdf 的路径:

world_path=os.path.join(pkg_share, 'world/my_world.sdf')

最后,在 launch.actions.ExecuteProcess(cmd=['gazebo',... 行中添加世界路径,如下所示。

launch.actions.ExecuteProcess(cmd=['gazebo', '--verbose', '-s', 'libgazebo_ros_init.so', '-s', 'libgazebo_ros_factory.so', world_path], output='screen'),

我们还必须将 world 目录添加到我们的 CMakeLists.txt 文件中。打开 CmakeLists.txt 并将“world”目录附加到 install(DIRECTORY…) 中,如下面的代码片段所示。

install(
  DIRECTORY src launch rviz config world
  DESTINATION share/${PROJECT_NAME}
)

构建、运行和验证

我们现在可以构建并运行我们的项目。导航到项目的根目录并执行以下几行:

colcon build
. install/setup.bash
ros2 launch sam_bot_description display.launch.py

然后,RViz 和 Gazebo 将启动,其中都存在 sam_bot 。在 Gazebo 窗口中,我们创建的世界应该启动,并且 sam_bot 应该在该世界中生成。现在您应该能够使用 360 激光雷达传感器和深度摄像头观察 sam_bot ,如下图所示。

../../_images/gazebo_sensors.png

在 RViz 窗口中,我们可以验证我们是否正确地建模了我们的传感器,以及我们新添加的传感器的变换是否正确:

../../_images/rviz_sensors.png

最后,我们还可以在 RViz 中可视化传感器读数。要可视化在 /scan 主题上发布的 sensor_msgs/LaserScan 消息,请单击 RViz 窗口底部的添加按钮。然后转到 By topic 选项卡并选择 /scan 下的 LaserScan 选项,如下所示。

../../_images/add_topic_laserscan.png

接下来,将 RViz 中的 Reliability Policy 设置为 Best Effort ,并将 size 设置为 0.1,以便更清楚地看到点。您应该看到可视化的 LaserScan 检测,如下所示。这对应于我们添加到 Gazebo 世界的检测到的立方体和球体。

../../_images/demo_laserscan_rviz.png

为了可视化 sensor_msgs/Imagesensor_msgs/PointCloud2 ,分别对主题 /depth_camera/image_raw/depth_camera/points 执行相同操作:

../../_images/add_topic_image_pointcloud2.png

在 RViz 中添加 /depth_camera/image_raw 主题后,将 RViz 中的 Reliability Policy 设置为 Best Effort。然后您应该在 RViz 窗口左下方的图像窗口中看到立方体,如下所示。

../../_images/demo_image_rviz.png

您还应该看到 sensor_msgs/PointCloud2,如下所示。

../../_images/pointcloud2_data.png

地图绘制和定位

现在我们已经有一个安装了传感器的机器人,我们可以使用获得的传感器信息来构建环境地图并在地图上定位机器人。slam_toolbox 包是一套工具和功能,用于使用 ROS2 在可能庞大的地图中进行 2D 同步定位和地图绘制 (SLAM)。它也是 Nav2 中官方支持的 SLAM 库之一,我们建议在您需要在机器人设置上使用 SLAM 的情况下使用此包。除了 slam_toolbox 之外,还可以通过 nav2_amcl 包实现定位。该包实现了自适应蒙特卡罗定位 (AMCL),可估计机器人在地图中的位置和方向。其他技术也可能可用,请查看 Nav2 文档了解更多信息。

slam_toolboxnav2_amcl 都使用来自激光扫描传感器的信息来感知机器人的环境。因此,为了验证它们是否可以访问激光扫描传感器读数,我们必须确保它们订阅了发布 sensor_msgs/LaserScan 消息的正确主题。这可以通过将它们的 scan_topic 参数设置为发布该消息的主题来进行配置。将 sensor_msgs/LaserScan 消息发布到 /scan 主题是一种惯例。因此,默认情况下,scan_topic 参数设置为 /scan。回想一下,当我们在上一节中将激光雷达传感器添加到 sam_bot 时,我们将激光雷达传感器将发布 sensor_msgs/LaserScan 消息的主题设置为 /scan

由于完整配置参数可能非常复杂,因此本教程不会深入讨论这些参数。相反,我们建议您查看以下链接中的官方文档。

See also

有关 slam_toolbox 的完整配置参数列表,请参阅 slam_toolbox 的 Github 存储库
有关 nav2_amcl 的完整配置参数列表和示例配置,请参阅 AMCL 配置指南

您还可以参考 (SLAM) 地图绘制时导航指南 以获取有关如何将 Nav2 与 SLAM 结合使用的教程。您可以通过在 RViz 中可视化地图和机器人的姿势来验证 slam_toolboxnav2_amcl 是否已正确设置,类似于上一节中显示的内容

Costmap 2D

costmap 2D 包利用传感器信息以占用网格的形式提供机器人环境的表示。占用网格中的单元格存储 0-254 之间的成本值,这些成本值表示穿越这些区域的成本。成本为 0 表示单元格是空闲的,而成本为 254 表示单元格被严重占用。导航算法使用这两个极端之间的值来引导您的机器人远离作为潜在场的障碍物。Nav2 中的 Costmaps 是通过 nav2_costmap_2d 包实现的。

costmap 实现由多个层组成,每个层都具有一定的功能,有助于增加单元格的总成本。该包由以下层组成,但基于插件,允许自定义和使用新层:静态层、膨胀层、范围层、障碍层和体素层。静态层表示代价地图的地图部分,从发布到 /map 主题的消息(如 SLAM 生成的消息)中获得。障碍物层包括发布 LaserScanPointCloud2 消息之一或两者的传感器检测到的对象。体素层类似于障碍物层,因此它可以使用 LaserScanPointCloud2 传感器信息之一或两者,但处理 3D 数据。范围层允许包含声纳和红外传感器提供的信息。最后,膨胀层表示致命障碍物周围的附加成本值,这样我们的机器人就可以避免由于机器人的几何形状而撞到障碍物。在本教程的下一小节中,我们将讨论 nav2_costmap_2d 中不同层的基本配置。

这些层通过插件接口集成到成本地图中,然后使用用户指定的 inflation radius 进行膨胀(如果启用了膨胀层)。有关成本地图概念的更深入讨论,您可以查看 ROS1 costmap_2D documentation 。请注意, nav2_costmap_2d 包主要是 ROS1 导航堆栈版本的简单 ROS2 端口,需要进行少量更改以支持 ROS2 和一些新的层插件。

Configuring nav2_costmap_2d

在本小节中,我们将展示 nav2_costmap_2d 的示例配置,以便它使用 sam_bot 的激光雷达传感器提供的信息。我们将展示一个使用静态层、障碍物层、体素层和膨胀层的示例配置。我们将障碍物和体素层都设置为使用激光雷达传感器发布到“/scan”主题的“LaserScan”消息。我们还设置了一些基本参数来定义检测到的障碍物如何反映在成本地图中。请注意,此配置将包含在 Nav2 的配置文件中。

 1global_costmap:
 2  global_costmap:
 3    ros__parameters:
 4      update_frequency: 1.0
 5      publish_frequency: 1.0
 6      global_frame: map
 7      robot_base_frame: base_link
 8      use_sim_time: True
 9      robot_radius: 0.22
10      resolution: 0.05
11      track_unknown_space: false
12      rolling_window: false
13      plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
14      static_layer:
15        plugin: "nav2_costmap_2d::StaticLayer"
16        map_subscribe_transient_local: True
17      obstacle_layer:
18        plugin: "nav2_costmap_2d::ObstacleLayer"
19        enabled: True
20        observation_sources: scan
21        scan:
22          topic: /scan
23          max_obstacle_height: 2.0
24          clearing: True
25          marking: True
26          data_type: "LaserScan"
27          raytrace_max_range: 3.0
28          raytrace_min_range: 0.0
29          obstacle_max_range: 2.5
30          obstacle_min_range: 0.0
31      inflation_layer:
32        plugin: "nav2_costmap_2d::InflationLayer"
33        cost_scaling_factor: 3.0
34        inflation_radius: 0.55
35      always_send_full_costmap: True
36
37local_costmap:
38  local_costmap:
39    ros__parameters:
40      update_frequency: 5.0
41      publish_frequency: 2.0
42      global_frame: odom
43      robot_base_frame: base_link
44      use_sim_time: True
45      rolling_window: true
46      width: 3
47      height: 3
48      resolution: 0.05
49      robot_radius: 0.22
50      plugins: ["voxel_layer", "inflation_layer"]
51      voxel_layer:
52        plugin: "nav2_costmap_2d::VoxelLayer"
53        enabled: True
54        publish_voxel_map: True
55        origin_z: 0.0
56        z_resolution: 0.05
57        z_voxels: 16
58        max_obstacle_height: 2.0
59        mark_threshold: 0
60        observation_sources: scan
61        scan:
62          topic: /scan
63          max_obstacle_height: 2.0
64          clearing: True
65          marking: True
66          data_type: "LaserScan"
67      inflation_layer:
68        plugin: "nav2_costmap_2d::InflationLayer"
69        cost_scaling_factor: 3.0
70        inflation_radius: 0.55
71      always_send_full_costmap: True

在上面的配置中,请注意我们为两个不同的costmap设置了参数: global_costmaplocal_costmap。我们设置了两个costmap,因为 global_costmap 主要用于整个地图的长期规划,而 local_costmap 用于短期规划和避免碰撞。

我们用于配置的层在 plugins 参数中定义,如第13行的 global_costmap 和第50行的 local_costmap 所示。这些值设置为映射层名称列表,它们还用作我们从第14行和第51行开始设置的层参数的命名空间。请注意,此列表中的每个层/命名空间都必须有一个“plugin”参数(如第15、18、32、52和68行所示),定义要为该特定层加载的插件类型。

对于静态层(第 14-16 行),我们将 map_subscribe_transient_local 参数设置为 True。这将设置地图主题的 QoS 设置。静态层的另一个重要参数是 map_topic,它定义要订阅的地图主题。未定义时,默认为 /map 主题。

对于障碍物层(第 17-30 行),我们在 observation_sources 参数(第 20 行)下将其传感器源定义为 scan,其参数在第 22-30 行中设置。我们将其 topic 参数设置为发布定义的传感器源的主题,并根据它将使用的传感器源设置 data_type。在我们的配置中,障碍物层将使用激光雷达传感器发布到 /scanLaserScan

请注意,障碍物层和体素层可以使用 LaserScanPointCloud2 中的一种或两种作为其 data_type ,但默认设置为 LaserScan 。下面的代码片段展示了使用 LaserScanPointCloud2 作为传感器源的示例。这在设置您自己的物理机器人时可能特别有用。

obstacle_layer:
  plugin: "nav2_costmap_2d::ObstacleLayer"
  enabled: True
  observation_sources: scan pointcloud
  scan:
    topic: /scan
    data_type: "LaserScan"
  pointcloud:
    topic: /depth_camera/points
    data_type: "PointCloud2"

对于障碍物层的其他参数,max_obstacle_height 参数设置传感器读数返回占用网格的最大高度。传感器读数的最小高度也可以使用 min_obstacle_height 参数设置,由于我们没有在配置中设置它,因此默认为 0。clearing 参数用于设置是否要从代价地图中移除障碍物。清除操作是通过网格的光线追踪完成的。使用 raytrace_max_rangeraytrace_min_range 分别设置从代价地图中光线追踪清除对象的最大和最小范围。marking 参数用于设置是否将插入的障碍物标记到代价地图中。我们还分别通过 obstacle_max_rangeobstacle_min_range 设置在代价地图中标记障碍物的最大和最小范围。

对于膨胀层(第 31-34 行和第 67-70 行),我们使用 cost_scaling_factor 参数设置膨胀半径的指数衰减因子。使用 inflation_radius 定义致命障碍物周围膨胀的半径值。

对于体素层(第 51-66 行),我们将 publish_voxel_map 参数设置为 True ,以启用 3D 体素网格的发布。使用 z_resolution 参数定义体素在高度上的分辨率,而使用 z_voxels 参数定义每列中的体素数量。 mark_threshold 参数设置列中标记为占用网格中的最小体素数量。我们将体素层的 observation_sources 参数设置为 scan ,并将扫描参数(第 61-66 行)设置为与我们讨论过的障碍物层的参数类似。如其 topicdata_type 参数中所定义,体素层将使用激光雷达扫描仪在 /scan 主题上发布的 LaserScan

请注意,我们没有为我们的配置使用范围层,但它可能对您自己的机器人设置有用。对于范围层,其基本参数是 topicsinput_sensor_typeclear_on_max_reading 参数。要订阅的范围主题在 topics 参数中定义。 input_sensor_type 设置为 ALLVARIABLE”或“FIXEDclear_on_max_reading 是一个布尔参数,用于设置是否清除最大范围内的传感器读数。如果您需要进行设置,请查看下面链接中的配置指南。

See also

For more information on nav2_costmap_2d and the complete list of layer plugin parameters, see the Costmap 2D Configuration Guide. 有关 nav2_costmap_2d 的更多信息以及图层插件参数的完整列表,请参阅 Costmap 2D 配置指南

构建、运行和验证

我们将首先启动 display.launch.py​​,它将启动机器人状态发布器,在我们的 URDF 中提供 base_link => sensors 转换。它还启动了 Gazebo,作为我们的物理模拟器,并从差速驱动插件中提供了 odom => base_link,我们在上一个指南 使用 Gazebo 模拟里程计系统 中将其添加到 sam_bot。它还启动了 RViz,我们可以使用它来可视化机器人和传感器信息。

在我们正确设置机器人描述、里程计传感器和必要的变换后,我们最终将启动 Nav2 系统本身。目前,我们将仅探索 Nav2 的成本地图生成系统。启动 Nav2 后,我们将在 RViz 中可视化成本地图以确认我们的输出。

启动描述节点、RViz 和 Gazebo

现在让我们通过启动文件 display.launch.py 启动我们的机器人描述节点、RViz 和 Gazebo。打开一个新终端并执行以下几行。

colcon build
. install/setup.bash
ros2 launch sam_bot_description display.launch.py

现在应该启动 RViz 和 Gazebo,并且两者都有 sam_bot。回想一下,base_link => sensors 转换现在由 robot_state_publisher 发布,而 odom => base_link 转换则由我们的 Gazebo 插件发布。现在应该可以在 RViz 中无错误地显示这两个转换。

启动 slam_toolbox

为了能够启动 slam_toolbox,请确保您已通过执行以下命令安装了 slam_toolbox 包:

sudo apt install ros-<ros2-distro>-slam-toolbox

我们将使用包的内置启动文件启动 slam_toolboxasync_slam_toolbox_node 。打开一个新终端,然后执行以下几行:

ros2 launch slam_toolbox online_async_launch.py

slam_toolbox 现在应该发布到 /map 主题并提供 map => odom 转换。

我们可以在 RViz 中验证 /map 主题是否已发布。在 RViz 窗口中,单击左下角的添加按钮,然后转到 按主题 选项卡,然后选择 /map 主题下的 Map。您应该能够看到 /map 中收到的消息,如下图所示。

../../_images/map.png

我们还可以通过在新终端中执行以下几行来检查转换是否正确:

ros2 run tf2_tools view_frames.py

注意:对于 Galactic 及更新版本,它应该是 view_frames 而不是 view_frames.py 上面的行将创建一个显示当前转换树的 frames.pdf 文件。您的转换树应该类似于下面显示的转换树:

../../_images/view_frames.png

Launching Nav2

首先,通过执行以下操作确保您已经安装了 Nav2 包:

sudo apt install ros-<ros2-distro>-navigation2
sudo apt install ros-<ros2-distro>-nav2-bringup

我们现在将使用 nav2_bringup 的内置启动文件 navigation_launch.py 启动 Nav2。打开一个新终端并执行以下操作:

ros2 launch nav2_bringup navigation_launch.py

请注意,我们在上一小节中讨论的 nav2_costmap_2d 的参数包含在 navigation_launch.py 的默认参数中。除了 nav2_costmap_2d 参数外,它还包含 Nav2 实现中包含的其他节点的参数。

在我们正确设置并启动 Nav2 后, /global_costmap/local_costmap 主题现在应该处于活动状态。

Note

要显示 costmap,请按此顺序运行 3 个命令

  1. 启动描述节点、RViz 和 Gazebo - 在日志中等待“已连接到 gazebo 主节点”

  2. 启动 slam_toolbox - 在日志中等待“正在注册传感器”

  3. 启动 Nav2 - 在日志中等待“正在创建绑定计时器”

在 RViz 中可视化 Costmaps

可以在 RViz 中可视化 global_costmaplocal_costmap 和检测到的障碍物的体素表示。

要在 RViz 中可视化 global_costmap ,请单击 RViz 窗口左下角的添加按钮。转到“按主题”选项卡,然后选择 /global_costmap/costmap 主题下的“地图”。 global_costmap 应显示在 RViz 窗口中,如下所示。 global_costmap 显示我们的机器人在 Gazebo 中的模拟世界中导航时应避开的区域(黑色)。

../../_images/costmap_global_rviz.png

To visualize the local_costmap in RViz, select the Map under the /local_costmap/costmap topic. Set the color scheme in RViz to costmap to make it appear similar to the image below. 要在 RViz 中可视化 local_costmap ,请选择 /local_costmap/costmap 主题下的 Map 。将 RViz 中的 color scheme 设置为 costmap ,使其看起来类似于下图。

../../_images/local_costmap_rviz.png

为了可视化检测到的物体的体素表示,请打开一个新终端并执行以下几行:

ros2 run nav2_costmap_2d nav2_costmap_2d_markers voxel_grid:=/local_costmap/voxel_grid visualization_marker:=/my_marker

The line above sets the topic where the the markers will be published to /my_marker. To see the markers in RViz, select Marker under the /my_marker topic, as shown below. 上面的行将标记发布到 /my_marker 的主题设置为。要在 RViz 中查看标记,请选择 /my_marker 主题下的 Marker ,如下所示。

../../_images/add_my_marker.png

然后将 RViz 中的 fixed frame 设置为 odom ,现在您应该可以看到 RViz 中的体素,它们代表我们在 Gazebo 世界中的立方体和球体:

../../_images/voxel_layer.png

结论

在我们的机器人设置指南的这一部分中,我们讨论了传感器信息对于与 Nav2 相关的不同任务的重要性。更具体地说,诸如地图绘制 (SLAM)、定位 (AMCL) 和感知 (costmap) 任务之类的任务。

我们还讨论了 Nav2 中常见的传感器消息类型,这些消息标准化了不同传感器供应商的消息格式。我们还讨论了如何使用 Gazebo 将传感器添加到模拟机器人以及如何通过 RViz 验证传感器是否正常工作。

最后,我们使用不同的层为 nav2_costmap_2d 包设置了一个基本配置,以生成全局和局部成本图。然后,我们通过在 RViz 中可视化这些成本图来验证我们的工作。