编写新的规划器插件

带有渐变演示的动画gif     `[待校准@2043] <http://dev.nav2.fishros.com/calibpage/#/home?msgid=2043>`_

概述

本教程展示了如何创建自己的规划器插件。 [校准@混沌无形]

必要条件

教程步骤

1创建新规划器插件

我们将创建一条简单的直线规划器。本教程中的注释代码可以在 navigation_tutorials 库中找到,因为 nav2_straightline_planner 这个包可以被认为是编写规划器插件的参考。 [校准@混沌无形]

我们的示例插件继承自基类 “nav2_core::GlobalPlanner”。基类提供5种纯虚拟方法来实现规划器插件。该插件将被规划器服务器用来计算轨迹。让我们进一步了解编写规划器插件所需的方法。 [校准@混沌无形]

Virtual method

方法描述

必须重写?

configure()

当规划器服务器进入on_configure状态时,该方法被调用。理想情况下,这个方法应该执行ROS参数的声明和规划器成员变量的初始化。该方法4个输入参数: 指向父节点的共享指针,规划器名称,tf缓冲区指针和指向成本地图的共享指针。 [校准@混沌无形]

[校准@小鱼]

activate() [待校准@2110]

当规划器服务器进入on_activate状态时,方法被调用。理想情况下,这个方法应该在规划器进入激活状态之前实现必要的操作。 [校准@混沌无形]

[校准@小鱼]

deactivate()

当规划器服务器进入on_deactivate状态时,方法被调用。理想情况下,这个方法应该实现规划器进入非活动状态前的必要操作。 [校准@混沌无形]

[校准@小鱼]

cleanup()

当规划器服务器进入on_cleanup状态时,方法被调用。理想情况下,这个方法应该清理为规划器创建的资源。 [校准@混沌无形]

[校准@小鱼]

createPlan()

当规划器服务器要求为指定的起始和目标姿势提供全局规划时,该方法被调用。该方法返回携带全局规划的 “nav_msgs::msg:: path”。这个方法需要两个输入参数:起始位姿和目标位姿。 [校准@混沌无形]

[校准@小鱼]

对于本教程,我们将使用方法 StraightLine::configure()StraightLine::createPlan() 创建直线规划器。 [待校准@2159]

在规划器中, configure() 方法必须从ROS参数和任何需要的初始化中设置成员变量。 [校准@混沌无形]

node_ = parent;
tf_ = tf;
name_ = name;
costmap_ = costmap_ros->getCostmap();
global_frame_ = costmap_ros->getGlobalFrameID();

// Parameter initialization
nav2_util::declare_parameter_if_not_declared(node_, name_ + ".interpolation_resolution", rclcpp::ParameterValue(0.1));
node_->get_parameter(name_ + ".interpolation_resolution", interpolation_resolution_);

这里, name_ + ".interpolation_resolution" 获取ROS参数 interpolation_resolution,这是我们规划器所持有的。Navigation2 允许加载多个插件,为了保持组织性,每个插件都被映射到一些ID/名称。现在如果我们想检索特定插件的参数,我们使用 <mapped_name_of_plugin>.<name_of_parameter> ,如上面的片段。例如,示例规划器映射名称 "GridBased" 并检索 interpolation_resolution 参数,这是特定于"GridBased" 的,我们使用 Gridbased.interpolation_resolution 。换句话说, GridBased 作为命名空间插件特定参数。当我们讨论参数文件 (或params文件) 时,我们将看到更多有关此的信息。 [校准@小鱼]

createPlan() 法中,我们需要创建一条从给定的开始到目标位姿的路径。 ``StraightLine::createPlan()``使用起始姿势和目标姿势来求解全局路径规划问题。成功后,它将路径转换为 “nav_msgs::msg:: path” 并返回到规划器服务器。下面的注释展示了此方法的实现。 [校准@混沌无形]

nav_msgs::msg::Path global_path;

// Checking if the goal and start state is in the global frame
if (start.header.frame_id != global_frame_) {
  RCLCPP_ERROR(
    node_->get_logger(), "Planner will only except start position from %s frame",
    global_frame_.c_str());
  return global_path;
}

if (goal.header.frame_id != global_frame_) {
  RCLCPP_INFO(
    node_->get_logger(), "Planner will only except goal position from %s frame",
    global_frame_.c_str());
  return global_path;
}

global_path.poses.clear();
global_path.header.stamp = node_->now();
global_path.header.frame_id = global_frame_;
// calculating the number of loops for current value of interpolation_resolution_
int total_number_of_loop = std::hypot(
  goal.pose.position.x - start.pose.position.x,
  goal.pose.position.y - start.pose.position.y) /
  interpolation_resolution_;
double x_increment = (goal.pose.position.x - start.pose.position.x) / total_number_of_loop;
double y_increment = (goal.pose.position.y - start.pose.position.y) / total_number_of_loop;

