65.9K
CodeProject 正在变化。 阅读更多。
Home

Rodney - 漫长的自主机器人之路(第七部分)。

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2019 年 4 月 23 日

Apache

19分钟阅读

viewsIcon

11640

downloadIcon

486

关于 ROS(机器人操作系统)家用机器人的系列文章的第七部分。

引言

Rodney 机器人项目是一个业余机器人项目,旨在利用 ROS (机器人操作系统) 设计和构建一个自主的家庭机器人。本文是描述该项目的第七篇文章。

背景

第一部分 中,为了帮助定义我们机器人的需求,我们选择了第一个任务并将其分解为多个设计目标,以便更容易管理。

该任务取自文章 "让我们来造个机器人!",内容是:送信到... - 由于机器人将能够识别家庭成员,为什么不让它成为“消息接收者和提醒者”呢?我可以这样说:“机器人,提醒(姓名)晚上 6 点来车站接我。” 这样,即使家庭成员的手机静音了,或者正在听很大的音乐,或者(出现其他无法接我的原因),机器人也可以在家中穿行,找到那个人,并将消息传达给他们。

该任务的设计目标是

  • 设计目标1:能够使用摄像头环顾四周,搜索人脸,尝试识别任何看到的人,并显示识别出来的信息
  • 设计目标2:面部表情和语音合成。罗德尼需要能够传递信息
  • 设计目标3:通过远程键盘和/或操纵杆控制移动
  • 设计目标4:增加激光测距仪或类似的测距传感器以辅助导航
  • 设计目标5:自主移动
  • 设计目标6:任务分配和完成通知

在上一次,我们添加了一个旋转的 LIDAR (激光雷达) 以完成设计目标 4,并添加了一个 IMU 以改进里程计。在本次,我将利用 ROS Navigation Stack,最终赋予 Rodney 自主移动能力。这将包括用于 SLAM (同步定位与地图构建)、概率定位系统、全局和局部导航规划的现有 ROS 包,所有这些都将使我们朝着完成设计目标 5 迈出坚实的步伐。

Rodney 运行 SLAM 导航的视频

导航“堆栈”

在 ROS 的旧版本中,软件包可以组织成 ROS 堆栈。这是一个过时的概念,但有时您仍然会看到该术语,特别是“Nav Stack”。现在它指的是一组可用于自主导航的软件包。在本文中,我们将不编写任何新代码,而是使用现有的 ROS 包。我将解释如何配置和启动这些包,但是其中一些包含许多可配置的参数,您应该参考每个包的 ROS Wiki 以了解这些参数。

映射

为了让我们的机器人能够导航,它将需要一个世界地图。我们将用于创建地图的节点将在 Linux 工作站上运行,并将从机器人传感器记录的数据构建地图。您也可以在机器人硬件上运行该节点。ROS 包名为 gmapping,它提供基于激光的 SLAM (同步定位与地图构建)。下图显示了 gmapping 从 Rodney 的传感器创建的地图,并在 rviz 中显示。

地图将以两个文件形式保存以供将来使用:一个 .yaml 文件和一个 .pgm 文件。.pgm 文件可以在许多图像编辑工具中进行编辑,因此您始终可以整理地图并屏蔽掉您可能不希望机器人访问的区域。

您可以实时创建地图,但大多数教程都会告诉您记录传感器数据,然后从记录的数据中生成地图。这允许您在生成地图时尝试不同的参数设置。我更喜欢两者结合:记录数据,同时在 rviz 中可视化创建的地图。这可以确保您不会遗漏地图中真正需要的部分。

您可以通过手动模式缓慢驾驶机器人来创建地图。为了提高地图质量,除了缓慢驾驶外,最好多次访问一个位置。我将在“使用代码”部分展示如何记录数据以及如何创建和保存地图。

一旦我们有了地图,机器人将需要访问地图,并且像往常一样使用 ROS,这通过话题来实现。提供这些话题的节点属于 map_server 包。由于地图可能很大,包含地图的话题不会被持续发布,而是被“闩锁”的,任何需要地图的新节点都会被传递一份副本。该包除了包含 map_server 节点外,还包含另一个名为 map_saver 的节点,该节点将用于将 gmapping 创建的地图保存到磁盘。

为了在机器人硬件上启动 map_server 节点,我们将在 rodney.launch 文件中包含以下内容。

