ros2中navigation2的BT常用语法2

behavior tree常用语法

我们继续通过Navigate To Pose and Pause Near Goal-Obstacle(导航到目标位姿并具有接近障碍物暂停功能)来讲解BT常用指令。
Navigate To Pose and Pause Near Goal-Obstacle

4.ReactiveSquence反应序列

该节点对于连续检查条件特别有用;但是用户在使用异步子节点时也应该小心,以确保它们不会比预期的更频繁(可以通过装饰节点控制第一个子节点的频率)。
主要功能是同过调用第一个子节点,根据反馈情况,调用第二个节点。例如,调用第一个节点返回running,则不会调用第二个节点。调用第一个节点返回success,则调用第二个节点。调用第一个节点返回failtrue,则终止所有节点,返回failtrue。当第二个节点running时,父节点返回running,下个tick继续调用第一个节点。

看伪代码更容易理解,暂时未找到源代码,后续更新
让我们看另一个例子:
在这里插入图片描述

 status = RUNNING;

    for (int index=0; index < number_of_children; index++)
    {
        child_status = child[index]->tick();

        if( child_status == RUNNING ) {
            return RUNNING;
        }
        else if( child_status == FAILURE ) {
            HaltAllChildren();
            return FAILURE;
        }
    }
    // all the children returned success. Return SUCCESS too.
    HaltAllChildren();
    return SUCCESS;

5.Fallback 后退控制节点

在这里插入图片描述
如果第一个节点failtrue,则不返回直接tick第二节点,一直到有节点success,父节点Fallback返回success。如果子节点返回running,则父节点返回running。

c++伪代码:

    // index is initialized to 0 in the constructor
    status = RUNNING;

    while( _index < number_of_children )
    {
        child_status = child[index]->tick();

        if( child_status == RUNNING ) {
            // Suspend execution and return RUNNING.
            // At the next tick, _index will be the same.
            return RUNNING;
        }
        else if( child_status == FAILURE ) {
            // continue the while loop
            _index++;
        }
        else if( child_status == SUCCESS ) {
            // Suspend execution and return SUCCESS.
            HaltAllChildren();
            _index = 0;
            return SUCCESS;
        }
    }
    // all the children returned FAILURE. Return FAILURE too.
    index = 0;
    HaltAllChildren();
    return FAILURE;

6.ReactiveFallback 反应式后退控制节点

如果前面的条件之一将其状态从 FAILURE 更改为 SUCCESS,则当您想要中断异步子节点时使用此 控制节点。

在下面的示例中,角色最多可以睡8 个小时。如果他/她已经充分休息,则节点将返回SUCCESS ,areYouRested?异步节点将被中断。Timeout (8 hrs)Sleep
在这里插入图片描述

    status = RUNNING;

    for (int index=0; index < number_of_children; index++)
    {
        child_status = child[index]->tick();

        if( child_status == RUNNING ) {
            // Suspend all subsequent siblings and return RUNNING.
            HaltSubsequentSiblings();
            return RUNNING;
        }

        // if child_status == FAILURE, continue to tick next sibling

        if( child_status == SUCCESS ) {
            // Suspend execution and return SUCCESS.
            HaltAllChildren();
            return SUCCESS;
        }
    }
    // all the children returned FAILURE. Return FAILURE too.
    HaltAllChildren();
    return FAILURE;

7.Squence序列节点

就是顺序执行,tick子节点,子节点返回成功继续调用下一个,知道所有都成功,返回success。如果子节点running,父节点返回running,但当前子节点索引记忆,下一次从当前节点调用。如果子节点返回failtrue,父节点返回failtrue,记忆索引清零。
在这里插入图片描述
c++伪代码:特点是如果失败,要清楚_index,从开始重新调用

    status = RUNNING;
    // _index is a private member

    while(_index < number_of_children)
    {
        child_status = child[_index]->tick();

        if( child_status == SUCCESS ) {
            _index++;
        }
        else if( child_status == RUNNING ) {
            // keep same index
            return RUNNING;
        }
        else if( child_status == FAILURE ) {
            HaltAllChildren();
            _index = 0;
            return FAILURE;
        }
    }
    // all the children returned success. Return SUCCESS too.
    HaltAllChildren();
    _index = 0;
    return SUCCESS;