for (int i = 0; i < total_number_of_loop; ++i) {
  geometry_msgs::msg::PoseStamped pose;
  pose.pose.position.x = start.pose.position.x + x_increment * i;
  pose.pose.position.y = start.pose.position.y + y_increment * i;
  pose.pose.position.z = 0.0;
  pose.pose.orientation.x = 0.0;
  pose.pose.orientation.y = 0.0;
  pose.pose.orientation.z = 0.0;
  pose.pose.orientation.w = 1.0;
  pose.header.stamp = node_->now();
  pose.header.frame_id = global_frame_;
  global_path.poses.push_back(pose);
}

global_path.poses.push_back(goal);

return global_path;

其余的方法没有被使用,但必须覆盖它们。按照规定,我们覆盖了所有的方法,但把它们留成空白。 [校准@混沌无形]

2-导出planner插件

既然我们已经创建了我们的自定义规划器,我们需要导出我们的规划器插件,以便它对规划器服务器可见。插件在运行时加载,如果它们不可见,那么我们的规划器服务器将无法加载它。在ROS 2中,导出和加载插件由 pluginlib 处理。 [校准@混沌无形]

在我们的教程里, nav2_straightline_planner::StraightLine 类被动态加载为 nav2_core::GlobalPlanner [校准@混沌无形]

  1. 为了导出规划器,我们需要提供两行信息 [校准@混沌无形]

#include "pluginlib/class_list_macros.hpp"
PLUGINLIB_EXPORT_CLASS(nav2_straightline_planner::StraightLine, nav2_core::GlobalPlanner)

请注意,它需要pluginlib导出插件的类。Pluginlib将提供作为宏的 PLUGINLIB_EXPORT_CLASS ,它负责所有的导出工作。 [待校准@2167]

把这些行内容放在文件的末尾是很好的做法,但从技术上讲,你也可以写在顶部。 [校准@混沌无形]

  1. 下一步将是在软件包的根目录下创建插件的描述文件。例如,我们的教程包中的 global_planner_plugin.xml 文件。此文件包含以下信息 [校准@混沌无形]

<library path="nav2_straightline_planner_plugin">
  <class name="nav2_straightline_planner/StraightLine" type="nav2_straightline_planner::StraightLine" base_class_type="nav2_core::GlobalPlanner">
    <description>This is an example plugin which produces straight path.</description>
  </class>
</library>
  1. 下一步是使用cmake函数``pluginlib_export_plugin_description_file()``导出插件``CMakeLists.txt`。此函数将插件描述文件安装到 share 目录,并设置属性索引以使其可被发现。 [校准@混沌无形]

pluginlib_export_plugin_description_file(nav2_core global_planner_plugin.xml)
  1. 插件描述文件也应添加到 package.xml [待校准@2171]

<export>
  <build_type>ament_cmake</build_type>
  <nav2_core plugin="${prefix}/global_planner_plugin.xml" />
</export>
  1. 编译并注册。接下来,我们将使用这个插件。 [待校准@2172]

3-通过params文件传递插件名称

要启用插件,我们需要将 nav2_params.yaml 文件修改为以下内容,以替换以下参数 [待校准@2174]

备注

用Galactic或后, plugin_namesplugin_types 替换单个 plugins string载体插件名称。类型现在定义在 plugin_name 命名空间 ''plugin: `` field (e.g. `` 插件: MyPlugin: Plugin'')。内联注释代码块将帮助您通过这个。 [待校准@2091]

planner_server:
ros__parameters:
  planner_plugin_types: ["nav2_navfn_planner/NavfnPlanner"] # For Eloquent and earlier
  planner_plugin_ids: ["GridBased"] # For Eloquent and earlier
  plugins: ["GridBased"] # For Foxy and later
  use_sim_time: True
  GridBased:
    plugin: nav2_navfn_planner/NavfnPlanner # For Foxy and later
    tolerance: 2.0
    use_astar: false
    allow_unknown: true

[待校准@2175]

planner_server:
ros__parameters:
  planner_plugin_types: ["nav2_straightline_planner/StraightLine"] # For Eloquent and earlier
  planner_plugin_ids: ["GridBased"] # For Eloquent and earlier
  plugins: ["GridBased"] # For Foxy and later
  use_sim_time: True
  GridBased:
    plugin: nav2_straightline_planner/StraightLine # For Foxy and later
    interpolation_resolution: 0.1

在上面的片段中,你可以观察到我们的``nav2_straightline_planner/StraightLine``规划器与它的id GridBased``的映射。为了传递插件特定的参数,我们使用了 ``<plugin_id>.<plugin_specific_parameter>[校准@混沌无形]

4-运行直线插件

运行Turtlebot3模拟器,启用navigation 2。如何制作它的详细说明写在 入门 。下面是该命令的快捷方式: [校准@混沌无形]

$ ros2 launch nav2_bringup tb3_simulation_launch.py params_file:=/path/to/your_params_file.yaml

然后转到RViz,点击顶部的 "2D Pose Estimate" 按钮,在地图上指出位置,就像 入门 中描述的那样。机器人将在地图上定位,然后点击 "Navigation2 goal" ,点击你想让你的规划器考虑目标位姿。之后,规划器将规划路径,机器人将开始向目标移动。 [校准@混沌无形]