Nav2 Navigation Stack - jazzy  jazzy
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 #include <limits>
25 
26 #include "nav2_msgs/action/navigate_to_pose.hpp"
27 #include "nav2_behavior_tree/bt_action_server.hpp"
28 #include "ament_index_cpp/get_package_share_directory.hpp"
29 #include "nav2_util/node_utils.hpp"
30 
31 namespace nav2_behavior_tree
32 {
33 
34 template<class ActionT>
36  const rclcpp_lifecycle::LifecycleNode::WeakPtr & parent,
37  const std::string & action_name,
38  const std::vector<std::string> & plugin_lib_names,
39  const std::string & default_bt_xml_filename,
40  OnGoalReceivedCallback on_goal_received_callback,
41  OnLoopCallback on_loop_callback,
42  OnPreemptCallback on_preempt_callback,
43  OnCompletionCallback on_completion_callback)
44 : action_name_(action_name),
45  default_bt_xml_filename_(default_bt_xml_filename),
46  plugin_lib_names_(plugin_lib_names),
47  node_(parent),
48  on_goal_received_callback_(on_goal_received_callback),
49  on_loop_callback_(on_loop_callback),
50  on_preempt_callback_(on_preempt_callback),
51  on_completion_callback_(on_completion_callback)
52 {
53  auto node = node_.lock();
54  logger_ = node->get_logger();
55  clock_ = node->get_clock();
56 
57  // Declare this node's parameters
58  if (!node->has_parameter("bt_loop_duration")) {
59  node->declare_parameter("bt_loop_duration", 10);
60  }
61  if (!node->has_parameter("default_server_timeout")) {
62  node->declare_parameter("default_server_timeout", 20);
63  }
64  if (!node->has_parameter("default_cancel_timeout")) {
65  node->declare_parameter("default_cancel_timeout", 20);
66  }
67  if (!node->has_parameter("action_server_result_timeout")) {
68  node->declare_parameter("action_server_result_timeout", 900.0);
69  }
70  if (!node->has_parameter("always_reload_bt_xml")) {
71  node->declare_parameter("always_reload_bt_xml", false);
72  }
73  if (!node->has_parameter("wait_for_service_timeout")) {
74  node->declare_parameter("wait_for_service_timeout", 1000);
75  }
76 
77  std::vector<std::string> error_code_names = {
78  "follow_path_error_code",
79  "compute_path_error_code"
80  };
81 
82  if (!node->has_parameter("error_code_names")) {
83  const rclcpp::ParameterValue value = node->declare_parameter(
84  "error_code_names",
85  rclcpp::PARAMETER_STRING_ARRAY);
86  if (value.get_type() == rclcpp::PARAMETER_NOT_SET) {
87  std::string error_codes_str;
88  for (const auto & error_code : error_code_names) {
89  error_codes_str += " " + error_code;
90  }
91  RCLCPP_WARN_STREAM(
92  logger_, "Error_code parameters were not set. Using default values of:"
93  << error_codes_str + "\n"
94  << "Make sure these match your BT and there are not other sources of error codes you"
95  "reported to your application");
96  rclcpp::Parameter error_code_names_param("error_code_names", error_code_names);
97  node->set_parameter(error_code_names_param);
98  } else {
99  error_code_names = value.get<std::vector<std::string>>();
100  std::string error_codes_str;
101  for (const auto & error_code : error_code_names) {
102  error_codes_str += " " + error_code;
103  }
104  RCLCPP_INFO_STREAM(logger_, "Error_code parameters were set to:" << error_codes_str);
105  }
106  }
107 }
108 
109 template<class ActionT>
111 {}
112 
113 template<class ActionT>
115 {
116  auto node = node_.lock();
117  if (!node) {
118  throw std::runtime_error{"Failed to lock node"};
119  }
120 
121  // Name client node after action name
122  std::string client_node_name = action_name_;
123  std::replace(client_node_name.begin(), client_node_name.end(), '/', '_');
124  // Use suffix '_rclcpp_node' to keep parameter file consistency #1773
125  auto options = rclcpp::NodeOptions().arguments(
126  {"--ros-args",
127  "-r",
128  std::string("__node:=") +
129  std::string(node->get_name()) + "_" + client_node_name + "_rclcpp_node",
130  "-p",
131  "use_sim_time:=" +
132  std::string(node->get_parameter("use_sim_time").as_bool() ? "true" : "false"),
133  "--"});
134 
135  // Support for handling the topic-based goal pose from rviz
136  client_node_ = std::make_shared<rclcpp::Node>("_", options);
137 
138  // Declare parameters for common client node applications to share with BT nodes
139  // Declare if not declared in case being used an external application, then copying
140  // all of the main node's parameters to the client for BT nodes to obtain
141  nav2_util::declare_parameter_if_not_declared(
142  node, "global_frame", rclcpp::ParameterValue(std::string("map")));
143  nav2_util::declare_parameter_if_not_declared(
144  node, "robot_base_frame", rclcpp::ParameterValue(std::string("base_link")));
145  nav2_util::declare_parameter_if_not_declared(
146  node, "transform_tolerance", rclcpp::ParameterValue(0.1));
147  rclcpp::copy_all_parameter_values(node, client_node_);
148 
149  // set the timeout in seconds for the action server to discard goal handles if not finished
150  double action_server_result_timeout =
151  node->get_parameter("action_server_result_timeout").as_double();
152  rcl_action_server_options_t server_options = rcl_action_server_get_default_options();
153  server_options.result_timeout.nanoseconds = RCL_S_TO_NS(action_server_result_timeout);
154 
155  action_server_ = std::make_shared<ActionServer>(
156  node->get_node_base_interface(),
157  node->get_node_clock_interface(),
158  node->get_node_logging_interface(),
159  node->get_node_waitables_interface(),
160  action_name_, std::bind(&BtActionServer<ActionT>::executeCallback, this),
161  nullptr, std::chrono::milliseconds(500), false, server_options);
162 
163  // Get parameters for BT timeouts
164  int bt_loop_duration;
165  node->get_parameter("bt_loop_duration", bt_loop_duration);
166  bt_loop_duration_ = std::chrono::milliseconds(bt_loop_duration);
167  int default_server_timeout;
168  node->get_parameter("default_server_timeout", default_server_timeout);
169  default_server_timeout_ = std::chrono::milliseconds(default_server_timeout);
170  int default_cancel_timeout;
171  node->get_parameter("default_cancel_timeout", default_cancel_timeout);
172  default_cancel_timeout_ = std::chrono::milliseconds(default_cancel_timeout);
173  int wait_for_service_timeout;
174  node->get_parameter("wait_for_service_timeout", wait_for_service_timeout);
175  wait_for_service_timeout_ = std::chrono::milliseconds(wait_for_service_timeout);
176  node->get_parameter("always_reload_bt_xml", always_reload_bt_xml_);
177 
178  // Get error code id names to grab off of the blackboard
179  error_code_names_ = node->get_parameter("error_code_names").as_string_array();
180 
181  // Create the class that registers our custom nodes and executes the BT
182  bt_ = std::make_unique<nav2_behavior_tree::BehaviorTreeEngine>(plugin_lib_names_, client_node_);
183 
184  // Create the blackboard that will be shared by all of the nodes in the tree
185  blackboard_ = BT::Blackboard::create();
186 
187  // Put items on the blackboard
188  blackboard_->set<rclcpp::Node::SharedPtr>("node", client_node_); // NOLINT
189  blackboard_->set<std::chrono::milliseconds>("server_timeout", default_server_timeout_); // NOLINT
190  blackboard_->set<std::chrono::milliseconds>("cancel_timeout", default_cancel_timeout_); // NOLINT
191  blackboard_->set<std::chrono::milliseconds>("bt_loop_duration", bt_loop_duration_); // NOLINT
192  blackboard_->set<std::chrono::milliseconds>(
193  "wait_for_service_timeout",
194  wait_for_service_timeout_);
195 
196  return true;
197 }
198 
199 template<class ActionT>
201 {
202  if (!loadBehaviorTree(default_bt_xml_filename_)) {
203  RCLCPP_ERROR(logger_, "Error loading XML file: %s", default_bt_xml_filename_.c_str());
204  return false;
205  }
206  action_server_->activate();
207  return true;
208 }
209 
210 template<class ActionT>
212 {
213  action_server_->deactivate();
214  return true;
215 }
216 
217 template<class ActionT>
219 {
220  client_node_.reset();
221  action_server_.reset();
222  topic_logger_.reset();
223  plugin_lib_names_.clear();
224  current_bt_xml_filename_.clear();
225  blackboard_.reset();
226  bt_->haltAllActions(tree_);
227  bt_->resetGrootMonitor();
228  bt_.reset();
229  return true;
230 }
231 
232 template<class ActionT>
233 void BtActionServer<ActionT>::setGrootMonitoring(const bool enable, const unsigned server_port)
234 {
235  enable_groot_monitoring_ = enable;
236  groot_server_port_ = server_port;
237 }
238 
239 template<class ActionT>
240 bool BtActionServer<ActionT>::loadBehaviorTree(const std::string & bt_xml_filename)
241 {
242  // Empty filename is default for backward compatibility
243  auto filename = bt_xml_filename.empty() ? default_bt_xml_filename_ : bt_xml_filename;
244 
245  // Use previous BT if it is the existing one and always reload flag is not set to true
246  if (!always_reload_bt_xml_ && current_bt_xml_filename_ == filename) {
247  RCLCPP_DEBUG(logger_, "BT will not be reloaded as the given xml is already loaded");
248  return true;
249  }
250 
251  // Reset any existing Groot2 monitoring
252  bt_->resetGrootMonitor();
253 
254  // Read the input BT XML from the specified file into a string
255  std::ifstream xml_file(filename);
256 
257  if (!xml_file.good()) {
258  RCLCPP_ERROR(logger_, "Couldn't open input XML file: %s", filename.c_str());
259  return false;
260  }
261 
262  // Create the Behavior Tree from the XML input
263  try {
264  tree_ = bt_->createTreeFromFile(filename, blackboard_);
265  for (auto & subtree : tree_.subtrees) {
266  auto & blackboard = subtree->blackboard;
267  blackboard->set("node", client_node_);
268  blackboard->set<std::chrono::milliseconds>("server_timeout", default_server_timeout_);
269  blackboard->set<std::chrono::milliseconds>("cancel_timeout", default_cancel_timeout_);
270  blackboard->set<std::chrono::milliseconds>("bt_loop_duration", bt_loop_duration_);
271  blackboard->set<std::chrono::milliseconds>(
272  "wait_for_service_timeout",
273  wait_for_service_timeout_);
274  }
275  } catch (const std::exception & e) {
276  RCLCPP_ERROR(logger_, "Exception when loading BT: %s", e.what());
277  return false;
278  }
279 
280  topic_logger_ = std::make_unique<RosTopicLogger>(client_node_, tree_);
281 
282  current_bt_xml_filename_ = filename;
283 
284  if (enable_groot_monitoring_) {
285  bt_->addGrootMonitoring(&tree_, groot_server_port_);
286  RCLCPP_DEBUG(
287  logger_, "Enabling Groot2 monitoring for %s: %d",
288  action_name_.c_str(), groot_server_port_);
289  }
290 
291  return true;
292 }
293 
294 template<class ActionT>
296 {
297  if (!on_goal_received_callback_(action_server_->get_current_goal())) {
298  action_server_->terminate_current();
299  cleanErrorCodes();
300  return;
301  }
302 
303  auto is_canceling = [&]() {
304  if (action_server_ == nullptr) {
305  RCLCPP_DEBUG(logger_, "Action server unavailable. Canceling.");
306  return true;
307  }
308  if (!action_server_->is_server_active()) {
309  RCLCPP_DEBUG(logger_, "Action server is inactive. Canceling.");
310  return true;
311  }
312  return action_server_->is_cancel_requested();
313  };
314 
315  auto on_loop = [&]() {
316  if (action_server_->is_preempt_requested() && on_preempt_callback_) {
317  on_preempt_callback_(action_server_->get_pending_goal());
318  }
319  topic_logger_->flush();
320  on_loop_callback_();
321  };
322 
323  // Execute the BT that was previously created in the configure step
324  nav2_behavior_tree::BtStatus rc = bt_->run(&tree_, on_loop, is_canceling, bt_loop_duration_);
325 
326  // Make sure that the Bt is not in a running state from a previous execution
327  // note: if all the ControlNodes are implemented correctly, this is not needed.
328  bt_->haltAllActions(tree_);
329 
330  // Give server an opportunity to populate the result message or simple give
331  // an indication that the action is complete.
332  auto result = std::make_shared<typename ActionT::Result>();
333 
334  populateErrorCode(result);
335 
336  on_completion_callback_(result, rc);
337 
338  switch (rc) {
339  case nav2_behavior_tree::BtStatus::SUCCEEDED:
340  action_server_->succeeded_current(result);
341  RCLCPP_INFO(logger_, "Goal succeeded");
342  break;
343 
344  case nav2_behavior_tree::BtStatus::FAILED:
345  action_server_->terminate_current(result);
346  RCLCPP_ERROR(logger_, "Goal failed");
347  break;
348 
349  case nav2_behavior_tree::BtStatus::CANCELED:
350  action_server_->terminate_all(result);
351  RCLCPP_INFO(logger_, "Goal canceled");
352  break;
353  }
354 
355  cleanErrorCodes();
356 }
357 
358 template<class ActionT>
360  typename std::shared_ptr<typename ActionT::Result> result)
361 {
362  int highest_priority_error_code = std::numeric_limits<int>::max();
363  for (const auto & error_code : error_code_names_) {
364  try {
365  int current_error_code = blackboard_->get<int>(error_code);
366  if (current_error_code != 0 && current_error_code < highest_priority_error_code) {
367  highest_priority_error_code = current_error_code;
368  }
369  } catch (...) {
370  RCLCPP_DEBUG(
371  logger_,
372  "Failed to get error code: %s from blackboard",
373  error_code.c_str());
374  }
375  }
376 
377  if (highest_priority_error_code != std::numeric_limits<int>::max()) {
378  result->error_code = highest_priority_error_code;
379  }
380 }
381 
382 template<class ActionT>
384 {
385  for (const auto & error_code : error_code_names_) {
386  blackboard_->set<unsigned short>(error_code, 0); //NOLINT
387  }
388 }
389 
390 } // namespace nav2_behavior_tree
391 
392 #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.
void populateErrorCode(typename std::shared_ptr< typename ActionT::Result > result)
updates the action server result to the highest priority error code posted on the blackboard
~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.
void cleanErrorCodes()
Setting BT error codes to success. Used to clean blackboard between different BT runs.
void setGrootMonitoring(const bool enable, const unsigned server_port)
Enable (or disable) Groot2 monitoring of BT.
bool on_configure()
Configures member variables Initializes action server for, builds behavior tree from xml file,...