Skip to content

Gazebo 传感器设置

本指南介绍传感器在安全导航中的重要性,以及如何为 Nav2 配置传感器。前半部分简要介绍 Nav2 中常用的传感器及常见传感器消息类型,然后在此前构建的仿真机器人 sam_bot 上添加基本传感器,并在 RViz 中可视化验证仿真传感器数据。

传感器读数可用于建图、定位和感知。后半部分讨论建图与定位如何使用传感器数据,并介绍 Nav2 的 nav2_costmap_2d 包——它生成路径规划所需的代价地图(costmap)。下面为该包配置基本参数,使其正确接收 sam_bot 的传感器数据,最后在 RViz 中可视化生成的代价地图以验证结果。

移动机器人配备大量传感器来感知周围环境。传感器数据可用于构建和维护环境地图、在地图上定位机器人,以及检测环境中的障碍物——这些任务对于在动态环境中安全高效地导航至关重要。

常用传感器包括激光雷达、雷达、RGB 相机、深度相机、IMU 和 GPS。为统一传感器消息格式、方便不同厂商产品互操作,ROS 提供了 sensor_msgs 包来定义标准传感器接口。只要传感器遵循 sensor_msgs 标准,就可以自由选用不同厂商的产品。下一小节将介绍导航中常用的消息类型:sensor_msgs/LaserScan、sensor_msgs/PointCloud2、sensor_msgs/Range 和 sensor_msgs/Image。

除 sensor_msgs 外,还有 radar_msgs 和 vision_msgs 标准接口。radar_msgs 定义了雷达专用消息,vision_msgs 定义了计算机视觉相关消息,如目标检测、分割等机器学习任务的输出。支持的消息包括 vision_msgs/Classification2D、vision_msgs/Classification3D、vision_msgs/Detection2D 和 vision_msgs/Detection3D 等。

注意:更多详情请参阅 sensor_msgs、radar_msgs 和 vision_msgs 的 API 文档。

实体机器人的传感器通常都有对应的 ROS 驱动程序(即连接传感器、将数据填充到消息并发布的 ROS 节点),遵循 sensor_msgs 标准接口。借助这一标准,用户可轻松搭配不同厂商的传感器,Nav2 等通用软件包也能读取这些标准化消息,执行与具体硬件无关的任务。在 sam_bot 等仿真机器人中,Gazebo 的传感器插件同样按照 sensor_msgs 格式发布数据。

本小节介绍设置 Nav2 时常见的几种 sensor_msgs 类型,分别给出简要说明、Gazebo 仿真截图以及 RViz 中的读数可视化。

注意:除下面列出的类型外,还有其他 sensor_msgs 类型。完整的消息列表及定义请参阅 sensor_msgs 文档。

该消息表示平面激光测距仪的单次扫描,用于 slam_toolbox 和 nav2_amcl 的定位与建图,或 nav2_costmap_2d 的感知。

sensor_laserscan

该消息包含一组 3D 点及每个点的可选附加信息,数据来源可以是 3D 激光雷达、2D 激光雷达、深度相机等。

sensor_pointcloud2

该消息来自有源测距仪的单次距离读数。测距仪发射能量并报告沿一段弧线有效的单个距离值。声呐、红外传感器和一维测距仪都是使用该消息的典型传感器。

sensor_range

该消息表示来自 RGB 或深度相机的读数,分别对应 RGB 值或距离值。

sensor_image

本节以上一个教程为基础,为仿真机器人 sam_bot 添加传感器。与上一个教程中使用 Gazebo 插件添加里程计类似,这里用 Gazebo 插件在 sam_bot 上仿真一个激光雷达和一个深度相机。如果使用的是真实机器人,以下步骤中大部分对于设置 URDF 坐标系仍然必要,提前添加 Gazebo 插件也方便后续使用。

请确保已正确安装 Gazebo,安装步骤可参考上一个教程中的设置与先决条件。

首先为 sam_bot 添加激光雷达。在 URDF 的 </robot> 标签前粘贴以下内容,添加激光雷达的连杆和关节:

