15 #ifndef NAV2_BEHAVIOR_TREE__BT_ACTION_SERVER_IMPL_HPP_
16 #define NAV2_BEHAVIOR_TREE__BT_ACTION_SERVER_IMPL_HPP_
28 #include "nav2_msgs/action/navigate_to_pose.hpp"
29 #include "nav2_behavior_tree/bt_action_server.hpp"
30 #include "nav2_ros_common/node_utils.hpp"
31 #include "rcl_action/action_server.h"
32 #include "nav2_ros_common/lifecycle_node.hpp"
33 #include "rclcpp/copy_all_parameter_values.hpp"
35 namespace nav2_behavior_tree
38 template<
class ActionT,
class NodeT>
40 const typename NodeT::WeakPtr & parent,
41 const std::string & action_name,
42 const std::vector<std::string> & plugin_lib_names,
43 const std::string & default_bt_xml_filename,
44 OnGoalReceivedCallback on_goal_received_callback,
45 OnLoopCallback on_loop_callback,
46 OnPreemptCallback on_preempt_callback,
47 OnCompletionCallback on_completion_callback,
48 const std::vector<std::string> & search_directories)
49 : action_name_(action_name),
50 default_bt_xml_filename_or_id_(default_bt_xml_filename),
51 search_directories_(search_directories),
52 plugin_lib_names_(plugin_lib_names),
54 on_goal_received_callback_(on_goal_received_callback),
55 on_loop_callback_(on_loop_callback),
56 on_preempt_callback_(on_preempt_callback),
57 on_completion_callback_(on_completion_callback),
58 internal_error_code_(0),
61 auto node = node_.lock();
62 logger_ = node->get_logger();
63 clock_ = node->get_clock();
65 std::vector<std::string> default_error_code_name_prefixes = {
80 if (node->has_parameter(
"error_code_names")) {
81 throw std::runtime_error(
82 "parameter 'error_code_names' has been replaced by "
83 " 'error_code_name_prefixes' and MUST be removed.\n"
84 " Please review migration guide and update your configuration.");
88 error_code_name_prefixes_ = node->declare_or_get_parameter(
89 "error_code_name_prefixes",
90 default_error_code_name_prefixes);
93 std::string error_code_name_prefixes_str;
94 for (
const auto & error_code_name_prefix : error_code_name_prefixes_) {
95 error_code_name_prefixes_str +=
" " + error_code_name_prefix;
98 if (error_code_name_prefixes_ == default_error_code_name_prefixes) {
100 logger_,
"error_code_name_prefixes parameters were not set. Using default values of:"
101 << error_code_name_prefixes_str +
"\n"
102 <<
"Make sure these match your BT and there are not other sources of error codes you"
103 <<
"reported to your application");
106 logger_,
"Error_code parameters were set to:"
107 << error_code_name_prefixes_str);
111 template<
class ActionT,
class NodeT>
115 template<
class ActionT,
class NodeT>
118 auto node = node_.lock();
120 throw std::runtime_error{
"Failed to lock node"};
124 std::string client_node_name = action_name_;
125 std::replace(client_node_name.begin(), client_node_name.end(),
'/',
'_');
128 auto new_arguments = node->get_node_options().arguments();
129 nav2::replaceOrAddArgument(
130 new_arguments,
"-r",
"__node", std::string(
"__node:=") +
131 std::string(node->get_name()) +
"_" + client_node_name +
"_rclcpp_node");
132 auto options = node->get_node_options();
133 options = options.arguments(new_arguments);
136 client_node_ = std::make_shared<nav2::LifecycleNode>(
"_", options);
137 client_node_->configure();
138 client_node_->activate();
143 nav2::declare_parameter_if_not_declared(
144 node,
"global_frame", rclcpp::ParameterValue(std::string(
"map")));
145 nav2::declare_parameter_if_not_declared(
146 node,
"robot_base_frame", rclcpp::ParameterValue(std::string(
"base_link")));
147 nav2::declare_parameter_if_not_declared(
148 node,
"transform_tolerance", rclcpp::ParameterValue(0.1));
149 rclcpp::copy_all_parameter_values(node, client_node_);
153 action_server_ = nav2::interfaces::create_action_server<ActionT>(
155 on_goal_received_callback_,
nullptr, std::chrono::milliseconds(500),
false);
158 bt_loop_duration_ = std::chrono::milliseconds(
159 node->declare_or_get_parameter(
"bt_loop_duration", 10));
161 default_server_timeout_ = std::chrono::milliseconds(
162 node->declare_or_get_parameter(
"default_server_timeout", 20));
164 default_cancel_timeout_ = std::chrono::milliseconds(
165 node->declare_or_get_parameter(
"default_cancel_timeout", 50));
167 wait_for_service_timeout_ = std::chrono::milliseconds(
168 node->declare_or_get_parameter(
"wait_for_service_timeout", 1000));
170 always_reload_bt_ = node->declare_or_get_parameter(
171 "always_reload_bt_xml",
false);
173 log_idle_ = node->declare_or_get_parameter(
174 "bt_log_idle_transitions",
true);
177 error_code_name_prefixes_ = node->get_parameter(
"error_code_name_prefixes").as_string_array();
180 bt_ = std::make_unique<nav2_behavior_tree::BehaviorTreeEngine>(plugin_lib_names_, client_node_);
183 blackboard_ = BT::Blackboard::create();
186 blackboard_->template set<nav2::LifecycleNode::SharedPtr>(
"node", client_node_);
187 blackboard_->template set<std::chrono::milliseconds>(
"server_timeout", default_server_timeout_);
188 blackboard_->template set<std::chrono::milliseconds>(
"cancel_timeout", default_cancel_timeout_);
189 blackboard_->template set<std::chrono::milliseconds>(
"bt_loop_duration", bt_loop_duration_);
190 blackboard_->template set<std::chrono::milliseconds>(
191 "wait_for_service_timeout",
192 wait_for_service_timeout_);
197 template<
class ActionT,
class NodeT>
200 resetInternalError();
201 if (!loadBehaviorTree(default_bt_xml_filename_or_id_)) {
202 RCLCPP_ERROR(logger_,
"Error loading BT: %s", default_bt_xml_filename_or_id_.c_str());
205 action_server_->activate();
209 template<
class ActionT,
class NodeT>
212 action_server_->deactivate();
216 template<
class ActionT,
class NodeT>
219 client_node_->deactivate();
220 client_node_->cleanup();
221 client_node_.reset();
222 action_server_.reset();
223 topic_logger_.reset();
224 plugin_lib_names_.clear();
225 current_bt_file_or_id_.clear();
227 bt_->haltAllActions(tree_);
228 bt_->resetGrootMonitor();
233 template<
class ActionT,
class NodeT>
236 const unsigned server_port)
238 enable_groot_monitoring_ = enable;
239 groot_server_port_ = server_port;
242 template<
class ActionT,
class NodeT>
245 namespace fs = std::filesystem;
249 bt_xml_filename_or_id.empty() ? default_bt_xml_filename_or_id_ : bt_xml_filename_or_id;
252 if (!always_reload_bt_ && current_bt_file_or_id_ == file_or_id) {
253 RCLCPP_DEBUG(logger_,
"BT will not be reloaded as the given xml or ID is already loaded");
258 bt_->resetGrootMonitor();
260 bool is_bt_id =
false;
261 if (!file_or_id.ends_with(
".xml")) {
265 std::set<std::string> registered_ids;
266 std::vector<std::string> conflicting_files;
268 auto register_all_bt_files = [&](
const std::string & skip_file =
"") {
269 for (
const auto & directory : search_directories_) {
270 for (
const auto & entry : fs::directory_iterator(directory)) {
271 if (entry.path().extension() !=
".xml") {
274 if (!skip_file.empty() && entry.path().string() == skip_file) {
278 auto tree_info = bt_->parseTreeInfo(entry.path().string());
279 if (tree_info.behavior_tree_ids.empty()) {
280 RCLCPP_ERROR(logger_,
"Skipping BT file %s (missing ID)", entry.path().c_str());
284 bool conflict_found =
false;
285 for (
const auto &
id : tree_info.behavior_tree_ids) {
286 if (registered_ids.count(
id)) {
287 conflict_found =
true;
291 if (conflict_found) {
292 conflicting_files.push_back(entry.path().string());
296 RCLCPP_DEBUG(logger_,
"Registering Tree from File: %s", entry.path().string().c_str());
297 bt_->registerTreeFromFile(entry.path().string());
298 for (
const auto &
id : tree_info.behavior_tree_ids) {
299 registered_ids.insert(
id);
308 std::string main_file = file_or_id;
309 auto tree_info = bt_->parseTreeInfo(main_file);
310 if (tree_info.main_id.empty()) {
311 RCLCPP_ERROR(logger_,
"Failed to extract ID from %s", main_file.c_str());
313 ActionT::Result::FAILED_TO_LOAD_BEHAVIOR_TREE,
314 "Failed to extract ID from " + main_file);
317 main_id = tree_info.main_id;
318 RCLCPP_DEBUG(logger_,
"Registering Tree from File: %s", main_file.c_str());
319 bt_->registerTreeFromFile(main_file);
320 for (
const auto &
id : tree_info.behavior_tree_ids) {
321 registered_ids.insert(
id);
332 register_all_bt_files(main_file);
335 main_id = file_or_id;
336 register_all_bt_files();
340 if (!conflicting_files.empty()) {
341 std::string files_list;
342 for (
const auto & file : conflicting_files) {
343 if (!files_list.empty()) {
350 "Skipping conflicting BT XML files, multiple files have the same ID. "
351 "Please set unique behavior tree IDs. This may affect loading of subtrees. "
352 "Files not loaded: %s",
355 }
catch (
const std::exception & e) {
357 ActionT::Result::FAILED_TO_LOAD_BEHAVIOR_TREE,
358 "Exception registering behavior trees: " + std::string(e.what()));
364 tree_ = bt_->createTree(main_id, blackboard_);
365 RCLCPP_INFO(logger_,
"Created BT from ID: %s", main_id.c_str());
367 for (
auto & subtree : tree_.subtrees) {
368 auto & blackboard = subtree->blackboard;
369 blackboard->template set(
"node", client_node_);
370 blackboard->template set<std::chrono::milliseconds>(
"server_timeout",
371 default_server_timeout_);
372 blackboard->template set<std::chrono::milliseconds>(
"cancel_timeout",
373 default_cancel_timeout_);
374 blackboard->template set<std::chrono::milliseconds>(
"bt_loop_duration", bt_loop_duration_);
375 blackboard->template set<std::chrono::milliseconds>(
376 "wait_for_service_timeout",
377 wait_for_service_timeout_);
379 }
catch (
const std::exception & e) {
381 ActionT::Result::FAILED_TO_LOAD_BEHAVIOR_TREE,
382 std::string(
"Exception when creating BT tree from ID: ") + e.what());
387 topic_logger_ = std::make_unique<RosTopicLogger>(client_node_, tree_, log_idle_);
388 current_bt_file_or_id_ = file_or_id;
390 if (enable_groot_monitoring_) {
391 bt_->addGrootMonitoring(&tree_, groot_server_port_);
393 logger_,
"Enabling Groot2 monitoring for %s: %d",
394 action_name_.c_str(), groot_server_port_);
400 template<
class ActionT,
class NodeT>
403 muxer_preemption_requested_ =
false;
405 auto current_goal = action_server_->get_current_goal();
408 ActionT::Result::GOAL_REJECTED,
409 "No current goal available when starting BT execution.");
412 if (!current_goal || !loadBehaviorTree(current_goal->behavior_tree)) {
413 auto result = std::make_shared<typename ActionT::Result>();
414 populateErrorCode(result);
416 nav2_behavior_tree::BtStatus rc = nav2_behavior_tree::BtStatus::FAILED;
417 on_completion_callback_(result, rc);
419 action_server_->terminate_current(result);
421 logger_,
"Goal failed error_code:%d error_msg:'%s'", result->error_code,
422 result->error_msg.c_str());
428 auto is_canceling = [&]() {
429 if (action_server_ ==
nullptr) {
430 RCLCPP_DEBUG(logger_,
"Action server unavailable. Canceling.");
433 if (!action_server_->is_server_active()) {
434 RCLCPP_DEBUG(logger_,
"Action server is inactive. Canceling.");
437 return action_server_->is_cancel_requested() || muxer_preemption_requested_;
440 auto on_loop = [&]() {
441 if (action_server_->is_preempt_requested() && on_preempt_callback_) {
442 on_preempt_callback_(action_server_->get_pending_goal());
444 topic_logger_->flush();
449 nav2_behavior_tree::BtStatus rc = bt_->run(&tree_, on_loop, is_canceling, bt_loop_duration_);
453 bt_->haltAllActions(tree_);
457 auto result = std::make_shared<typename ActionT::Result>();
459 populateErrorCode(result);
461 on_completion_callback_(result, rc);
464 case nav2_behavior_tree::BtStatus::SUCCEEDED:
465 action_server_->succeeded_current(result);
466 RCLCPP_INFO(logger_,
"Goal succeeded");
469 case nav2_behavior_tree::BtStatus::FAILED:
470 action_server_->terminate_current(result);
472 logger_,
"Goal failed error_code:%d error_msg:'%s'", result->error_code,
473 result->error_msg.c_str());
476 case nav2_behavior_tree::BtStatus::CANCELED:
477 action_server_->terminate_all(result);
478 RCLCPP_INFO(logger_,
"Goal canceled");
485 template<
class ActionT,
class NodeT>
488 const std::string & error_msg)
490 internal_error_code_ = error_code;
491 internal_error_msg_ = error_msg;
493 logger_,
"Setting internal error error_code:%d, error_msg:%s",
494 internal_error_code_, internal_error_msg_.c_str());
497 template<
class ActionT,
class NodeT>
500 internal_error_code_ = ActionT::Result::NONE;
501 internal_error_msg_ =
"";
504 template<
class ActionT,
class NodeT>
506 typename std::shared_ptr<typename ActionT::Result> result)
508 if (internal_error_code_ != ActionT::Result::NONE) {
509 result->error_code = internal_error_code_;
510 result->error_msg = internal_error_msg_;
516 template<
class ActionT,
class NodeT>
518 typename std::shared_ptr<typename ActionT::Result> result)
520 int highest_priority_error_code = std::numeric_limits<int>::max();
521 std::string highest_priority_error_msg =
"";
524 if (internal_error_code_ != 0) {
525 highest_priority_error_code = internal_error_code_;
526 highest_priority_error_msg = internal_error_msg_;
529 for (
const auto & error_code_name_prefix : error_code_name_prefixes_) {
531 name = error_code_name_prefix +
"_error_code";
532 int current_error_code = blackboard_->get<
int>(name);
533 if (current_error_code != 0 && current_error_code < highest_priority_error_code) {
534 highest_priority_error_code = current_error_code;
535 name = error_code_name_prefix +
"_error_msg";
536 highest_priority_error_msg = blackboard_->get<std::string>(name);
541 "Failed to get error code name: %s from blackboard",
546 if (highest_priority_error_code != std::numeric_limits<int>::max()) {
547 result->error_code = highest_priority_error_code;
548 result->error_msg = highest_priority_error_msg;
552 template<
class ActionT,
class NodeT>
556 for (
const auto & error_code_name_prefix : error_code_name_prefixes_) {
557 name = error_code_name_prefix +
"_error_code";
558 blackboard_->template set<unsigned short>(name, 0);
559 name = error_code_name_prefix +
"_error_msg";
560 blackboard_->template set<std::string>(name,
"");
562 resetInternalError();
An action server that uses behavior tree to execute an action.
BtActionServer(const typename NodeT::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, const std::vector< std::string > &search_directories=std::vector< std::string >{})
A constructor for nav2_behavior_tree::BtActionServer class.
bool on_cleanup()
Resets member variables.
void setGrootMonitoring(const bool enable, const unsigned server_port)
Enable (or disable) Groot2 monitoring of BT.
bool loadBehaviorTree(const std::string &bt_xml_filename_or_id="")
Replace current BT with another one.
bool populateInternalError(typename std::shared_ptr< typename ActionT::Result > result)
populate result with internal error code and error_msg if not NONE
bool on_deactivate()
Deactivates action server.
~BtActionServer()
A destructor for nav2_behavior_tree::BtActionServer class.
bool on_configure()
Configures member variables Initializes action server for, builds behavior tree from xml file,...
void resetInternalError(void)
reset internal error code and message
void executeCallback()
Action server callback.
void cleanErrorCodes()
Setting BT error codes to success. Used to clean blackboard between different BT runs.
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
bool on_activate()
Activates action server.
void setInternalError(uint16_t error_code, const std::string &error_msg)
Set internal error code and message.