8.SequenceStar序列星

当您不想再次勾选已返回 SUCCESS 的子项时,请使用此 ControlNode。

示例:

这是一个必须访问位置 A、B 和 C一次的巡逻代理/机器人。如果动作GoTo(B)失败,则不会再次勾选GoTo(A) 。

另一方面,isBatteryOK必须在每次滴答时检查,因此其父级必须是ReactiveSequence.
在这里插入图片描述
c++代码:特点是子节点failtrue,不清除_index,下次调用时继续上次的_index位置开始调用。

  status = RUNNING;
    // _index is a private member

    while( index < number_of_children)
    {
        child_status = child[index]->tick();

        if( child_status == SUCCESS ) {
            _index++;
        }
        else if( child_status == RUNNING || 
                 child_status == FAILURE ) 
        {
            // keep same index
            return child_status;
        }
    }
    // all the children returned success. Return SUCCESS too.
    HaltAllChildren();
    _index = 0;
    return SUCCESS;

Decorators装饰器节点

装饰器节点只能拥有一个子节点。
装饰器决定了子节点什么时候调用,以及调用多少次。

InverterNode
调用子节点一次,如果子节点失败,则返回 SUCCESS,如果子节点成功,则返回 FAILURE。
如果子节点返回 RUNNING,则该节点也返回 RUNNING

ForceSuccessNode
如果子节点返回 RUNNING,则该节点也返回 RUNNING。
否则,它总是返回 SUCCESS。

ForceFailureNode
如果子节点返回 RUNNING,则该节点也返回 RUNNING。
否则,它总是返回 FAILURE。

RepeatNode
将孩子最多勾选 N 次,其中 N 作为Input Port传递,只要孩子返回 SUCCESS。
如果孩子返回 FAILURE,则中断循环,在这种情况下,也返回 FAILURE。
如果子节点返回 RUNNING,则该节点也返回 RUNNING。

RetryNode
将孩子最多勾选 N 次,其中 N 作为Input Port传递,只要孩子返回 FAILURE。
如果孩子返回 SUCCESS,则中断循环,在这种情况下,也返回 SUCCESS。
如果子节点返回 RUNNING,则该节点也返回 RUNNING。

RateController
将子节点限制为给定速率

#include <chrono>
#include <string>

#include "nav2_behavior_tree/plugins/decorator/rate_controller.hpp"

namespace nav2_behavior_tree
{

RateController::RateController(
  const std::string & name,
  const BT::NodeConfiguration & conf)
: BT::DecoratorNode(name, conf),
  first_time_(false)
{
  double hz = 1.0;
  getInput("hz", hz);
  period_ = 1.0 / hz;
}

BT::NodeStatus RateController::tick()
{
  if (status() == BT::NodeStatus::IDLE) {
    // Reset the starting point since we're starting a new iteration of
    // the rate controller (moving from IDLE to RUNNING)
    start_ = std::chrono::high_resolution_clock::now();
    first_time_ = true;
  }

  setStatus(BT::NodeStatus::RUNNING);

  // Determine how long its been since we've started this iteration
  auto now = std::chrono::high_resolution_clock::now();
  auto elapsed = now - start_;

  // Now, get that in seconds
  typedef std::chrono::duration<float> float_seconds;
  auto seconds = std::chrono::duration_cast<float_seconds>(elapsed);

  // The child gets ticked the first time through and any time the period has
  // expired. In addition, once the child begins to run, it is ticked each time
  // 'til completion
  if (first_time_ || (child_node_->status() == BT::NodeStatus::RUNNING) ||
    seconds.count() >= period_)
  {
    first_time_ = false;
    const BT::NodeStatus child_state = child_node_->executeTick();

    switch (child_state) {
      case BT::NodeStatus::RUNNING:
        return BT::NodeStatus::RUNNING;

      case BT::NodeStatus::SUCCESS:
        start_ = std::chrono::high_resolution_clock::now();  // Reset the timer
        return BT::NodeStatus::SUCCESS;

      case BT::NodeStatus::FAILURE:
      default:
        return BT::NodeStatus::FAILURE;
    }
  }

  return status();
}

}  // namespace nav2_behavior_tree

