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