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"
34 namespace nav2_behavior_tree
37 template<
class ActionT,
class NodeT>
39 const typename NodeT::WeakPtr & parent,
40 const std::string & action_name,
41 const std::vector<std::string> & plugin_lib_names,
42 const std::string & default_bt_xml_filename,
43 OnGoalReceivedCallback on_goal_received_callback,
44 OnLoopCallback on_loop_callback,
45 OnPreemptCallback on_preempt_callback,
46 OnCompletionCallback on_completion_callback,
47 const std::vector<std::string> & search_directories)
48 : action_name_(action_name),
49 default_bt_xml_filename_or_id_(default_bt_xml_filename),
50 search_directories_(search_directories),
51 plugin_lib_names_(plugin_lib_names),
53 on_goal_received_callback_(on_goal_received_callback),
54 on_loop_callback_(on_loop_callback),
55 on_preempt_callback_(on_preempt_callback),
56 on_completion_callback_(on_completion_callback),
57 internal_error_code_(0),
60 auto node = node_.lock();
61 logger_ = node->get_logger();
62 clock_ = node->get_clock();
64 std::vector<std::string> default_error_code_name_prefixes = {
79 if (node->has_parameter(
"error_code_names")) {
80 throw std::runtime_error(
81 "parameter 'error_code_names' has been replaced by "
82 " 'error_code_name_prefixes' and MUST be removed.\n"
83 " Please review migration guide and update your configuration.");
87 error_code_name_prefixes_ = node->declare_or_get_parameter(
88 "error_code_name_prefixes",
89 default_error_code_name_prefixes);
92 std::string error_code_name_prefixes_str;
93 for (
const auto & error_code_name_prefix : error_code_name_prefixes_) {
94 error_code_name_prefixes_str +=
" " + error_code_name_prefix;
97 if (error_code_name_prefixes_ == default_error_code_name_prefixes) {
99 logger_,
"error_code_name_prefixes parameters were not set. Using default values of:"
100 << error_code_name_prefixes_str +
"\n"
101 <<
"Make sure these match your BT and there are not other sources of error codes you"
102 <<
"reported to your application");
105 logger_,
"Error_code parameters were set to:"
106 << error_code_name_prefixes_str);
110 template<
class ActionT,
class NodeT>
114 template<
class ActionT,
class NodeT>
117 auto node = node_.lock();
119 throw std::runtime_error{
"Failed to lock node"};
123 std::string client_node_name = action_name_;
124 std::replace(client_node_name.begin(), client_node_name.end(),
'/',
'_');
127 auto new_arguments = node->get_node_options().arguments();
128 nav2::replaceOrAddArgument(
129 new_arguments,
"-r",
"__node", std::string(
"__node:=") +
130 std::string(node->get_name()) +
"_" + client_node_name +
"_rclcpp_node");
131 auto options = node->get_node_options();
132 options = options.arguments(new_arguments);
135 client_node_ = std::make_shared<nav2::LifecycleNode>(
"_", options);
136 client_node_->configure();
137 client_node_->activate();
142 nav2::declare_parameter_if_not_declared(
143 node,
"global_frame", rclcpp::ParameterValue(std::string(
"map")));
144 nav2::declare_parameter_if_not_declared(
145 node,
"robot_base_frame", rclcpp::ParameterValue(std::string(
"base_link")));
146 nav2::declare_parameter_if_not_declared(
147 node,
"transform_tolerance", rclcpp::ParameterValue(0.1));
148 rclcpp::copy_all_parameter_values(node, client_node_);
152 action_server_ = nav2::interfaces::create_action_server<ActionT>(
154 on_goal_received_callback_,
nullptr, std::chrono::milliseconds(500),
false);
157 bt_loop_duration_ = std::chrono::milliseconds(
158 node->declare_or_get_parameter(
"bt_loop_duration", 10));
160 default_server_timeout_ = std::chrono::milliseconds(
161 node->declare_or_get_parameter(
"default_server_timeout", 20));
163 default_cancel_timeout_ = std::chrono::milliseconds(
164 node->declare_or_get_parameter(
"default_cancel_timeout", 50));
166 wait_for_service_timeout_ = std::chrono::milliseconds(
167 node->declare_or_get_parameter(
"wait_for_service_timeout", 1000));
169 always_reload_bt_ = node->declare_or_get_parameter(
170 "always_reload_bt_xml",
false);
172 log_idle_ = node->declare_or_get_parameter(
173 "bt_log_idle_transitions",
true);
176 error_code_name_prefixes_ = node->get_parameter(
"error_code_name_prefixes").as_string_array();
179 bt_ = std::make_unique<nav2_behavior_tree::BehaviorTreeEngine>(plugin_lib_names_, client_node_);
182 blackboard_ = BT::Blackboard::create();
185 blackboard_->template set<nav2::LifecycleNode::SharedPtr>(
"node", client_node_);
186 blackboard_->template set<std::chrono::milliseconds>(
"server_timeout", default_server_timeout_);
187 blackboard_->template set<std::chrono::milliseconds>(
"cancel_timeout", default_cancel_timeout_);
188 blackboard_->template set<std::chrono::milliseconds>(
"bt_loop_duration", bt_loop_duration_);
189 blackboard_->template set<std::chrono::milliseconds>(
190 "wait_for_service_timeout",
191 wait_for_service_timeout_);
196 template<
class ActionT,
class NodeT>
199 resetInternalError();
200 if (!loadBehaviorTree(default_bt_xml_filename_or_id_)) {
201 RCLCPP_ERROR(logger_,
"Error loading BT: %s", default_bt_xml_filename_or_id_.c_str());
204 action_server_->activate();
208 template<
class ActionT,
class NodeT>
211 action_server_->deactivate();
215 template<
class ActionT,
class NodeT>
218 client_node_->deactivate();
219 client_node_->cleanup();
220 client_node_.reset();
221 action_server_.reset();
222 topic_logger_.reset();
223 plugin_lib_names_.clear();
224 current_bt_file_or_id_.clear();
226 bt_->haltAllActions(tree_);
227 bt_->resetGrootMonitor();
232 template<
class ActionT,
class NodeT>
235 const unsigned server_port)
237 enable_groot_monitoring_ = enable;
238 groot_server_port_ = server_port;
241 template<
class ActionT,
class NodeT>
244 namespace fs = std::filesystem;
248 bt_xml_filename_or_id.empty() ? default_bt_xml_filename_or_id_ : bt_xml_filename_or_id;
251 if (!always_reload_bt_ && current_bt_file_or_id_ == file_or_id) {
252 RCLCPP_DEBUG(logger_,
"BT will not be reloaded as the given xml or ID is already loaded");
257 bt_->resetGrootMonitor();
259 bool is_bt_id =
false;
260 if (!file_or_id.ends_with(
".xml")) {
264 std::set<std::string> registered_ids;
265 std::vector<std::string> conflicting_files;
267 auto register_all_bt_files = [&](
const std::string & skip_file =
"") {
268 for (
const auto & directory : search_directories_) {
269 for (
const auto & entry : fs::directory_iterator(directory)) {
270 if (entry.path().extension() !=
".xml") {
273 if (!skip_file.empty() && entry.path().string() == skip_file) {
277 auto tree_info = bt_->parseTreeInfo(entry.path().string());
278 if (tree_info.behavior_tree_ids.empty()) {
279 RCLCPP_ERROR(logger_,
"Skipping BT file %s (missing ID)", entry.path().c_str());
283 bool conflict_found =
false;
284 for (
const auto &
id : tree_info.behavior_tree_ids) {
285 if (registered_ids.count(
id)) {
286 conflict_found =
true;
290 if (conflict_found) {
291 conflicting_files.push_back(entry.path().string());
295 RCLCPP_DEBUG(logger_,
"Registering Tree from File: %s", entry.path().string().c_str());
296 bt_->registerTreeFromFile(entry.path().string());
297 for (
const auto &
id : tree_info.behavior_tree_ids) {
298 registered_ids.insert(
id);
307 std::string main_file = file_or_id;
308 auto tree_info = bt_->parseTreeInfo(main_file);
309 if (tree_info.main_id.empty()) {
310 RCLCPP_ERROR(logger_,
"Failed to extract ID from %s", main_file.c_str());
312 ActionT::Result::FAILED_TO_LOAD_BEHAVIOR_TREE,
313 "Failed to extract ID from " + main_file);
316 main_id = tree_info.main_id;
317 RCLCPP_DEBUG(logger_,
"Registering Tree from File: %s", main_file.c_str());
318 bt_->registerTreeFromFile(main_file);
319 for (
const auto &
id : tree_info.behavior_tree_ids) {
320 registered_ids.insert(
id);
331 register_all_bt_files(main_file);
334 main_id = file_or_id;
335 register_all_bt_files();
339 if (!conflicting_files.empty()) {
340 std::string files_list;
341 for (
const auto & file : conflicting_files) {
342 if (!files_list.empty()) {
349 "Skipping conflicting BT XML files, multiple files have the same ID. "
350 "Please set unique behavior tree IDs. This may affect loading of subtrees. "
351 "Files not loaded: %s",
354 }
catch (
const std::exception & e) {
356 ActionT::Result::FAILED_TO_LOAD_BEHAVIOR_TREE,
357 "Exception registering behavior trees: " + std::string(e.what()));
363 tree_ = bt_->createTree(main_id, blackboard_);
364 RCLCPP_INFO(logger_,
"Created BT from ID: %s", main_id.c_str());
366 for (
auto & subtree : tree_.subtrees) {
367 auto & blackboard = subtree->blackboard;
368 blackboard->template set(
"node", client_node_);
369 blackboard->template set<std::chrono::milliseconds>(
"server_timeout",
370 default_server_timeout_);
371 blackboard->template set<std::chrono::milliseconds>(
"cancel_timeout",
372 default_cancel_timeout_);
373 blackboard->template set<std::chrono::milliseconds>(
"bt_loop_duration", bt_loop_duration_);
374 blackboard->template set<std::chrono::milliseconds>(
375 "wait_for_service_timeout",
376 wait_for_service_timeout_);
378 }
catch (
const std::exception & e) {
380 ActionT::Result::FAILED_TO_LOAD_BEHAVIOR_TREE,
381 std::string(
"Exception when creating BT tree from ID: ") + e.what());
386 topic_logger_ = std::make_unique<RosTopicLogger>(client_node_, tree_, log_idle_);
387 current_bt_file_or_id_ = file_or_id;
389 if (enable_groot_monitoring_) {
390 bt_->addGrootMonitoring(&tree_, groot_server_port_);
392 logger_,
"Enabling Groot2 monitoring for %s: %d",
393 action_name_.c_str(), groot_server_port_);
399 template<
class ActionT,
class NodeT>
402 muxer_preemption_requested_ =
false;
404 auto current_goal = action_server_->get_current_goal();
407 ActionT::Result::GOAL_REJECTED,
408 "No current goal available when starting BT execution.");
411 if (!current_goal || !loadBehaviorTree(current_goal->behavior_tree)) {
412 auto result = std::make_shared<typename ActionT::Result>();
413 populateErrorCode(result);
415 nav2_behavior_tree::BtStatus rc = nav2_behavior_tree::BtStatus::FAILED;
416 on_completion_callback_(result, rc);
418 action_server_->terminate_current(result);
420 logger_,
"Goal failed error_code:%d error_msg:'%s'", result->error_code,
421 result->error_msg.c_str());
427 auto is_canceling = [&]() {
428 if (action_server_ ==
nullptr) {
429 RCLCPP_DEBUG(logger_,
"Action server unavailable. Canceling.");
432 if (!action_server_->is_server_active()) {
433 RCLCPP_DEBUG(logger_,
"Action server is inactive. Canceling.");
436 return action_server_->is_cancel_requested() || muxer_preemption_requested_;
439 auto on_loop = [&]() {
440 if (action_server_->is_preempt_requested() && on_preempt_callback_) {
441 on_preempt_callback_(action_server_->get_pending_goal());
443 topic_logger_->flush();
448 nav2_behavior_tree::BtStatus rc = bt_->run(&tree_, on_loop, is_canceling, bt_loop_duration_);
452 bt_->haltAllActions(tree_);
456 auto result = std::make_shared<typename ActionT::Result>();
458 populateErrorCode(result);
460 on_completion_callback_(result, rc);
463 case nav2_behavior_tree::BtStatus::SUCCEEDED:
464 action_server_->succeeded_current(result);
465 RCLCPP_INFO(logger_,
"Goal succeeded");
468 case nav2_behavior_tree::BtStatus::FAILED:
469 action_server_->terminate_current(result);
471 logger_,
"Goal failed error_code:%d error_msg:'%s'", result->error_code,
472 result->error_msg.c_str());
475 case nav2_behavior_tree::BtStatus::CANCELED:
476 action_server_->terminate_all(result);
477 RCLCPP_INFO(logger_,
"Goal canceled");
484 template<
class ActionT,
class NodeT>
487 const std::string & error_msg)
489 internal_error_code_ = error_code;
490 internal_error_msg_ = error_msg;
492 logger_,
"Setting internal error error_code:%d, error_msg:%s",
493 internal_error_code_, internal_error_msg_.c_str());
496 template<
class ActionT,
class NodeT>
499 internal_error_code_ = ActionT::Result::NONE;
500 internal_error_msg_ =
"";
503 template<
class ActionT,
class NodeT>
505 typename std::shared_ptr<typename ActionT::Result> result)
507 if (internal_error_code_ != ActionT::Result::NONE) {
508 result->error_code = internal_error_code_;
509 result->error_msg = internal_error_msg_;
515 template<
class ActionT,
class NodeT>
517 typename std::shared_ptr<typename ActionT::Result> result)
519 int highest_priority_error_code = std::numeric_limits<int>::max();
520 std::string highest_priority_error_msg =
"";
523 if (internal_error_code_ != 0) {
524 highest_priority_error_code = internal_error_code_;
525 highest_priority_error_msg = internal_error_msg_;
528 for (
const auto & error_code_name_prefix : error_code_name_prefixes_) {
530 name = error_code_name_prefix +
"_error_code";
531 int current_error_code = blackboard_->get<
int>(name);
532 if (current_error_code != 0 && current_error_code < highest_priority_error_code) {
533 highest_priority_error_code = current_error_code;
534 name = error_code_name_prefix +
"_error_msg";
535 highest_priority_error_msg = blackboard_->get<std::string>(name);
540 "Failed to get error code name: %s from blackboard",
545 if (highest_priority_error_code != std::numeric_limits<int>::max()) {
546 result->error_code = highest_priority_error_code;
547 result->error_msg = highest_priority_error_msg;
551 template<
class ActionT,
class NodeT>
555 for (
const auto & error_code_name_prefix : error_code_name_prefixes_) {
556 name = error_code_name_prefix +
"_error_code";
557 blackboard_->template set<unsigned short>(name, 0);
558 name = error_code_name_prefix +
"_error_msg";
559 blackboard_->template set<std::string>(name,
"");
561 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.