#include "behaviortree_cpp_v3/bt_factory.h"
BT_REGISTER_NODES(factory)
{
  factory.registerNodeType<nav2_behavior_tree::RateController>("RateController");
}

Distance Controller
根据机器人行进的距离标记子节点


#include <chrono>
#include <string>
#include <memory>
#include <cmath>

#include "nav2_util/robot_utils.hpp"
#include "nav2_util/geometry_utils.hpp"
#include "geometry_msgs/msg/pose_stamped.hpp"
#include "tf2_ros/buffer.h"

#include "behaviortree_cpp_v3/decorator_node.h"

#include "nav2_behavior_tree/plugins/decorator/distance_controller.hpp"

namespace nav2_behavior_tree
{

DistanceController::DistanceController(
  const std::string & name,
  const BT::NodeConfiguration & conf)
: BT::DecoratorNode(name, conf),
  distance_(1.0),
  global_frame_("map"),
  robot_base_frame_("base_link"),
  first_time_(false)
{
  getInput("distance", distance_);
  getInput("global_frame", global_frame_);
  getInput("robot_base_frame", robot_base_frame_);
  node_ = config().blackboard->get<rclcpp::Node::SharedPtr>("node");
  tf_ = config().blackboard->get<std::shared_ptr<tf2_ros::Buffer>>("tf_buffer");

  node_->get_parameter("transform_tolerance", transform_tolerance_);
}

inline BT::NodeStatus DistanceController::tick()
{
  if (status() == BT::NodeStatus::IDLE) {
    // Reset the starting position since we're starting a new iteration of
    // the distance controller (moving from IDLE to RUNNING)
    if (!nav2_util::getCurrentPose(
        start_pose_, *tf_, global_frame_, robot_base_frame_,
        transform_tolerance_))
    {
      RCLCPP_DEBUG(node_->get_logger(), "Current robot pose is not available.");
      return BT::NodeStatus::FAILURE;
    }
    first_time_ = true;
  }

  setStatus(BT::NodeStatus::RUNNING);

  // Determine distance travelled since we've started this iteration
  geometry_msgs::msg::PoseStamped current_pose;
  if (!nav2_util::getCurrentPose(
      current_pose, *tf_, global_frame_, robot_base_frame_,
      transform_tolerance_))
  {
    RCLCPP_DEBUG(node_->get_logger(), "Current robot pose is not available.");
    return BT::NodeStatus::FAILURE;
  }

  // Get euclidean distance
  auto travelled = nav2_util::geometry_utils::euclidean_distance(
    start_pose_.pose, current_pose.pose);

  // The child gets ticked the first time through and every time the threshold
  // distance is crossed. In addition, once the child begins to run, it is
  // ticked each time 'til completion
  if (first_time_ || (child_node_->status() == BT::NodeStatus::RUNNING) ||
    travelled >= distance_)
  {
    first_time_ = false;
    const BT::NodeStatus child_state = child_node_->executeTick();

    switch (child_state) {
      case BT::NodeStatus::RUNNING:
        return BT::NodeStatus::RUNNING;

      case BT::NodeStatus::SUCCESS:
        if (!nav2_util::getCurrentPose(
            start_pose_, *tf_, global_frame_, robot_base_frame_,
            transform_tolerance_))
        {
          RCLCPP_DEBUG(node_->get_logger(), "Current robot pose is not available.");
          return BT::NodeStatus::FAILURE;
        }
        return BT::NodeStatus::SUCCESS;

      case BT::NodeStatus::FAILURE:
      default:
        return BT::NodeStatus::FAILURE;
    }
  }

  return status();
}

}  // namespace nav2_behavior_tree