<arg name="map_file" default="second_floor"/>
<node pkg="map_server" type="map_server" name="map_server" 
      args="$(find rodney)/maps/$(arg map_file).yaml" output="screen"/> 

这意味着我们将把地图文件存储在 rodney 包中的一个新文件夹 maps 中。如果我们调用启动文件时没有提供 map_file 参数,将使用默认值 second_floor

机器人定位

这个导航拼图的下一个必需部分是机器人定位。除了跟踪它在世界上的位置外,机器人还需要知道它的起始位置。现在 odom 和激光传感器并不完美,所以机器人无法精确知道自己的位置,但它会维护一个可能的候选姿势数组。姿势是世界中的一个位置和方向。随着机器人的移动,它会缩小它认为自己所在的位置,并丢弃不太可能是其位置的姿势。

对于这个功能,我们将使用 amcl 包。AMCL 代表自适应蒙特卡洛定位,幸运的是,我们只需要配置和启动它。如果您想了解更多关于 AMCL 的信息,那么这个 维基百科页面 是一个不错的起点。

按照 ROS 的惯例,我们将通过将一些配置数据加载到参数服务器来配置节点。这些数据将存储在 rodney/config 文件夹中的 amcl_config.yaml 文件中。这是我的配置文件版本,但与所有这些导航包一样,它们高度可配置,因此请参考 ROS Wiki 了解该包。

# Overall filter parameters
min_particles: 500
max_particles: 3000
kld_err: 0.05
kld_z: 0.99
update_min_d: 0.2
update_min_a: 0.5
resample_interval: 1
transform_tolerance: 0.5
recovery_alpha_slow: 0.0
recovery_alpha_fast: 0.0
gui_publish_rate: 1.0
# Laser model parameters
laser_max_beams: 30
laser_z_hit: 0.5
laser_z_short: 0.05                 
laser_z_max: 0.05                  
laser_z_rand: 0.5                   
laser_sigma_hit: 0.2
laser_lambda_short: 0.1
laser_likelihood_max_dist: 2.0 
laser_model_type: likelihood_field  
# Odometry model parameters
odom_model_type: diff   
odom_alpha1: 0.2
odom_alpha2: 0.2
odom_alpha3: 0.8
odom_alpha4: 0.2
odom_alpha5: 0.1
odom_frame_id: odom
base_frame_id: base_footprint

为了使用给定的配置启动此节点,我们将以下内容添加到 rodney.launch 文件中。

<node pkg="amcl" type="amcl" name="amcl" output="screen">
  <rosparam command="load" file="$(find rodney)/config/amcl_config.yaml"/>
</node>

值得注意的是,我从默认值更改的大部分参数都与 nav stack 的变换容差和采样频率有关。我们要求树莓派在短时间内完成大量复杂的计算!

规划路线

现在我们进入流程的关键部分。如果我们想使用地图从 A 点到 B 点(或从姿势 A 到姿势 B)移动,需要有东西来规划路线并命令机器人移动。为此,我们将使用 move_base 包。该包需要使用 amcl 数据、地图、odom 数据和激光数据来规划最佳路线。不仅如此,它还需要考虑自地图构建以来发生的变化。如果一个人或家人走到了机器人的路径中,它将需要找到一条新路线。同样,为了理解这个包,请阅读它的 ROS Wiki 页面。

在规划路线时,该包将使用两个代价地图。代价地图显示了机器人可以存在的好地方和坏地方。好地方是在开阔区域,坏地方是靠墙。一个代价地图是全局代价地图,基于地图且保持静态。这张地图用于规划全局路线,但如前所述,如果地图发生变化了怎么办?这就是局部代价地图的作用。它会随着机器人的移动而更新,而全局代价地图覆盖整个地图,局部代价地图仅覆盖机器人周围的即时位置。

move_base 包可以使用许多全局和局部规划器,只要它们符合 nav_core::BaseGlobalPlanner 接口用于全局规划和 nav_core::BaseLocalPlanner 接口用于局部规划。所以您甚至可以编写自己的规划器。

有默认的规划器供您使用,或者您可以在配置文件中声明您正在使用的规划器。我将使用 global_plannerdwa_local_planner

我们需要 move_base 包的几个配置文件,它们将被加载到参数服务器中。每个文件将位于 rodney/config 文件夹中。

base_local_planner_params.yaml

