- 避障是如何工作的
- 什么是局部规划器?
- 什么是局部成本图?
- 路径规划回顾
- 如何使用动态重新配置和其他 Rviz 工具
局部规划器
一旦全局规划器计算出要遵循的路径,该路径就会发送给局部规划器。然后,局部规划器将执行全局规划的每个部分(让我们将局部规划想象为全局规划的一小部分)。因此,给定要遵循的规划(由全局规划器提供)和地图,局部规划器将提供速度命令以移动机器人。
与全局规划器不同,局部规划器监视里程表和激光数据,并为机器人选择无碰撞的局部规划(让我们将局部规划想象为全局规划的一小部分)。因此,局部规划器可以动态重新计算机器人的路径,以防止机器人撞到物体,但仍允许它到达目的地。
一旦计算出局部规划,它将发布到名为 /local_plan 的主题中。局部规划器还会将其尝试遵循的全局规划部分发布到主题 /global_plan 中。让我们做一个练习,以便你更好地理解这一点。
练习
a) 打开 Rviz 并添加显示,以便能够可视化局部规划器的 /global_plan 和 /local_plan 主题。
b) 向机器人发送目标姿势并可视化两个主题。
和全局规划器一样,还有不同类型的局部规划器。根据您的设置(您使用的机器人、它导航的环境等)和您想要的性能类型,您将使用其中一种或另一种。让我们来看看最重要的几种。
base_local_planner
base_local_planner提供了轨迹展开(Trajectory Rollout)和动态窗口方法(Dynamic Window Approach, DWA)算法的实现,用于计算和执行机器人的全局路径规划。
总结一下,这些算法的基本工作原理如下:
- 从机器人的控制空间中离散地采样。
- 对于每个采样的速度,从机器人的当前状态开始执行前向模拟,以预测应用该速度后的情况。
- 评估每条前向模拟得到的轨迹。
- 丢弃不合法的轨迹。
- 选择得分最高的轨迹,并将相关的速度发送到移动底盘。
- 重复以上步骤。
DWA 与轨迹展开的不同之处在于机器人的空间采样方式。轨迹展开从整个前向模拟期间内可实现的速度集合中进行采样,考虑了机器人的加速度限制;而 DWA 则仅在一个模拟步骤中从可实现的速度集合中进行采样,考虑了机器人的加速度限制。
由于 DWA 采样的空间较小,因此它是一种更高效的算法,但对于加速度限制较低的机器人,DWA 可能不如轨迹展开表现得好,因为 DWA 不会对恒定加速度进行前向模拟。在实际应用中,DWA 和轨迹展开的性能类似,因此推荐使用 DWA 以获得效率上的优势。
base_local_planner的 DWA 算法在一个新的局部规划器中得到了改进,这就是我们接下来要介绍的 DWA local planner。
dwa_local_planner
DWA local planner 提供了动态窗口方法(Dynamic Window Approach, DWA)算法的实现。它基本上是对基础局部规划器的 DWA 选项的重写,但代码更简洁、更易于理解,特别是在轨迹模拟方面。
因此,对于使用 DWA 方法进行局部规划的应用,dwa_local_planner 可能是最佳选择。这是最常用的选项。
eband_local_planner
eband_local_planner实现了弹性带(Elastic Band)方法,以计算要跟随的局部路径。
teb_local_planner
teb 局部规划器实现了有时间弹性带(Timed Elastic Band)方法,以计算要跟随的局部路径。
修改Local Planner
move_base 节点使用的局部规划器在 base_local_planner 参数中指定。可以在参数文件中设置,如下例所示:
base_local_planner: "base_local_planner/TrajectoryPlannerROS" # Sets the Trajectory Rollout algorithm from base local planner base_local_planner: "dwa_local_planner/DWAPlannerROS" # Sets the dwa local planner base_local_planner: "eband_local_planner/EBandPlannerROS" # Sets the eband local planner base_local_planner: "teb_local_planner/TebLocalPlannerROS" # Sets the teb local planner
或者可以直接在启动文件中设置,就像我们的例子一样:
<arg name="base_local_planner" default="dwa_local_planner/DWAPlannerROS"/>
正如您所期望的,每个局部规划器也有自己的参数。这些参数将根据您使用的局部规划器而有所不同。在本课程中,我们将重点介绍 DWA 局部规划器参数,因为它是最常见的选择。无论如何,如果您想检查其他局部规划器的具体参数,您可以在此处查看:
base_local_planner: base_local_planner - ROS Wiki
eband_local_planner: eband_local_planner - ROS Wiki
teb_local_planner: teb_local_planner - ROS Wiki
DWAPlannerROS 参数
如果您检查包 my_move_base_launcher 中的文件 my_move_base_params.yaml,您将看到 DWAPlannerROS 规划器的参数:
DWAPlannerROS: # Robot configuration parameters acc_lim_x: 2.5 acc_lim_y: 0 acc_lim_th: 3.2 max_vel_x: 0.5 min_vel_x: 0.0 max_vel_y: 0 min_vel_y: 0 max_vel_trans: 0.5 min_vel_trans: 0.1 max_vel_theta: 1.0 min_vel_theta: 0.2 # Goal Tolerance Parameters yaw_goal_tolerance: 0.1 xy_goal_tolerance: 0.2 latch_xy_goal_tolerance: false
DWA 局部规划器最重要的参数如下:
机器人配置参数
- /acc_lim_x(默认值:2.5):机器人在 x 方向上的加速度限制,单位是米/秒²。
- /acc_lim_th(默认值:3.2):机器人在旋转方向上的加速度限制,单位是弧度/秒²。
- /max_vel_trans(默认值:0.55):机器人最大平移速度的绝对值,单位是米/秒。
- /min_vel_trans(默认值:0.1):机器人最小平移速度的绝对值,单位是米/秒。
- /max_vel_x(默认值:0.55):机器人最大 x 方向速度,单位是米/秒。
- /min_vel_x(默认值:0.0):机器人最小 x 方向速度,单位是米/秒,负值表示向后运动。
- /max_vel_theta(默认值:1.0):机器人最大旋转速度的绝对值,单位是弧度/秒。
- /min_vel_theta(默认值:0.4):机器人最小旋转速度的绝对值,单位是弧度/秒。
目标容差参数
- /yaw_goal_tolerance(双精度,默认值:0.05):控制器在达到目标时在偏航/旋转方向上的容差,单位是弧度。
- /xy_goal_tolerance(双精度,默认值:0.10):控制器在达到目标时在 x 和 y 方向上的容差,单位是米。
- /latch_xy_goal_tolerance(布尔值,默认值:false):如果目标容差被锁定,当机器人达到目标 xy 位置时,它会原地旋转,即使在旋转过程中最终位置超出了目标容差范围。
练习
a) 打开上一章中创建的 my_move_base_params.yaml 文件进行编辑。
b) 修改 DWAPlannerROS 的 xy_goal_tolerance 参数并将其设置为更高的值。
c) 检查是否注意到性能有任何差异。
High XY tolerance:
Low XY tolerance:
正如您在练习中看到的,您在参数文件中设置的目标容差越高,机器人设定的目标就越不准确。
正如您在参数文件中看到的,还有其他参数被注释(因此规划器采用它们的默认值):
# 前向模拟参数 # sim_time: 2.0 # 模拟时间(秒) # sim_granularity: 0.02 # 模拟粒度(秒) # vx_samples: 6 # x方向速度样本数 # vy_samples: 0 # y方向速度样本数 # vtheta_samples: 20 # 旋转速度样本数 # penalize_negative_x: true # 是否惩罚负x方向速度 # 轨迹评分参数 # path_distance_bias: 32.0 # 控制器保持接近给定路径的权重 # goal_distance_bias: 24.0 # 控制器试图到达局部目标的权重,也控制速度 # occdist_scale: 0.01 # 控制器尝试避免障碍物的权重 # forward_point_distance: 0.325 # 从机器人中心点到额外评分点的距离(米) # stop_time_buffer: 0.2 # 为了使轨迹被认为有效,机器人在碰撞前必须停止的时间(秒) # scaling_speed: 0.25 # 机器人足迹开始缩放的速度绝对值(米/秒) # max_scaling_factor: 0.2 # 机器人足迹的最大缩放因子 # 振荡防止参数 # oscillation_reset_dist: 0.25 # 机器人在振荡标志重置之前必须行驶的距离(米)(默认值: 0.05)
练习:
a) 修改局部规划器参数文件中的 sim_time 参数并将其设置为 4.0。
b) 检查是否注意到局部规划器的性能或可视化方面有任何差异。
Regular sim_time (local plan in blue):
High sim_time (local plan in blue):
从上面的练习中可以看出,sim_time 参数越高,计算出的局部规划就越长。但是,请记住,这也会增加使用的计算资源。
轨迹评分参数
- /path_distance_bias(默认值:32.0):控制器应保持接近给定路径的权重
- /goal_distance_bias(默认值:24.0):控制器应尝试达到其局部目标的权重;还控制速度
- /occdist_scale(默认值:0.01):控制器应尝试避开障碍物的权重
这里有一个 dwa_local_planner_params.yaml 的示例:
# 轨迹评分参数 # path_distance_bias: 32.0 # 控制器保持接近给定路径的权重 # goal_distance_bias: 24.0 # 控制器试图到达局部目标的权重,也控制速度 # occdist_scale: 0.01 # 控制器尝试避免障碍物的权重 # forward_point_distance: 0.325 # 从机器人中心点到额外评分点的距离(米) # stop_time_buffer: 0.2 # 机器人在碰撞前必须停止的时间,以使轨迹被认为有效(秒) # scaling_speed: 0.25 # 机器人足迹开始缩放的速度绝对值(米/秒) # max_scaling_factor: 0.2 # 机器人足迹的最大缩放因子
尝试自己修改这些参数,看看它们如何影响规划过程。例如,您可以尝试更改局部规划器参数文件中的 path_distance_bias 参数。
path_distance_bias
参数用于控制机器人在轨迹追踪时保持接近给定路径的权重。修改这个参数会对机器人行为产生以下影响:
增加
path_distance_bias
的值:
- 机器人会更紧密地跟随路径。 增大的权重使得控制器更加重视保持机器人在给定路径上的位置,导致机器人更努力地纠正偏离路径的情况。
- 可能导致路径跟随过度。 如果
path_distance_bias
设得过高,机器人可能会变得过于固执,紧紧依赖路径,可能会导致在复杂或狭窄环境中的表现变差,甚至导致在障碍物附近出现困难。减少
path_distance_bias
的值:
- 机器人会更灵活。 较低的权重使得控制器对路径的依赖性降低,机器人可能会有更多的自由度来进行调整和优化其他目标,例如到达目标点或避开障碍物。
- 可能导致路径偏离。 如果这个权重设置得过低,机器人可能不会很好地遵循给定路径,从而导致偏离预定路径,尤其是在路径弯曲或变化较大的情况下。
在全局规划器部分,我们已经向您介绍了代价地图,重点介绍了全局代价地图。现在是时候谈谈局部代价地图了。
Local Costmap
您需要知道的第一件事是,局部规划器使用局部代价地图来计算局部规划。
与全局代价地图不同,局部代价地图直接根据机器人的传感器读数创建。给定代价地图的宽度和高度(由用户定义),它会在机器人在整个环境中移动时将机器人保持在代价地图的中心,并在机器人移动时从地图中删除障碍物信息。
让我们做一个练习,以便您更好地了解局部代价地图的外观,以及如何区分局部代价地图和全局代价地图。
练习
a) 打开 Rviz 并添加适当的显示,以便可视化全局和局部成本地图。
b) 执行以下命令以在房间中生成障碍物。
检查您的工作区中是否已经有 object.urdf 文件。如果您还没有,您需要执行以下命令将其移动到您的工作区。
cp /home/simulations/public_sim_ws/src/all/turtlebot/turtlebot_navigation_gazebo/urdf/object.urdf /home/user/catkin_ws/src
创建对象
rosrun gazebo_ros spawn_model -file /home/user/catkin_ws/src/object.urdf -urdf -x 0 -y 0 -z 1 -model my_object
c) 启动键盘操作并靠近生成的物体。
roslaunch husky_launch keyboard_teleop.launch
d) 检查全局和局部Costmaps之间的差异。
Husky 面对生成的障碍物:
全局成本地图(未出现障碍物):
局部成本地图(障碍物出现):
因此,正如您在上一个练习中看到的,局部代价地图确实会检测模拟中出现的新对象,而全局代价地图则不会。
您可能已经推断出这种情况,因为全局代价地图是从静态地图文件创建的。这意味着即使环境发生变化,代价地图也不会改变。相反,局部代价地图是根据机器人的传感器读数创建的,因此它将始终使用来自传感器的新读数进行更新。
由于全局代价地图和局部代价地图的行为不同,因此参数文件也必须不同。让我们来看看我们需要为局部代价地图设置的最重要的参数。
Local Costmap 参数
你需要了解的参数如下:
- global_frame: 成本地图操作的全局坐标系。在局部成本地图中,此参数必须设置为 "/odom"。
- robot_base_frame: 机器人基座链接的坐标系名称。
- rolling_window: 是否使用滚动窗口版本的成本地图。如果
static_map
参数设置为true
,则此参数必须设置为false
。在局部成本地图中,此参数必须设置为true
。- update_frequency(默认值: 5.0): 更新地图的频率,以赫兹(Hz)为单位。
- width(默认值: 10): 成本地图的宽度。
- height(默认值: 10): 成本地图的高度。
- plugins: 插件规范的序列,每个层一个。每个规范是一个包含
name
和type
字段的字典。name
用于定义插件的参数命名空间。正如你所看到的,这些参数与全局成本地图的参数相同。然而,我们增加了一些在处理局部成本地图时很有用的参数:
width
和height
。我们还增加了update_frequency
,稍后将在本单元中讨论。现在,让我们做一个简单的练习来测试
width
和height
参数。练习
a) 将一个名为 my_local_costmap_params.yaml 的文件添加到您在练习my_move_base_launcher中创建的包的 params目录中。
b) 将 husky_navigation 包的 costmap_local.yaml 文件的内容复制到此文件中。
global_frame: odom rolling_window: true plugins: - {name: obstacles_laser, type: "costmap_2d::ObstacleLayer"} - {name: inflation, type: "costmap_2d::InflationLayer"}
c) 修改您在练习my_move_base_launcher中创建的 my_move_base.launch 文件,以便它加载您刚刚创建的局部 costmap 参数文件。
<?xml version="1.0"?> <launch> <!-- Run the map server --> <arg name="map_file" default="$(find husky_navigation)/maps/my_map.yaml"/> <node name="map_server" pkg="map_server" type="map_server" args="$(arg map_file)" /> <!--- Run AMCL --> <include file="$(find husky_navigation)/launch/amcl.launch" /> <arg name="no_static_map" default="false"/> <arg name="base_global_planner" default="navfn/NavfnROS"/> <arg name="base_local_planner" default="dwa_local_planner/DWAPlannerROS"/> <!-- <arg name="base_local_planner" default="base_local_planner/TrajectoryPlannerROS"/> --> <node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen"> <param name="base_global_planner" value="$(arg base_global_planner)"/> <param name="base_local_planner" value="$(arg base_local_planner)"/> <rosparam file="$(find my_move_base_launcher)/params/my_move_base_params.yaml" command="load"/> <!-- observation sources located in costmap_common.yaml --> <rosparam file="$(find husky_navigation)/config/costmap_common.yaml" command="load" ns="global_costmap" /> <rosparam file="$(find husky_navigation)/config/costmap_common.yaml" command="load" ns="local_costmap" /> <!-- local costmap, needs size --> <rosparam file="$(find my_move_base_launcher)/params/my_local_costmap_params.yaml" command="load" ns="local_costmap" /> <param name="local_costmap/width" value="10.0"/> # Change to 5 for the Exercise <param name="local_costmap/height" value="10.0"/> # Change to 5 for the Exercise <!-- static global costmap, static map provides size --> <rosparam file="$(find my_move_base_launcher)/params/my_global_costmap_params.yaml" command="load" ns="global_costmap" unless="$(arg no_static_map)"/> <!-- global costmap with laser, for odom_navigation_demo --> <rosparam file="$(find husky_navigation)/config/costmap_global_laser.yaml" command="load" ns="global_costmap" if="$(arg no_static_map)"/> <param name="global_costmap/width" value="100.0" if="$(arg no_static_map)"/> <param name="global_costmap/height" value="100.0" if="$(arg no_static_map)"/> </node> </launch>
d) 启动 Rviz 并再次可视化局部costmap。可视化地图和 costmap 模式。
e) 修改宽度和高度参数并将其设置为 5。再次可视化 costmap。
10x10 costmap (map view):
10x10 costmap (costmap view):
5x5 costmap (map view):
5x5 costmap (costmap view):
正如您在上一个练习中所看到的,为代价地图设置正确的宽度和高度非常重要。根据您想要导航的环境,您将设置一个或另一个值以正确显示障碍物。
让我们继续使用局部代价地图参数。尽管参数与全局代价地图相同,但它们的设置值并不相同。
例如,对于局部代价地图,rolling_window 参数将设置为 true。这样,我们表明我们不希望代价地图从静态地图初始化(就像我们对全局代价地图所做的那样),而是从机器人的传感器读数构建。
此外,由于我们没有任何静态地图,因此需要将 global_frame 参数设置为 odom。
您可以在上一个练习中创建的 my_local_costmap_params.yaml 文件中看到这一点:
global_frame: odom rolling_window: true
正如我们在全局成本地图中看到的,局部成本地图也可以添加层。在局部成本地图的情况下,通常会添加以下两层:
- costmap_2d::ObstacleLayer: 用于避障。
- costmap_2d::InflationLayer: 用于对障碍物进行膨胀处理。
因此,你最终会得到如下结果:
plugins: - {name: obstacle_layer, type: "costmap_2d::ObstacleLayer"} - {name: inflation_layer, type: "costmap_2d::InflationLayer"}
**障碍层**对**局部代价地图**和**全局代价地图**使用不同的插件。对于局部代价地图,它使用**costmap_2d::ObstacleLayer**,而对于全局代价地图,它使用**costmap_2d::VoxelLayer**。这非常重要,因为在导航中,对障碍层使用错误的插件是一个常见错误。
让我们添加最后一个参数!正如您通过练习已经看到的,局部成本地图会不断更新自身。这些更新周期以 update_frequency 参数指定的速率进行。每个周期的工作方式如下:
- 传感器数据进入。
- 执行标记和清除操作。
- 为每个单元格分配适当的成本值。
- 对每个有障碍物的单元格执行障碍物膨胀。这包括将成本值从每个占用的单元格向外传播到指定的膨胀半径。
练习
a) 在 local costmap 参数文件中,将地图的 update_frequency 参数改得更慢。
b) 再次重复上面练习,看看现在会发生什么。
代价地图中的对象的生成稍有延迟。
现在,您可能想知道……上面提到的标记和清除操作是什么?
正如您已经知道的,代价地图会自动订阅传感器主题,并根据从传感器收到的数据进行自我更新。每个传感器都用于标记(将障碍物信息插入代价地图)、清除(从代价地图中删除障碍物信息)或两者兼而有之。
标记操作只是数组中的索引,用于更改单元格的成本。
但是,清除操作包括从传感器原点向外通过网格进行光线追踪,以报告每个观察结果。
标记和清除操作可以在障碍层中定义。
此时,我们几乎可以说您已经知道如何配置全局和本地代价地图。但如果您还记得的话,还有一个我们尚未讨论的参数文件。那就是通用代价地图参数文件。这些参数将影响全局和本地代价地图。
Common Costmap Parameters
基本上,您需要在这个文件中设置的最重要参数如下:
- footprint: 足迹是移动底盘的轮廓。在 ROS 中,它表示为一个二维数组,例如 [x0, y0], [x1, y1], [x2, y2], ...。这个足迹将用于计算内接圆和外接圆的半径,这些圆用于以适合该机器人的方式膨胀障碍物。通常,为了安全起见,我们希望足迹略大于机器人的实际轮廓。
- robot_radius: 如果机器人是圆形的,我们将指定此参数而不是足迹。
- layers parameters: 在这里我们将定义每一层的参数。每一层都有其自己的参数。
障碍层
障碍层负责标记和清除操作。
如您所知,成本地图会自动订阅传感器主题,并根据接收到的数据进行更新。每个传感器用于标记(将障碍物信息插入成本地图)、清除(从成本地图中移除障碍物信息),或两者兼具。
- 标记操作:只是通过数组索引来改变一个单元格的成本。
- 清除操作:则是通过从传感器原点向外进行网格射线追踪,处理每个报告的观察数据。
标记和清除操作可以在障碍层中定义。
为了配置障碍层,我们首先需要为层设置名称,然后设置
observation_sources
参数。
- observation_sources(default: ""):由空格分隔的观察源名称列表。这定义了以下各个
source_name
命名空间。您可以在 husky_navigation 包中的 costmap_common.yaml 文件中查看其操作方式:
obstacles_laser: # Name of the layer observation_sources: laser # We define 1 observation_source named laser
现在我们可以为这个
observation_source
定义具体的参数。observation_sources
中的每个source_name
定义了一个命名空间,其中可以设置参数:
- /source_name/topic(默认值: source_name):传感器数据接收的主题。默认为源名称。
- /source_name/data_type(默认值: "PointCloud"):与主题相关联的数据类型,目前只支持 "PointCloud"、"PointCloud2" 和 "LaserScan"。
- /source_name/clearing(默认值: false):是否使用该观察源来清除空闲空间。
- /source_name/marking(默认值: true):是否使用该观察源来标记障碍物。
- /source_name/inf_is_valid(默认值: false):允许在 "LaserScan" 观察消息中存在 Inf 值。Inf 值将被转换为激光的最大范围。
- /source_name/max_obstacle_height(默认值: 2.0):要插入到成本地图中的障碍物的最大高度(米)。该参数应设置为比机器人的实际高度稍高。
- /source_name/obstacle_range(默认值: 2.5):障碍物将在成本地图中插入的默认最大距离(米)。此设置可以按传感器进行覆盖。
- /source_name/raytrace_range(默认值: 3.0):使用传感器数据从地图中射线追踪障碍物的默认范围(米)。此设置可以按传感器进行覆盖。
您可以在 husky_navigation 包中的 costmap_common.yaml 文件中查看其完成方式:
laser: {data_type: LaserScan, clearing: true, marking: true, topic: scan, inf_is_valid: true, obstacle_range: 5.5}
正如您在示例中看到的,我们仅声明 1 个 observer_source,用于激光。这意味着我们的 Husky 机器人将根据从其激光接收到的数据构建其本地成本地图。
练习:
a) 将名为 my_common_costmap_params.yaml 的文件添加到您在练习my_move_base_launcher中创建的包的 params 目录中。
b) 将 husky_navigation 包的 costmap_common.yaml 文件的内容复制到这个新文件中。
# 机器人足迹定义,表示机器人在代价图上的占用区域 footprint: [[-0.5, -0.33], [-0.5, 0.33], [0.5, 0.33], [0.5, -0.33]] # 足迹填充值,用于确保代价图上机器人的边缘有一定的缓冲 footprint_padding: 0.01 # 机器人基础坐标系 robot_base_frame: base_link # 更新代价图的频率(Hz) update_frequency: 4.0 # 发布代价图的频率(Hz) publish_frequency: 3.0 # 坐标变换的容忍度(秒),用于处理传感器数据和代价图之间的坐标变换 transform_tolerance: 0.5 # 代价图的分辨率(米/像素),定义每个像素在实际世界中的大小 resolution: 0.05 # 层定义部分 # 静态地图层 static: # 静态地图的主题名称 map_topic: /map # 是否订阅地图更新 subscribe_to_updates: true # 障碍物激光层 obstacles_laser: # 观测源,表示激光数据的来源 observation_sources: laser # 激光数据的配置 laser: { data_type: LaserScan, # 数据类型 clearing: true, # 是否清除已探测到的障碍物 marking: true, # 是否标记障碍物 topic: scan, # 激光数据的主题 inf_is_valid: true, # 是否将无穷大距离视为有效 obstacle_range: 5.5 # 识别障碍物的最大范围(米) } # 膨胀层 inflation: # 膨胀半径(米),用于扩展障碍物区域以考虑机器人周围的安全距离 inflation_radius: 1.0
a) 现在,修改 obstacle_range 参数并将其设置为 1。
b) 将机器人移近障碍物并观察会发生什么。
Inflation Layer
Inflation Layer负责对每个包含障碍物的单元格进行膨胀处理。
- inflation_radius(默认值: 0.55):用于膨胀障碍物成本值的半径(米)。
- cost_scaling_factor(默认值: 10.0):在膨胀过程中应用于成本值的缩放因子。
练习
a) 现在,修改 costmap 的 inflation_radius 参数,使其速度更慢。
b) 靠近某个物体,检查差异。
inflation_radius
参数用于控制障碍物周围的膨胀区域的大小,这有助于为机器人提供一个更加保守的避障区域。Low inflation:
High inflation:
Static Layer
静态层负责向需要它的代价地图(全局代价地图)提供静态地图。
map_topic(string,默认值:“map”):代价地图为静态地图订阅的主题。
Rotate Recovery
基本上,旋转恢复行为是一种简单的恢复行为,通过将机器人旋转 360 度来尝试清理空间。这样,机器人可能能够找到一条没有障碍物的路径继续导航。
它有一些可以自定义的参数,以便改变或改善其行为:
旋转恢复参数
- /sim_granularity (默认值: 0.017): 在检查是否可以安全地进行原地旋转时,检查障碍物的距离,单位是弧度。默认为 1 度。
- /frequency (默认值: 20.0): 向移动底盘发送速度命令的频率,单位是赫兹 (HZ)。
其他参数
- /yaw_goal_tolerance (double, 默认值: 0.05): 控制器在实现目标时在偏航/旋转方向上的容差,单位是弧度。
- /acc_lim_th (double, 默认值: 3.2): 机器人的旋转加速度限制,单位是弧度/秒²。
- /max_rotational_vel (double, 默认值: 1.0): 底盘允许的最大旋转速度,单位是弧度/秒。
- /min_in_place_rotational_vel (double, 默认值: 0.4): 执行原地旋转时底盘允许的最小旋转速度,单位是弧度/秒。
这些参数也可以在
move_base
参数文件中设置。Clear Costmap
清除成本图恢复是一种简单的恢复行为,通过清除机器人地图中指定区域之外的障碍物来清理空间。基本上,局部成本图会恢复到与全局成本图相同的状态。
让我们来进行一个简短的练习,以测试这些恢复行为。
练习
向无法到达的机器人发送导航目标,并检查是否触发恢复行为。
例如,您可以在模拟世界的厨房内设置一个目标。查看下图:
move base节点的输出:
旋转恢复行为:
move_base 节点还提供了一项服务,用于清除 costmap 中的障碍物。这项服务称为 /move_base/clear_cotmaps。
请记住,通过清除 costmap 中的障碍物,您将使这些障碍物对机器人不可见。因此,调用此服务时要小心,因为它可能会导致机器人开始撞到障碍物。
练习
a) 如果还没有,则将一个未出现在全局代价地图中的对象添加到场景中。
b) 移动机器人,使其在局部代价地图中检测到这个新障碍物。
c) 转动机器人,使其不再看到障碍物(激光束检测不到它)。
d) 通过 WebShell 调用 /clear_costmaps 服务,并检查发生了什么。
rosservice call /move_base/clear_costmaps "{}"
被激光检测到的物体,放置到局部Ccostmap中:
Husky转身,激光不再检测到物体,但它仍然出现在本地代价地图中。
调用/move_base/clear_costmaps服务后,对象从局部代价地图中清除:
Recap
恭喜你!到目前为止,你已经了解了本章涵盖的几乎所有重要内容。由于这是课程的最后一章,这意味着你距离完全掌握 ROS 导航系统已经非常接近了!
不过,你可能会对有关路径规划的大量信息感到不知所措。因此,我认为这是一个总结本章内容的好时机。让我们开始吧!
move_base 节点
move_base
节点基本上是协调所有路径规划系统的节点。它接收目标位姿作为输入,并输出必要的速度指令,以便将机器人从初始位姿移动到指定的目标位姿。为实现这一目标,move_base
节点管理一个完整的内部流程,其中包括以下几个部分:
- 全局规划器
- 局部规划器
- 成本图
- 恢复行为
全局规划器
当
move_base
节点收到新的目标时,它会立即将其发送给全局规划器。全局规划器随后将计算出一条安全的路径,供机器人到达指定的目标。全局规划器使用全局成本图数据来计算这条路径。有不同类型的全局规划器。根据你的设置,你将使用不同的全局规划器。
局部规划器
一旦全局规划器为机器人计算出路径,它会将路径发送给局部规划器。局部规划器将执行这条路径,将其拆分为更小的(局部)部分。因此,给定一个要遵循的计划和地图,局部规划器将提供速度指令以移动机器人。局部规划器在局部成本图上操作。
有不同类型的局部规划器。根据你对性能的需求,你将选择不同的局部规划器。
成本图
成本图基本上是表示地图上哪些点对机器人是安全的,哪些点是不安全的地图。有 2 种类型的成本图:
- 全局成本图
- 局部成本图
基本上,它们之间的区别在于,全局成本图是使用先前构建的静态地图数据构建的,而局部成本图则是从机器人的传感器读取数据构建的。
恢复行为
恢复行为提供了在机器人卡住时的处理方法。导航栈提供了 2 种不同的恢复行为:
- 旋转恢复
- 清除成本图
配置
由于有许多不同的节点协同工作,因此可配置的参数数量也非常高。我认为总结一下需要设置的路径规划相关参数文件是一个好主意。你需要的参数文件如下:
<launch> <!-- Load move_base parameters --> <param file="$(find your_package)/config/move_base_params.yaml" command="load" /> <!-- Load global planner parameters --> <param file="$(find your_package)/config/global_planner_params.yaml" command="load" /> <!-- Load local planner parameters --> <param file="$(find your_package)/config/local_planner_params.yaml" command="load" /> <!-- Load common costmap parameters --> <param file="$(find your_package)/config/common_costmap_params.yaml" command="load" /> <!-- Load global costmap parameters --> <param file="$(find your_package)/config/global_costmap_params.yaml" command="load" /> <!-- Load local costmap parameters --> <param file="$(find your_package)/config/local_costmap_params.yaml" command="load" /> <!-- Start the move_base node --> <node pkg="move_base" type="move_base" name="move_base" /> </launch>
除了上述参数文件外,我们还需要一个启动文件,以便启动整个系统并加载不同的参数。
总结
总体而言,路径规划的流程如下:
在获取机器人的当前位置后,我们可以将目标位置发送到
move_base
节点。该节点将目标位置发送到全局规划器,全局规划器将计划出一条从当前位置到目标位置的路径。这个计划是基于全局成本图的,而全局成本图由地图服务器提供数据。全局规划器将此路径发送给本地规划器,本地规划器执行全局计划的每个部分。本地规划器获取里程计和激光数据值,并找到一个没有碰撞的局部计划。局部规划器与本地成本图相关联,本地成本图可以监视机器人周围的障碍物。局部规划器生成速度指令并将其发送到底盘控制器。底盘控制器将这些指令转换为实际的机器人运动。
如果机器人卡住了,恢复行为节点(如清除成本图恢复或旋转恢复)将被调用。
现在一切都更有意义了,对吧?
所以,凭借你在本课程中获得的所有知识,你可以再次查看下面的图示,尝试理解其中涉及的所有不同元素。
动态重新配置
到目前为止,我们已经了解了如何通过在参数文件中修改参数来更改参数。但是,猜猜看……这不是更改参数的唯一方法!您还可以使用 rqt_reconfigure 工具更改动态参数。请按照以下步骤操作:
a) 运行下一个命令以打开 rqt_reconfigure 工具。
rosrun rqt_reconfigure rqt_reconfigure
- 打开 move_base group。
- 选择 DWAPlannerROS 节点。
- 稍微调整一下以下 3 个参数:
- path_distance_bias
- goal_distance_bias
- occdist_scale
- 上述参数用于计算成本函数,用于对每条轨迹进行评分。更详细地说,它们定义了以下内容:
- path_distance_bias:控制器应保持接近给定路径的权重。
- goal_distance_bias:控制器应尝试达到其局部目标的权重,也控制速度。
- occdist_scale:控制器应尝试避开障碍物的权重。
- 打开 Rviz 并可视化全局和局部计划如何根据设置的值发生变化。
Rviz 中的其他有用可视化
到目前为止,我们已经了解了一些通过 Rviz 可视化 move_base 节点过程不同部分的方法。但是,还有一些可能值得了解:
Robot Footprint
它显示了机器人的足迹。
Current Goal
要显示导航堆栈试图实现的目标姿势,请添加姿势显示并将其主题设置为 /move_base_simple/goal。现在您将能够看到红色箭头所示的目标姿势。它可用于了解机器人的最终位置。