#include "behaviortree_cpp_v3/bt_factory.h"
BT_REGISTER_NODES(factory)
{
  factory.registerNodeType<nav2_behavior_tree::DistanceController>("DistanceController");
}
© 2022 GitHub, Inc.
Terms
Privacy
Security

Speed Controller
将子节点限制为基于当前机器人速度的速率。

Goal Updater
更新通过主题订阅收到的目标。

#include <string>
#include <memory>

#include "geometry_msgs/msg/pose_stamped.hpp"
#include "behaviortree_cpp_v3/decorator_node.h"

#include "nav2_behavior_tree/plugins/decorator/goal_updater_node.hpp"

#include "rclcpp/rclcpp.hpp"

namespace nav2_behavior_tree
{

using std::placeholders::_1;

GoalUpdater::GoalUpdater(
  const std::string & name,
  const BT::NodeConfiguration & conf)
: BT::DecoratorNode(name, conf)
{
  node_ = config().blackboard->get<rclcpp::Node::SharedPtr>("node");
  callback_group_ = node_->create_callback_group(
    rclcpp::CallbackGroupType::MutuallyExclusive,
    false);
  callback_group_executor_.add_callback_group(callback_group_, node_->get_node_base_interface());

  std::string goal_updater_topic;
  node_->get_parameter_or<std::string>("goal_updater_topic", goal_updater_topic, "goal_update");

  rclcpp::SubscriptionOptions sub_option;
  sub_option.callback_group = callback_group_;
  goal_sub_ = node_->create_subscription<geometry_msgs::msg::PoseStamped>(
    goal_updater_topic,
    10,
    std::bind(&GoalUpdater::callback_updated_goal, this, _1),
    sub_option);
}

inline BT::NodeStatus GoalUpdater::tick()
{
  geometry_msgs::msg::PoseStamped goal;

  getInput("input_goal", goal);

  callback_group_executor_.spin_some();

  if (rclcpp::Time(last_goal_received_.header.stamp) > rclcpp::Time(goal.header.stamp)) {
    goal = last_goal_received_;
  }

  setOutput("output_goal", goal);
  return child_node_->executeTick();
}

void
GoalUpdater::callback_updated_goal(const geometry_msgs::msg::PoseStamped::SharedPtr msg)
{
  last_goal_received_ = *msg;
}

}  // namespace nav2_behavior_tree

#include "behaviortree_cpp_v3/bt_factory.h"
BT_REGISTER_NODES(factory)
{
  factory.registerNodeType<nav2_behavior_tree::GoalUpdater>("GoalUpdater");
}

Single Trigger
每次 BT 运行仅触发一次以下节点/子树。

PathLongerOnApproach
如果新的全局路径明显大于接近目标的旧全局路径,则触发子节点

#include <string>
#include <memory>
#include <vector>
#include "nav2_util/geometry_utils.hpp"

#include "nav2_behavior_tree/plugins/decorator/path_longer_on_approach.hpp"