controller_frequency: 5.0
use_grid_path: true
base_global_planner: global_planner/GlobalPlanner
base_local_planner: dwa_local_planner/DWAPlannerROS
DWAPlannerROS:
  acc_lim_x: 0.75
  acc_lim_y: 0.0
  acc_lim_theta: 1.75                 # wiki calls it acc_lim_th but it is acc_lim_theta
  acc_lim_trans: 1.0
  max_trans_vel: 0.25
  min_trans_vel: 0.1
  max_vel_x: 0.25                     # The maximum forward velocity
  min_vel_x: -0.1
  max_vel_y: 0.0
  min_vel_y: 0.0
  max_rot_vel: 2.5
  min_rot_vel: 0.3
  yaw_goal_tolerance: 0.3
  xy_goal_tolerance: 0.25
  sim_time: 2.0
  sim_granularity: 0.1
  vx_samples: 3
  vy_samples: 10
  vth_samples: 20
  path_distance_bias: 32.0
  goal_distance_bias: 24.0
  occdist_scale: 0.05
  forward_point_distance: 0.3
  stop_time_buffer: 0.5
  scaling_speed: 0.25
  max_scaling_factor: 0.2
  oscillation_reset_dist: 0.05
  prune_plan: true
    
  trans_stopped_vel: 0.1
  rot_stopped_vel: 0.1 
  angular_sim_granularity: 0.1    
  twirling_scale: 0.0
  oscillation_reset_angle: 0.2
  use_dwa: true

在这里,我们设置将要使用的规划器以及局部规划器的许多参数。再次,请参考每个参数的相关 Wiki。大多数参数都可以通过动态重配置进行动态设置,我们在 Rodney 的先前文章中已使用过。值也非常特定于每个机器人及其环境。例如,曾经,我增加了最大速度和旋转速度,以为它能够更快地在狭窄的拐角处转弯,但结果机器人却卡在了障碍物上。似乎通过加速,机器人在计算出是糟糕的区域之前就陷入了问题区域。

代价地图在全局代价地图和局部代价地图之间有一些通用参数,以及一些只对全局或局部代价地图特有的参数。

通用参数存储在 costmap_common_params.yaml 文件中

obstacle_range: 2.5
raytrace_range: 3.0
footprint: [[0.170, 0.145], [-0.170, 0.145], 
           [-0.170, -0.145], [0.170, -0.145]] # Simple rectangle taking in the base and wheels
inflation_radius: 0.55
observation_sources: laser_scan_sensor
laser_scan_sensor:
  sensor_frame: laser
  data_type: LaserScan
  topic: scan
  marking: true
  clearing: true

对于足迹,我开始时使用了复杂的形状,考虑了从底座突出的车轮。但为了使计算不那么复杂,我将其更改为一个简单的矩形,其中包含底座和车轮。

对全局代价地图特有的参数存储在 global_costmap_params.yaml 文件中

global_costmap:
  global_frame: map
  robot_base_frame: base_footprint
  static_map: true
  transform_tolerance: 0.5
  update_frequency: 1.0

对局部代价地图特有的参数存储在 local_costmap_params.yaml 文件中

local_costmap:
  global_frame: odom
  robot_base_frame: base_footprint
  static_map: false
  rolling_window: true
  transform_tolerance: 0.5
  update_frequency: 1.0
  publish_frequency: 0.5
  width: 2.5
  height: 2.5

同样,为了帮助计算,我使用了机器人周围相对较小的局部代价地图,尺寸为 2.5x2.5 米。请记住,局部代价地图用于规划绕过对象(这些对象可能在创建静态地图后发生了移动)的路线,而全局代价地图是基于此的。增加宽度和高度可以使机器人提前规划,但代价是处理器需要进行的计算量。

此时可能值得注意的是,尽管我在树莓派 3B 上实现了导航功能,但我已升级到树莓派 3B+,以利用其稍快的处理器。

要启动 move_base 节点并加载所有参数,请在 rodney.lunch 文件中添加以下内容

<node pkg="move_base" type="move_base" name="move_base" respawn="false" output="screen">
  <rosparam command="load" file="$(find rodney)/config/base_local_planner_params.yaml"/>
  <rosparam command="load" file="$(find rodney)/config/costmap_common_params.yaml" 
                           ns="global_costmap"/>
  <rosparam command="load" file="$(find rodney)/config/costmap_common_params.yaml" 
                           ns="local_costmap"/>
  <rosparam command="load" file="$(find rodney)/config/global_costmap_params.yaml"/>
  <rosparam command="load" file="$(find rodney)/config/local_costmap_params.yaml"/>       
  <remap from="cmd_vel" to="demand_vel"/>