<link name="lidar_link">
<inertial>
<origin xyz="0 0 0" rpy="0 0 0"/>
<mass value="0.125"/>
<inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001" />
</inertial>
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<cylinder radius="0.0508" length="0.055"/>
</geometry>
</collision>
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<cylinder radius="0.0508" length="0.055"/>
</geometry>
</visual>
</link>
<joint name="lidar_joint" type="fixed">
<parent link="base_link"/>
<child link="lidar_link"/>
<origin xyz="0 0 0.12" rpy="0 0 0"/>
</joint>

在 SDF 的 </model> 行前添加以下内容,包含激光雷达的连杆、关节及 Gazebo 传感器配置:

<joint name="lidar_joint" type="fixed">
<parent>base_link</parent>
<child>lidar_link</child>
<pose relative_to="base_link">0.0 0.0 0.12 0 0 0</pose>
</joint>
<link name='lidar_link'>
<pose relative_to="lidar_joint"/>
<visual name="lidar_link_visual">
<geometry>
<cylinder>
<radius>0.0508</radius>
<length>0.055</length>
</cylinder>
</geometry>
</visual>
<collision name="lidar_link_collision">
<geometry>
<cylinder>
<radius>0.0508</radius>
<length>0.055</length>
</cylinder>
</geometry>
</collision>
<xacro:cylinder_inertia m="0.125" r="0.0508" h="0.055"/>
<sensor name="lidar" type="gpu_lidar">
<always_on>true</always_on>
<visualize>true</visualize>
<update_rate>5</update_rate>
<topic>scan</topic>
<gz_frame_id>lidar_link</gz_frame_id>
<lidar>
<scan>
<horizontal>
<samples>360</samples>
<resolution>1.000000</resolution>
<min_angle>0.000000</min_angle>
<max_angle>6.280000</max_angle>
</horizontal>
</scan>
<range>
<min>0.120000</min>
<max>3.5</max>
<resolution>0.015000</resolution>
</range>
<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.01</stddev>
</noise>
</lidar>
</sensor>
</link>

上面的代码片段为 lidar_link 配置了仿真激光雷达的扫描范围和距离参数,并指定 /scan 作为发布 sensor_msgs/LaserScan 消息的话题。

接下来为 sam_bot 添加深度相机。在 URDF 中激光雷达的 </joint> 标签之后粘贴以下内容,添加相机的连杆和关节:

<link name="camera_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.015 0.130 0.022"/>
</geometry>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.015 0.130 0.022"/>
</geometry>
</collision>
<inertial>
<origin xyz="0 0 0" rpy="0 0 0"/>
<mass value="0.035"/>
<inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001" />
</inertial>
</link>
<joint name="camera_joint" type="fixed">
<parent link="base_link"/>
<child link="camera_link"/>
<origin xyz="0.215 0 0.05" rpy="0 0 0"/>
</joint>

在 SDF 中激光雷达的 </link> 标签之后粘贴以下内容,添加相机的连杆、关节及 Gazebo 传感器配置:

<joint name="camera_joint" type="fixed">
<parent>base_link</parent>
<child>camera_link</child>
<pose relative_to="base_link">0.215 0 0.05 0 0 0</pose>
</joint>
<link name='camera_link'>
<pose relative_to="camera_joint"/>
<visual name="camera_link_visual">
<geometry>
<box><size>
0.015 0.130 0.0222
</size></box>
</geometry>
</visual>
<collision name="camera_link_collision">
<geometry>
<box><size>
0.015 0.130 0.0222
</size></box>
</geometry>
</collision>
<xacro:box_inertia m="0.035" w="0.015" d="0.130" h="0.0222"/>
<sensor name="depth_camera" type="rgbd_camera">
<always_on>true</always_on>
<visualize>true</visualize>
<update_rate>5.0</update_rate>
<topic>depth_camera</topic>
<gz_frame_id>camera_link</gz_frame_id>
<camera>
<horizontal_fov>1.047198</horizontal_fov>
<image>
<width>640</width>
<height>480</height>
</image>
<clip>
<near>0.05</near>
<far>3</far>
</clip>
</camera>
<baseline>0.2</baseline>
<pointCloudCutoff>0.5</pointCloudCutoff>
<pointCloudCutoffMax>3.0</pointCloudCutoffMax>
<distortionK1>0</distortionK1>
<distortionK2>0</distortionK2>
<distortionK3>0</distortionK3>
<distortionT1>0</distortionT1>
<distortionT2>0</distortionT2>
<CxPrime>0</CxPrime>
<Cx>0</Cx>
<Cy>0</Cy>
<focalLength>0</focalLength>
<hackBaseline>0</hackBaseline>
</sensor>
</link>

