Nav2 Navigation Stack - humble  humble
ROS 2 Navigation Stack
bt_action_server_impl.hpp
1 // Copyright (c) 2020 Sarthak Mittal
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef NAV2_BEHAVIOR_TREE__BT_ACTION_SERVER_IMPL_HPP_
16 #define NAV2_BEHAVIOR_TREE__BT_ACTION_SERVER_IMPL_HPP_
17 
18 #include <memory>
19 #include <string>
20 #include <fstream>
21 #include <set>
22 #include <exception>
23 #include <vector>
24 
25 #include "nav2_msgs/action/navigate_to_pose.hpp"
26 #include "nav2_behavior_tree/bt_action_server.hpp"
27 #include "ament_index_cpp/get_package_share_directory.hpp"
28 #include "nav2_util/node_utils.hpp"
29 
30 namespace nav2_behavior_tree
31 {
32 
33 template<class ActionT>
35  const rclcpp_lifecycle::LifecycleNode::WeakPtr & parent,
36  const std::string & action_name,
37  const std::vector<std::string> & plugin_lib_names,
38  const std::string & default_bt_xml_filename,
39  OnGoalReceivedCallback on_goal_received_callback,
40  OnLoopCallback on_loop_callback,
41  OnPreemptCallback on_preempt_callback,
42  OnCompletionCallback on_completion_callback)
43 : action_name_(action_name),
44  default_bt_xml_filename_(default_bt_xml_filename),
45  plugin_lib_names_(plugin_lib_names),
46  node_(parent),
47  on_goal_received_callback_(on_goal_received_callback),
48  on_loop_callback_(on_loop_callback),
49  on_preempt_callback_(on_preempt_callback),
50  on_completion_callback_(on_completion_callback)
51 {
52  auto node = node_.lock();
53  logger_ = node->get_logger();
54  clock_ = node->get_clock();
55 
56  // Declare this node's parameters
57  if (!node->has_parameter("bt_loop_duration")) {
58  node->declare_parameter("bt_loop_duration", 10);
59  }
60  if (!node->has_parameter("default_server_timeout")) {
61  node->declare_parameter("default_server_timeout", 20);
62  }
63  if (!node->has_parameter("always_reload_bt_xml")) {
64  node->declare_parameter("always_reload_bt_xml", false);
65  }
66  if (!node->has_parameter("wait_for_service_timeout")) {
67  node->declare_parameter("wait_for_service_timeout", 1000);
68  }
69 }
70 
71 template<class ActionT>
73 {}
74 
75 template<class ActionT>
77 {
78  auto node = node_.lock();
79  if (!node) {
80  throw std::runtime_error{"Failed to lock node"};
81  }
82 
83  // Name client node after action name
84  std::string client_node_name = action_name_;
85  std::replace(client_node_name.begin(), client_node_name.end(), '/', '_');
86  // Use suffix '_rclcpp_node' to keep parameter file consistency #1773
87  auto options = rclcpp::NodeOptions().arguments(
88  {"--ros-args",
89  "-r",
90  std::string("__node:=") +
91  std::string(node->get_name()) + "_" + client_node_name + "_rclcpp_node",
92  "--"});
93 
94  // Support for handling the topic-based goal pose from rviz
95  client_node_ = std::make_shared<rclcpp::Node>("_", options);
96 
97  // Declare parameters for common client node applications to share with BT nodes
98  // Declare if not declared in case being used an external application, then copying
99  // all of the main node's parameters to the client for BT nodes to obtain
100  nav2_util::declare_parameter_if_not_declared(
101  node, "global_frame", rclcpp::ParameterValue(std::string("map")));
102  nav2_util::declare_parameter_if_not_declared(
103  node, "robot_base_frame", rclcpp::ParameterValue(std::string("base_link")));
104  nav2_util::declare_parameter_if_not_declared(
105  node, "transform_tolerance", rclcpp::ParameterValue(0.1));
106  nav2_util::copy_all_parameters(node, client_node_);
107 
108  action_server_ = std::make_shared<ActionServer>(
109  node->get_node_base_interface(),
110  node->get_node_clock_interface(),
111  node->get_node_logging_interface(),
112  node->get_node_waitables_interface(),
113  action_name_, std::bind(&BtActionServer<ActionT>::executeCallback, this));
114 
115  // Get parameters for BT timeouts
116  int timeout;
117  node->get_parameter("bt_loop_duration", timeout);
118  bt_loop_duration_ = std::chrono::milliseconds(timeout);
119  node->get_parameter("default_server_timeout", timeout);
120  default_server_timeout_ = std::chrono::milliseconds(timeout);
121  int wait_for_service_timeout;
122  node->get_parameter("wait_for_service_timeout", wait_for_service_timeout);
123  wait_for_service_timeout_ = std::chrono::milliseconds(wait_for_service_timeout);
124  node->get_parameter("always_reload_bt_xml", always_reload_bt_xml_);
125 
126  // Create the class that registers our custom nodes and executes the BT
127  bt_ = std::make_unique<nav2_behavior_tree::BehaviorTreeEngine>(plugin_lib_names_);
128 
129  // Create the blackboard that will be shared by all of the nodes in the tree
130  blackboard_ = BT::Blackboard::create();
131 
132  // Put items on the blackboard
133  blackboard_->set<rclcpp::Node::SharedPtr>("node", client_node_); // NOLINT
134  blackboard_->set<std::chrono::milliseconds>("server_timeout", default_server_timeout_); // NOLINT
135  blackboard_->set<std::chrono::milliseconds>("bt_loop_duration", bt_loop_duration_); // NOLINT
136  blackboard_->set<std::chrono::milliseconds>(
137  "wait_for_service_timeout",
138  wait_for_service_timeout_);
139 
140  return true;
141 }
142 
143 template<class ActionT>
145 {
146  if (!loadBehaviorTree(default_bt_xml_filename_)) {
147  RCLCPP_ERROR(logger_, "Error loading XML file: %s", default_bt_xml_filename_.c_str());
148  return false;
149  }
150  action_server_->activate();
151  return true;
152 }
153 
154 template<class ActionT>
156 {
157  action_server_->deactivate();
158  return true;
159 }
160 
161 template<class ActionT>
163 {
164  client_node_.reset();
165  action_server_.reset();
166  topic_logger_.reset();
167  plugin_lib_names_.clear();
168  current_bt_xml_filename_.clear();
169  blackboard_.reset();
170  bt_->haltAllActions(tree_.rootNode());
171  bt_.reset();
172  return true;
173 }
174 
175 template<class ActionT>
176 bool BtActionServer<ActionT>::loadBehaviorTree(const std::string & bt_xml_filename)
177 {
178  // Empty filename is default for backward compatibility
179  auto filename = bt_xml_filename.empty() ? default_bt_xml_filename_ : bt_xml_filename;
180 
181  // Use previous BT if it is the existing one and always reload flag is not set to true
182  if (!always_reload_bt_xml_ && current_bt_xml_filename_ == filename) {
183  RCLCPP_DEBUG(logger_, "BT will not be reloaded as the given xml is already loaded");
184  return true;
185  }
186 
187  // Read the input BT XML from the specified file into a string
188  std::ifstream xml_file(filename);
189 
190  if (!xml_file.good()) {
191  RCLCPP_ERROR(logger_, "Couldn't open input XML file: %s", filename.c_str());
192  return false;
193  }
194 
195  auto xml_string = std::string(
196  std::istreambuf_iterator<char>(xml_file),
197  std::istreambuf_iterator<char>());
198 
199  // Create the Behavior Tree from the XML input
200  try {
201  tree_ = bt_->createTreeFromText(xml_string, blackboard_);
202  for (auto & blackboard : tree_.blackboard_stack) {
203  blackboard->set<rclcpp::Node::SharedPtr>("node", client_node_);
204  blackboard->set<std::chrono::milliseconds>("server_timeout", default_server_timeout_);
205  blackboard->set<std::chrono::milliseconds>("bt_loop_duration", bt_loop_duration_);
206  blackboard->set<std::chrono::milliseconds>(
207  "wait_for_service_timeout",
208  wait_for_service_timeout_);
209  }
210  } catch (const std::exception & e) {
211  RCLCPP_ERROR(logger_, "Exception when loading BT: %s", e.what());
212  return false;
213  }
214 
215  topic_logger_ = std::make_unique<RosTopicLogger>(client_node_, tree_);
216 
217  current_bt_xml_filename_ = filename;
218  return true;
219 }
220 
221 template<class ActionT>
223 {
224  if (!on_goal_received_callback_(action_server_->get_current_goal())) {
225  action_server_->terminate_current();
226  return;
227  }
228 
229  auto is_canceling = [&]() {
230  if (action_server_ == nullptr) {
231  RCLCPP_DEBUG(logger_, "Action server unavailable. Canceling.");
232  return true;
233  }
234  if (!action_server_->is_server_active()) {
235  RCLCPP_DEBUG(logger_, "Action server is inactive. Canceling.");
236  return true;
237  }
238  return action_server_->is_cancel_requested();
239  };
240 
241  auto on_loop = [&]() {
242  if (action_server_->is_preempt_requested() && on_preempt_callback_) {
243  on_preempt_callback_(action_server_->get_pending_goal());
244  }
245  topic_logger_->flush();
246  on_loop_callback_();
247  };
248 
249  // Execute the BT that was previously created in the configure step
250  nav2_behavior_tree::BtStatus rc = bt_->run(&tree_, on_loop, is_canceling, bt_loop_duration_);
251 
252  // Make sure that the Bt is not in a running state from a previous execution
253  // note: if all the ControlNodes are implemented correctly, this is not needed.
254  bt_->haltAllActions(tree_.rootNode());
255 
256  // Give server an opportunity to populate the result message or simple give
257  // an indication that the action is complete.
258  auto result = std::make_shared<typename ActionT::Result>();
259  on_completion_callback_(result, rc);
260 
261  switch (rc) {
262  case nav2_behavior_tree::BtStatus::SUCCEEDED:
263  action_server_->succeeded_current(result);
264  RCLCPP_INFO(logger_, "Goal succeeded");
265  break;
266 
267  case nav2_behavior_tree::BtStatus::FAILED:
268  action_server_->terminate_current(result);
269  RCLCPP_ERROR(logger_, "Goal failed");
270  break;
271 
272  case nav2_behavior_tree::BtStatus::CANCELED:
273  action_server_->terminate_all(result);
274  RCLCPP_INFO(logger_, "Goal canceled");
275  break;
276  }
277 }
278 
279 } // namespace nav2_behavior_tree
280 
281 #endif // NAV2_BEHAVIOR_TREE__BT_ACTION_SERVER_IMPL_HPP_
An action server that uses behavior tree to execute an action.
bool loadBehaviorTree(const std::string &bt_xml_filename="")
Replace current BT with another one.
bool on_cleanup()
Resets member variables.
~BtActionServer()
A destructor for nav2_behavior_tree::BtActionServer class.
BtActionServer(const rclcpp_lifecycle::LifecycleNode::WeakPtr &parent, const std::string &action_name, const std::vector< std::string > &plugin_lib_names, const std::string &default_bt_xml_filename, OnGoalReceivedCallback on_goal_received_callback, OnLoopCallback on_loop_callback, OnPreemptCallback on_preempt_callback, OnCompletionCallback on_completion_callback)
A constructor for nav2_behavior_tree::BtActionServer class.
void executeCallback()
Action server callback.
bool on_activate()
Activates action server.
bool on_deactivate()
Deactivates action server.
bool on_configure()
Configures member variables Initializes action server for, builds behavior tree from xml file,...