</node>

move_base 发布的话题通常是 cmd_vel,这是我们的 thunderborg 节点订阅的话题名称。由于我们希望能够手动驾驶机器人,因此我们的 rodney 节点被设置为在 cmd_vel 主题上发布数据,具体取决于机器人的当前模式,发布手动驾驶数据或自主数据。因此,我们在此将话题名称从 cmd_vel 重映射为 demand_vel

我还要对 rodney.launch 文件做一个更改,以便能够启用或禁用导航堆栈来启动软件。默认情况下将启用导航堆栈,但如果我们想远程控制机器人以生成新地图,则不希望运行地图服务器,也不希望启动其他正在查找地图的节点。因此,我将启动导航堆栈各部分的区域包装在“group unless”标签中。

完整的 rodney.launch 文件将在下面重现

<?xml version="1.0" ?>
<launch>    
  <!-- Static transforms in the system -->
  <node pkg="rodney" type="static_broadcaster.py" name="static_broadcaster_node"/>
  
  <!-- Load each of the config files into the parameter server -->           
  <rosparam command="load" file="$(find pan_tilt)/config/config.yaml"/>
  <rosparam command="load" file="$(find face_recognition)/config/config.yaml"/>
  <rosparam command="load" file="$(find head_control)/config/config.yaml"/>
  <rosparam command="load" file="$(find rodney_missions)/config/config.yaml"/>

  <!-- map and localization system -->
  <arg name="no_nav" default="false"/>  
  <group unless="$(arg no_nav)">
    <arg name="map_file" default="second_floor"/>
    <node pkg="map_server" type="map_server" name="map_server" 
          args="$(find rodney)/maps/$(arg map_file).yaml" output="screen"/> 
    <node pkg="amcl" type="amcl" name="amcl" output="screen">
      <rosparam command="load" file="$(find rodney)/config/amcl_config.yaml"/>
    </node>     
  </group>    
    
  <!-- Launch the camera node from one of its launch files -->
  <include file="$(find raspicam_node)/launch/camerav2_1280x960.launch" /> 
  
  <!-- Start all the nodes that make up Rondey -->
  <!-- Starting with those written for the project -->
  <node pkg="pan_tilt" type="pan_tilt_node" name="pan_tilt_node"/>
  <node pkg="face_recognition" type="face_recognition_node.py" name="face_recognition_node"/>
  <node pkg="head_control" type="head_control_node" name="head_control_node"/>  
  <node pkg="speech" type="speech_node" name="speech_node"/>
  <node pkg="rodney_missions" type="rodney_missions_node.py" 
        name="rodney_missions" output="screen"/>
  <node pkg="rodney" type="rodney_node" name="rodney" output="screen">
    <rosparam command="load" file="$(find rodney)/config/config.yaml"/> 
  </node>
  <node pkg="thunderborg" type="thunderborg_node.py" name="thunderborg_node">
    <rosparam command="load" file="$(find thunderborg)/config/config.yaml"/>
  </node>
  
  <!-- Teensy. 
       Use the defaults /dev/ttyACM0 (or teensy if dev rules updated) and 500000 -->
  <arg name="serial_port" default="/dev/teensy"/>
  <arg name="baud_rate" default="500000"/>  
  <node pkg="rosserial_python" type="serial_node.py" name="serial_node" output="screen">
    <param name="port" value="$(arg serial_port)"/>
    <param name="baud" value="$(arg baud_rate)"/>
  </node>
  
  <!-- The RPLidar and laser filter node 
       Have created symbolic link for /dev/ttyUSBn to be rplidar -->
  <node pkg="rplidar_ros" type="rplidarNode" name="rplidar_node" output="screen">
    <param name="serial_port" type="string" value="/dev/rplidar"/>
    <param name="serial_baudrate" type="int" value="115200"/>
    <param name="frame_id" type="string" value="laser"/>
    <remap from="scan" to="scan_filter_input"/>
  </node>
  <node pkg ="laser_filters" type="scan_to_scan_filter_chain" 
        name="scan_to_scan_filter_chain" output="screen">
    <rosparam command="load" file="$(find rodney)/config/laser_filter_config.yaml"/>
    <remap from="scan" to="scan_filter_input"/>
    <remap from="scan_filtered" to="scan"/>
  </node>
  
  <!-- The robot face -->
  <node pkg="homer_robot_face" type="RobotFace" name="RobotFace"/>
  
  <!-- Add calibration to raw imu data -->
  <node pkg="imu_calib" type="apply_calib" name="imu_calib" output="screen">
    <param name="calib_file" value="$(find rodney)/config/imu_calib.yaml"/>
  </node>
    
  <!-- Node to fuse motor encoder and IMU data for odom -->
  <node pkg="robot_localization" type="ekf_localization_node" name="ekf_localization_node">
    <remap from="odometry/filtered" to="odom"/>
    <rosparam command="load" file="$(find rodney)/config/robot_localization.yaml"/> 
  </node>
  
  <!-- Navigation --> 
  <group unless="$(arg no_nav)">   
    <node pkg="move_base" type="move_base" 
             name="move_base" respawn="false" output="screen">
      <rosparam command="load" file="$(find rodney)/config/base_local_planner_params.yaml"/>
      <rosparam command="load" 
       file="$(find rodney)/config/costmap_common_params.yaml" ns="global_costmap"/>
      <rosparam command="load" 
       file="$(find rodney)/config/costmap_common_params.yaml" ns="local_costmap"/>
      <rosparam command="load" file="$(find rodney)/config/global_costmap_params.yaml"/>
      <rosparam command="load" file="$(find rodney)/config/local_costmap_params.yaml"/>       
      <remap from="cmd_vel" to="demand_vel"/>
    </node>   
  </group>
   