namespace nav2_behavior_tree
{

PathLongerOnApproach::PathLongerOnApproach(
  const std::string & name,
  const BT::NodeConfiguration & conf)
: BT::DecoratorNode(name, conf)
{
  node_ = config().blackboard->get<rclcpp::Node::SharedPtr>("node");
}

bool PathLongerOnApproach::isPathUpdated(
  nav_msgs::msg::Path & new_path,
  nav_msgs::msg::Path & old_path)
{
  return new_path != old_path && old_path.poses.size() != 0 &&
         new_path.poses.size() != 0 &&
         old_path.poses.back() == new_path.poses.back();
}

bool PathLongerOnApproach::isRobotInGoalProximity(
  nav_msgs::msg::Path & old_path,
  double & prox_leng)
{
  return nav2_util::geometry_utils::calculate_path_length(old_path, 0) < prox_leng;
}

bool PathLongerOnApproach::isNewPathLonger(
  nav_msgs::msg::Path & new_path,
  nav_msgs::msg::Path & old_path,
  double & length_factor)
{
  return nav2_util::geometry_utils::calculate_path_length(new_path, 0) >
         length_factor * nav2_util::geometry_utils::calculate_path_length(
    old_path, 0);
}

inline BT::NodeStatus PathLongerOnApproach::tick()
{
  getInput("path", new_path_);
  getInput("prox_len", prox_len_);
  getInput("length_factor", length_factor_);

  if (status() == BT::NodeStatus::IDLE) {
    // Reset the starting point since we're starting a new iteration of
    // PathLongerOnApproach (moving from IDLE to RUNNING)
    first_time_ = true;
  }

  setStatus(BT::NodeStatus::RUNNING);

  // Check if the path is updated and valid, compare the old and the new path length,
  // given the goal proximity and check if the new path is longer
  if (isPathUpdated(new_path_, old_path_) && isRobotInGoalProximity(old_path_, prox_len_) &&
    isNewPathLonger(new_path_, old_path_, length_factor_) && !first_time_)
  {
    const BT::NodeStatus child_state = child_node_->executeTick();
    switch (child_state) {
      case BT::NodeStatus::RUNNING:
        return BT::NodeStatus::RUNNING;
      case BT::NodeStatus::SUCCESS:
        old_path_ = new_path_;
        return BT::NodeStatus::SUCCESS;
      case BT::NodeStatus::FAILURE:
        old_path_ = new_path_;
        return BT::NodeStatus::FAILURE;
      default:
        old_path_ = new_path_;
        return BT::NodeStatus::FAILURE;
    }
  }
  old_path_ = new_path_;
  first_time_ = false;
  return BT::NodeStatus::SUCCESS;
}

}  // namespace nav2_behavior_tree

#include "behaviortree_cpp_v3/bt_factory.h"
BT_REGISTER_NODES(factory)
{
  factory.registerNodeType<nav2_behavior_tree::PathLongerOnApproach>("PathLongerOnApproach");
}

Compute Path to Pose Action

#include <memory>
#include <string>

#include "nav2_behavior_tree/plugins/action/compute_path_to_pose_action.hpp"

namespace nav2_behavior_tree
{

ComputePathToPoseAction::ComputePathToPoseAction(
  const std::string & xml_tag_name,
  const std::string & action_name,
  const BT::NodeConfiguration & conf)
: BtActionNode<nav2_msgs::action::ComputePathToPose>(xml_tag_name, action_name, conf)
{
}

void ComputePathToPoseAction::on_tick()
{
  getInput("goal", goal_.goal);
  getInput("planner_id", goal_.planner_id);
  if (getInput("start", goal_.start)) {
    goal_.use_start = true;
  }
}

BT::NodeStatus ComputePathToPoseAction::on_success()
{
  setOutput("path", result_.result->path);
  return BT::NodeStatus::SUCCESS;
}

}  // namespace nav2_behavior_tree

#include "behaviortree_cpp_v3/bt_factory.h"
BT_REGISTER_NODES(factory)
{
  BT::NodeBuilder builder =
    [](const std::string & name, const BT::NodeConfiguration & config)
    {
      return std::make_unique<nav2_behavior_tree::ComputePathToPoseAction>(
        name, "compute_path_to_pose", config);
    };

  factory.registerBuilder<nav2_behavior_tree::ComputePathToPoseAction>(
    "ComputePathToPose", builder);
}```

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值