Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
goal_updater_node.cpp
1 // Copyright (c) 2018 Intel Corporation
2 // Copyright (c) 2020 Francisco Martin Rico
3 // Copyright (c) 2024 Angsa Robotics
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #include <string>
18 #include <memory>
19 
20 #include "behaviortree_cpp/decorator_node.h"
21 
22 #include "nav2_behavior_tree/plugins/decorator/goal_updater_node.hpp"
23 
24 #include "rclcpp/callback_group.hpp"
25 #include "rclcpp/logging.hpp"
26 #include "rclcpp/time.hpp"
27 
28 namespace nav2_behavior_tree
29 {
30 
31 using std::placeholders::_1;
32 
34  const std::string & name,
35  const BT::NodeConfiguration & conf)
36 : BT::DecoratorNode(name, conf),
37  goal_updater_topic_("goal_update"),
38  goals_updater_topic_("goals_update")
39 {
40  initialize();
41  bt_loop_duration_ =
42  config().blackboard->template get<std::chrono::milliseconds>("bt_loop_duration");
43 }
44 
45 void GoalUpdater::initialize()
46 {
47  createROSInterfaces();
48 }
49 
50 void GoalUpdater::createROSInterfaces()
51 {
52  node_ = config().blackboard->get<nav2::LifecycleNode::SharedPtr>("node");
53  callback_group_ = node_->create_callback_group(
54  rclcpp::CallbackGroupType::MutuallyExclusive,
55  false);
56  callback_group_executor_.add_callback_group(callback_group_, node_->get_node_base_interface());
57 
58  std::string goal_updater_topic_new;
59  std::string goals_updater_topic_new;
60  node_->get_parameter_or<std::string>("goal_updater_topic", goal_updater_topic_new, "goal_update");
61  node_->get_parameter_or<std::string>(
62  "goals_updater_topic", goals_updater_topic_new,
63  "goals_update");
64 
65  // Only create a new subscriber if the topic has changed or subscriber is empty
66  if (goal_updater_topic_new != goal_updater_topic_ || !goal_sub_) {
67  goal_updater_topic_ = goal_updater_topic_new;
68  goal_sub_ = node_->create_subscription<geometry_msgs::msg::PoseStamped>(
69  goal_updater_topic_,
70  std::bind(&GoalUpdater::callback_updated_goal, this, _1),
72  callback_group_);
73  }
74  if (goals_updater_topic_new != goals_updater_topic_ || !goals_sub_) {
75  goals_updater_topic_ = goals_updater_topic_new;
76  goals_sub_ = node_->create_subscription<nav_msgs::msg::Goals>(
77  goals_updater_topic_,
78  std::bind(&GoalUpdater::callback_updated_goals, this, _1),
80  callback_group_);
81  }
82 }
83 
84 inline BT::NodeStatus GoalUpdater::tick()
85 {
86  if (!BT::isStatusActive(status())) {
87  initialize();
88  }
89 
90  geometry_msgs::msg::PoseStamped goal;
91  nav_msgs::msg::Goals goals;
92 
93  getInput("input_goal", goal);
94  getInput("input_goals", goals);
95 
96  callback_group_executor_.spin_all(bt_loop_duration_);
97 
98  if (last_goal_received_set_) {
99  if (last_goal_received_.header.stamp == builtin_interfaces::msg::Time{}) {
100  // if the goal doesn't have a timestamp, we reject it
101  RCLCPP_WARN(
102  node_->get_logger(), "The received goal has no timestamp. Ignoring.");
103  setOutput("output_goal", goal);
104  } else {
105  auto last_goal_received_time = rclcpp::Time(last_goal_received_.header.stamp);
106  auto goal_time = rclcpp::Time(goal.header.stamp);
107  if (last_goal_received_time >= goal_time) {
108  setOutput("output_goal", last_goal_received_);
109  } else {
110  RCLCPP_INFO(
111  node_->get_logger(), "The timestamp of the received goal (%f) is older than the "
112  "current goal (%f). Ignoring the received goal.",
113  last_goal_received_time.seconds(), goal_time.seconds());
114  setOutput("output_goal", goal);
115  }
116  }
117  } else {
118  setOutput("output_goal", goal);
119  }
120 
121  if (last_goals_received_set_) {
122  if (last_goals_received_.goals.empty()) {
123  setOutput("output_goals", goals);
124  } else if (last_goals_received_.header.stamp == builtin_interfaces::msg::Time{}) {
125  RCLCPP_WARN(
126  node_->get_logger(), "The received goals array has no timestamp. Ignoring.");
127  setOutput("output_goals", goals);
128  } else {
129  auto last_goals_received_time = rclcpp::Time(last_goals_received_.header.stamp);
130  auto goals_time = rclcpp::Time(goals.header.stamp);
131  if (last_goals_received_time >= goals_time) {
132  setOutput("output_goals", last_goals_received_);
133  } else {
134  RCLCPP_INFO(
135  node_->get_logger(), "The timestamp of the received goals (%f) is older than the "
136  "current goals (%f). Ignoring the received goals.",
137  last_goals_received_time.seconds(), goals_time.seconds());
138  setOutput("output_goals", goals);
139  }
140  }
141  } else {
142  setOutput("output_goals", goals);
143  }
144 
145  return child_node_->executeTick();
146 }
147 
148 void
149 GoalUpdater::callback_updated_goal(const geometry_msgs::msg::PoseStamped::ConstSharedPtr & msg)
150 {
151  last_goal_received_ = *msg;
152  last_goal_received_set_ = true;
153 }
154 
155 void
156 GoalUpdater::callback_updated_goals(const nav_msgs::msg::Goals::ConstSharedPtr & msg)
157 {
158  last_goals_received_ = *msg;
159  last_goals_received_set_ = true;
160 }
161 
162 } // namespace nav2_behavior_tree
163 
164 #include "behaviortree_cpp/bt_factory.h"
165 BT_REGISTER_NODES(factory)
166 {
167  factory.registerNodeType<nav2_behavior_tree::GoalUpdater>("GoalUpdater");
168 }
A QoS profile for standard reliable topics with a history of 10 messages.
A BT::DecoratorNode that subscribes to a goal topic and updates the current goal on the blackboard.
GoalUpdater(const std::string &xml_tag_name, const BT::NodeConfiguration &conf)
A constructor for nav2_behavior_tree::GoalUpdater.