</launch>

在下一篇文章中,我们将修改 rodney_missions 节点以编程方式运行自主导航,并将其与面部识别功能结合起来,在房子里漫游寻找某个已知的人来传递消息。

在“使用代码”部分,我们将创建一个地图并使用 rviz 输入机器人将自主导航到的姿势。

机器人硬件

当前电路图的全尺寸图像可在图表 zip 文件夹中找到,以及显示所有节点和主题的 rqt_graph 图像的全尺寸副本。

到目前为止,该项目的完整物料清单 可在此处获取

在本文的第一部分,我提到了我在树莓派上使用的 Ubiquity Robot Image。安装图像、安装额外软件和为项目配置的说明 可在此处获取

Using the Code

像往常一样,我将在机器人硬件上运行代码,并在Linux PC上运行测试工具和手动控制节点。在下面的细节中,我将这台PC称为工作站。

在Pi上构建ROS包(机器人硬件)

如果尚未完成,请在 Raspberry Pi 上创建一个 catkin 工作区,并使用以下命令初始化它

$ mkdir -p ~/rodney_ws/src
$ cd ~/rodney_ws/
$ catkin_make

face_recognition, face_recognition_msgs, head_control, imu_calib, pan_tilt, rodney, rodney_missions, ros-keyboard, rplidar-ros, servo_msgs, speech, tacho_msgsthunderborg 包复制到 ~/rodney_ws/src 文件夹。

使用以下命令构建代码

$ cd ~/rodney_ws/ 
$ catkin_make

检查构建是否无任何错误地完成。

您还需要编译并将草图下载到 Teensy 3.5。

在工作站上构建ROS包

在工作站上,我们希望运行键盘、操纵杆和心跳节点,以便能够远程控制实际的机器人硬件。

使用以下命令创建一个工作空间

$ mkdir -p ~/test_ws/src 
$ cd ~/test_ws/ 
$ catkin_make

rodney, joystick, 和 ros-keyboard 包复制到 ~/test_ws/src 文件夹,然后使用以下命令构建代码

$ cd ~/test_ws/ 
$ catkin_make

检查构建是否无任何错误地完成。

提示

在工作站和树莓派上运行 ROS 代码和工具时,可能需要在多个终端中重复输入许多命令。在接下来的部分中,我将包含需要键入的所有命令,但这里有一些技巧可以为您节省大量输入。

在 Raspberry Pi 上,为了节省输入 "source devel/setup.bash",我已将其添加到 Raspberry Pi 的 .bashrc 文件中。

$ cd ~/
$ nano .bashrc

然后将 "source /home/ubuntu/rodney_ws/devel/setup.bash" 添加到文件末尾,保存并退出。

在工作站上运行测试代码和工具时,它也需要知道 ROS master 的位置,所以我已经将以下内容添加到工作站的 .bashrc 文件中。