上面的代码片段为 camera_link 配置了仿真深度相机参数,并指定传感器分别向 /depth_camera/image_raw 和 /depth_camera/points 话题发布 sensor_msgs/Image 和 sensor_msgs/PointCloud2 消息。

还需将所需传感器话题从 Gazebo 桥接到 ROS,在 bridge_config.yaml 中添加以下内容:

- ros_topic_name: "/scan"
gz_topic_name: "/scan"
ros_type_name: "sensor_msgs/msg/LaserScan"
gz_type_name: "gz.msgs.LaserScan"
direction: GZ_TO_ROS
- ros_topic_name: "/scan/points"
gz_topic_name: "/scan/points"
ros_type_name: "sensor_msgs/msg/PointCloud2"
gz_type_name: "gz.msgs.PointCloudPacked"
direction: GZ_TO_ROS
- ros_topic_name: "/depth_camera/camera_info"
gz_topic_name: "/depth_camera/camera_info"
ros_type_name: "sensor_msgs/msg/CameraInfo"
gz_type_name: "gz.msgs.CameraInfo"
direction: GZ_TO_ROS
- ros_topic_name: "/depth_camera/points"
gz_topic_name: "/depth_camera/points"
ros_type_name: "sensor_msgs/msg/PointCloud2"
gz_type_name: "gz.msgs.PointCloudPacked"
direction: GZ_TO_ROS
- ros_topic_name: "/depth_camera/image_raw"
gz_topic_name: "/depth_camera/image"
ros_type_name: "sensor_msgs/msg/Image"
gz_type_name: "gz.msgs.Image"
direction: GZ_TO_ROS

现在构建并运行项目。进入项目根目录,执行以下命令:

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

RViz 和 Gazebo 随即启动,sam_bot 将同时出现在两者中。Gazebo 窗口会加载已创建的世界,sam_bot 在其中生成。此时可看到带有 360 度激光雷达和深度相机的 sam_bot,如下图所示。

gazebo_sensors

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

rviz_sensors

还可以在 RViz 中可视化传感器读数。要可视化 /scan 话题上的 sensor_msgs/LaserScan 消息,点击 RViz 窗口底部的 Add 按钮,切换到 By topic 选项卡,在 /scan 下选择 LaserScan,如下图所示。

add_topic_laserscan

然后将 RViz 中的 Reliability Policy 设为 Best Effort,size 设为 0.1,以便更清晰地观察点云。此时应能看到如下所示的 LaserScan 可视化结果,对应的是添加到 Gazebo 世界中的立方体和球体。

demo_laserscan_rviz

可视化 sensor_msgs/Image 和 sensor_msgs/PointCloud2 的方式相同,分别对 /depth_camera/image_raw 和 /depth_camera/points 话题执行上述操作:

add_topic_image_pointcloud2

在 RViz 中添加 /depth_camera/image_raw 话题后,将 Reliability Policy 设为 Best Effort,即可在 RViz 左下方的图像窗口中看到立方体画面,如下图所示。

demo_image_rviz

同样,sensor_msgs/PointCloud2 的可视化效果如下所示。

pointcloud2_data

本指南介绍了 Nav2 中常见的传感器消息类型——它们统一了不同厂商传感器的消息格式,并演示了如何使用 Gazebo 为仿真机器人添加传感器,以及如何通过 RViz 验证传感器是否正常工作。