alias rodney='source ~/test_ws/devel/setup.bash; \
export ROS_MASTER_URI=http://ubiquityrobot:11311'

然后,只需在终端中键入“rodney”,即可运行这两个命令,并节省大量输入。

您还可以节省一些输入,因为某些ROS工具支持TAB补全。例如,键入rosrun rosserial_,然后按Tab键自动补全rosrun rosserial_python

监控日志文件

当机器人导航时,您可能想关注日志消息。这些消息将显示在您从中启动代码的终端中,但这对于机器人移动到不同房间时没有帮助。您可以使用以下命令在工作站上查看日志

$ export ROS_MASTER_URI=http://ubiquityrobot:11311
$ rqt_console

创建地图

在机器人硬件上,运行以下命令以启动系统中所有当前节点(导航堆栈除外)

$ source rodney_ws/devel/setup.bash
$ roslaunch rodney rodney.launch no_nav:=True

在工作站上,运行以下命令以启动远程控制节点

$ source test_ws/devel/setup.bash 
$ export ROS_MASTER_URI=http://ubiquityrobot:11311 
$ roslaunch rodney remote.launch

一个标题为“ROS 键盘输入”的小窗口应该正在运行。输入键盘按键时,请确保小窗口处于焦点。

接下来,我们将开始记录变换和激光扫描消息,以便我们展示如何从记录的数据创建地图。在终端中,使用以下命令开始录制

$ export ROS_MASTER_URI=http://ubiquityrobot:11311 
$ rosbag record -O data.bag /scan /tf

在这里,我们将启动 slam_gmapping,以便我们可以看到实时创建的地图。我还会限制地图的大小。在工作站的终端中,运行以下命令

$ export ROS_MASTER_URI=http://ubiquityrobot:11311 
$ rosparam set slam_gmapping/xmax 10
$ rosparam set slam_gmapping/ymax 10
$ rosparam set slam_gmapping/xmin -10
$ rosparam set slam_gmapping/ymin -10
$ rosparam set slam_gmapping/delta 0.05
$ rosrun gmapping slam_gmapping

作为替代方案,我创建了一个名为 mapping_launch 的测试包,其中包含一个名为 mapping.launch 的启动文件,该文件将设置参数并启动 gmapping。此包可在 Robotics-test-code 文件夹中找到。

在工作站的另一个终端中,使用以下命令启动 rviz

$ source test_ws/devel/setup.bash 
$ export ROS_MASTER_URI=http://ubiquityrobot:11311 
$ roslaunch rodney rviz.launch

配置 rviz,以便

  • 固定帧为 map
  • LaserScan 显示 /scan 主题
  • TF 显示 base_link
  • Map 显示 /map 主题

使用操纵杆和/或键盘,进入手动模式,确保 LIDAR 电机已运行,并手动驱动机器人绕其世界行驶。慢行并至少访问每个位置两次。创建的地图应该可以在 rviz 中看到。

创建地图后,按 Ctrl-C 终止正在运行 rosbag 的终端,以停止存储消息。

现在,我们可以将 rviz 中显示的地图保存到磁盘。在 slam_gmapping 仍然运行时,在运行 rosbag 的终端中输入以下命令

$ rosrun map_server map_saver -f my_first_map

这将导致保存两个文件:my_first_map.yamlmy_first_map.pgm

此时,如果您愿意,可以从 rosbag 文件重新生成地图,并使用不同的 gmapping 参数。在这里,我们不希望工作站上运行任何现有节点,因此关闭并关闭所有终端。您也可以关闭机器人。

在工作站上,我们需要运行一个 ROS 主节点(在之前的设置中,它是在机器人硬件上自动运行的),在一个终端中输入以下命令

$ roscore

在另一个终端中,使用 rosparam 设置所需的 gmapping 参数,然后键入以下命令

$ rosparam set use_sim_time_true 
$ rosrun gmapping slam_gmapping

在另一个终端中,使用以下命令回放之前记录的 rosbag 文件

$ rosbag play --clock data.bag

然后,坐下来看 gmapping 创建地图。如果您愿意,可以启动 rviz(不带 export ROS_MASTER_URI 命令),然后观察地图的创建过程。回放完所有消息后,使用以下命令将新地图保存到磁盘

$ rosrun map_server map_saver -f my_second_map

您可以在这里随意调整 gmapping 配置参数,并反复回放录制的 bag 文件,以查看参数对生成地图的影响。

自主导航

现在是我们一直期待的部分:自主导航。

在机器人硬件上,运行以下命令以启动系统中所有当前节点,包括导航堆栈。我将使用我的默认地图,但您可以通过在 roslaunch 命令末尾添加“map_file:=my_first_map”来设置地图。

$ source rodney_ws/devel/setup.bash
$ roslaunch rodney rodney.launch

在工作站上,运行以下命令以启动远程控制节点

$ source test_ws/devel/setup.bash 
$ export ROS_MASTER_URI=http://ubiquityrobot:11311 
$ roslaunch rodney remote.launch

一个标题为“ROS 键盘输入”的小窗口应该正在运行。输入键盘按键时,请确保小窗口处于焦点。

在工作站的另一个终端中,使用以下命令启动 rviz

$ source test_ws/devel/setup.bash 
$ export ROS_MASTER_URI=http://ubiquityrobot:11311 
$ roslaunch rodney rviz.launch

机器人定位

配置 rviz 以显示机器人模型或 base_link 轴、激光扫描、地图和姿势估计。还要确保地图是固定帧。

从显示中可以看出,激光扫描与地图不匹配,姿势估计分布散乱。因此,在我们给机器人设置导航目标之前,我们需要改进它的定位。

下图显示了一个定位不良的机器人。红线是激光扫描,绿箭头是姿势估计。

我们将执行的第一个操作是使用 rviz 为机器人提供改进的定位。单击“2D Pose Estimate”按钮,估计机器人的实际位置和姿势,然后单击/拖动地图上的大绿色箭头来设置初始姿势。您可以一直这样做,直到激光扫描与地图匹配为止。

现在我们有了一个良好的初始姿势,但姿势估计仍然不准确。我们可以通过手动模式驱动机器人来改进它们。原地旋转是一个很好的操作。在移动机器人的同时,您应该会看到姿势估计收敛到机器人的位置。

设置导航目标

现在我们准备让机器人出发了,但首先让我们看一下将用于规划路线的代价地图。在 rviz 中选择 Global Planning 以显示 Global Costmap。对于 Global Costmap,我喜欢选择“Draw Behind”,以便地图在主地图后面被冲淡。

从代价地图中,您可以看到规划器将尝试使用的开放空间以及像靠墙这样的危险区域。

现在,全局代价地图是根据主地图构建的,将用于规划理想路线,但机器人的实际移动将由局部代价地图控制,局部代价地图将在传感器数据到达时即时生成。这将使机器人能够避开地图创建时不存在的物体,例如,睡着的宠物。

rviz 中选择 Local Planning 以显示 Local Costmap。我喜欢将此地图叠加在主地图之上。

现在通过单击“2D Nav Goal”按钮来设置目标姿势,然后单击/拖动地图上的大绿色箭头来设置目标。我现在故意将机器人保持在手动模式,所以它不会移动,这给了我们一个机会来检查全局规划,全局规划显示为主图上的细绿色线。下图。

我们可以通过将焦点放在 **ROS 键盘输入** 窗口并按“1”键(非数字小键盘)来将机器人置于自主模式。这是运行任务 1 的请求,该任务当前为空,因此它所做的就是让机器人退出手动模式,这样导航堆栈生成的速度而不是操纵杆/键盘的速度将被发送到控制电机的 thundeborg 节点。

希望机器人能够导航到目标姿势,您可以在 rviz 上监视进度。下图显示机器人已到达目标。

我注意到,在设置了多个移动目标姿势后,最终尽管代价地图上路线清晰,导航堆栈却无法计算出路线。您可以在工作站上使用以下命令清除此问题

$ rosservice call /move_base/clear_costmaps

关注点

在这一部分,我们添加了自主导航。

在下一篇文章中,我们将以编程方式设置导航目标,并将面部识别功能包含在一个任务中,以便 Rodney 可以在家中漫游,寻找他应该传递消息的人。

虽然我在源代码 zip 文件中包含了当前开发的所有代码版本,但这些代码也可在我的 GitHub 站点 上找到。每个包都在自己的存储库中。请随意 Fork 存储库用于您自己的项目,进行更新,如果您觉得任何更改对我或他人都有帮助,请创建 Pull Request。

历史

  • 2019/04/23:初始发布
© . All rights reserved.