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(
"parameter 'error_code_names' has been replaced by "
81 " 'error_code_name_prefixes' and MUST be removed.\n"
82 " Please review migration guide and update your configuration.");
86 error_code_name_prefixes_ = node->declare_or_get_parameter(
87 "error_code_name_prefixes",
88 default_error_code_name_prefixes);
91 std::string error_code_name_prefixes_str;
92 for (
const auto & error_code_name_prefix : error_code_name_prefixes_) {
93 error_code_name_prefixes_str +=
" " + error_code_name_prefix;
96 if (error_code_name_prefixes_ == default_error_code_name_prefixes) {
98 logger_,
"error_code_name_prefixes parameters were not set. Using default values of:"
99 << error_code_name_prefixes_str +
"\n"
100 <<
"Make sure these match your BT and there are not other sources of error codes you"
101 <<
"reported to your application");
103 RCLCPP_INFO_STREAM(logger_,
"Error_code parameters were set to:"
104 << error_code_name_prefixes_str);
108 template<
class ActionT,
class NodeT>
112 template<
class ActionT,
class NodeT>
115 auto node = node_.lock();
117 throw std::runtime_error{
"Failed to lock node"};
121 std::string client_node_name = action_name_;
122 std::replace(client_node_name.begin(), client_node_name.end(),
'/',
'_');
125 auto new_arguments = node->get_node_options().arguments();
126 nav2::replaceOrAddArgument(new_arguments,
"-r",
"__node", std::string(
"__node:=") +
127 std::string(node->get_name()) +
"_" + client_node_name +
"_rclcpp_node");
128 auto options = node->get_node_options();
129 options = options.arguments(new_arguments);
132 client_node_ = std::make_shared<nav2::LifecycleNode>(
"_", options);
133 client_node_->configure();
134 client_node_->activate();
139 nav2::declare_parameter_if_not_declared(
140 node,
"global_frame", rclcpp::ParameterValue(std::string(
"map")));
141 nav2::declare_parameter_if_not_declared(
142 node,
"robot_base_frame", rclcpp::ParameterValue(std::string(
"base_link")));
143 nav2::declare_parameter_if_not_declared(
144 node,
"transform_tolerance", rclcpp::ParameterValue(0.1));
145 rclcpp::copy_all_parameter_values(node, client_node_);
149 action_server_ = nav2::interfaces::create_action_server<ActionT>(
151 nullptr, std::chrono::milliseconds(500),
false);
154 bt_loop_duration_ = std::chrono::milliseconds(
155 node->declare_or_get_parameter(
"bt_loop_duration", 10));
157 default_server_timeout_ = std::chrono::milliseconds(
158 node->declare_or_get_parameter(
"default_server_timeout", 20));
160 wait_for_service_timeout_ = std::chrono::milliseconds(
161 node->declare_or_get_parameter(
"wait_for_service_timeout", 1000));
163 always_reload_bt_ = node->declare_or_get_parameter(
164 "always_reload_bt_xml",
false);
167 error_code_name_prefixes_ = node->get_parameter(
"error_code_name_prefixes").as_string_array();
170 bt_ = std::make_unique<nav2_behavior_tree::BehaviorTreeEngine>(plugin_lib_names_, client_node_);
173 blackboard_ = BT::Blackboard::create();
176 blackboard_->set<nav2::LifecycleNode::SharedPtr>(
"node", client_node_);
177 blackboard_->set<std::chrono::milliseconds>(
"server_timeout", default_server_timeout_);
178 blackboard_->set<std::chrono::milliseconds>(
"bt_loop_duration", bt_loop_duration_);
179 blackboard_->set<std::chrono::milliseconds>(
180 "wait_for_service_timeout",
181 wait_for_service_timeout_);
186 template<
class ActionT,
class NodeT>
189 resetInternalError();
190 if (!loadBehaviorTree(default_bt_xml_filename_or_id_)) {
191 RCLCPP_ERROR(logger_,
"Error loading BT: %s", default_bt_xml_filename_or_id_.c_str());
194 action_server_->activate();
198 template<
class ActionT,
class NodeT>
201 action_server_->deactivate();
205 template<
class ActionT,
class NodeT>
208 client_node_->deactivate();
209 client_node_->cleanup();
210 client_node_.reset();
211 action_server_.reset();
212 topic_logger_.reset();
213 plugin_lib_names_.clear();
214 current_bt_file_or_id_.clear();
216 bt_->haltAllActions(tree_);
217 bt_->resetGrootMonitor();
222 template<
class ActionT,
class NodeT>
225 const unsigned server_port)
227 enable_groot_monitoring_ = enable;
228 groot_server_port_ = server_port;
231 template<
class ActionT,
class NodeT>
234 namespace fs = std::filesystem;
238 bt_xml_filename_or_id.empty() ? default_bt_xml_filename_or_id_ : bt_xml_filename_or_id;
241 if (!always_reload_bt_ && current_bt_file_or_id_ == file_or_id) {
242 RCLCPP_DEBUG(logger_,
"BT will not be reloaded as the given xml or ID is already loaded");
247 bt_->resetGrootMonitor();
249 bool is_bt_id =
false;
250 if ((file_or_id.length() < 4) ||
251 file_or_id.substr(file_or_id.length() - 4) !=
".xml")
256 std::set<std::string> registered_ids;
258 auto register_all_bt_files = [&](
const std::string & skip_file =
"") {
259 for (
const auto & directory : search_directories_) {
260 for (
const auto & entry : fs::directory_iterator(directory)) {
261 if (entry.path().extension() !=
".xml") {
264 if (!skip_file.empty() && entry.path().string() == skip_file) {
268 auto id = bt_->extractBehaviorTreeID(entry.path().string());
270 RCLCPP_ERROR(logger_,
"Skipping BT file %s (missing ID)", entry.path().c_str());
273 if (registered_ids.count(
id)) {
274 RCLCPP_WARN(logger_,
"Skipping conflicting BT file %s (duplicate ID %s)",
275 entry.path().c_str(),
id.c_str());
279 RCLCPP_DEBUG(logger_,
"Registering Tree from File: %s", entry.path().string().c_str());
280 bt_->registerTreeFromFile(entry.path().string());
281 registered_ids.insert(
id);
289 std::string main_file = file_or_id;
290 main_id = bt_->extractBehaviorTreeID(main_file);
291 if (main_id.empty()) {
292 RCLCPP_ERROR(logger_,
"Failed to extract ID from %s", main_file.c_str());
295 RCLCPP_DEBUG(logger_,
"Registering Tree from File: %s", main_file.c_str());
296 bt_->registerTreeFromFile(main_file);
297 registered_ids.insert(main_id);
307 register_all_bt_files(main_file);
310 main_id = file_or_id;
311 register_all_bt_files();
313 }
catch (
const std::exception & e) {
314 setInternalError(ActionT::Result::FAILED_TO_LOAD_BEHAVIOR_TREE,
315 "Exception registering behavior trees: " + std::string(e.what()));
321 tree_ = bt_->createTree(main_id, blackboard_);
322 RCLCPP_INFO(logger_,
"Created BT from ID: %s", main_id.c_str());
324 for (
auto & subtree : tree_.subtrees) {
325 auto & blackboard = subtree->blackboard;
326 blackboard->set(
"node", client_node_);
327 blackboard->set<std::chrono::milliseconds>(
"server_timeout", default_server_timeout_);
328 blackboard->set<std::chrono::milliseconds>(
"bt_loop_duration", bt_loop_duration_);
329 blackboard->set<std::chrono::milliseconds>(
330 "wait_for_service_timeout", wait_for_service_timeout_);
332 }
catch (
const std::exception & e) {
333 setInternalError(ActionT::Result::FAILED_TO_LOAD_BEHAVIOR_TREE,
334 std::string(
"Exception when creating BT tree from ID: ") + e.what());
339 topic_logger_ = std::make_unique<RosTopicLogger>(client_node_, tree_);
340 current_bt_file_or_id_ = file_or_id;
342 if (enable_groot_monitoring_) {
343 bt_->addGrootMonitoring(&tree_, groot_server_port_);
345 logger_,
"Enabling Groot2 monitoring for %s: %d",
346 action_name_.c_str(), groot_server_port_);
352 template<
class ActionT,
class NodeT>
355 if (!on_goal_received_callback_(action_server_->get_current_goal())) {
358 auto result = std::make_shared<typename ActionT::Result>();
359 populateErrorCode(result);
360 action_server_->terminate_current(result);
365 auto is_canceling = [&]() {
366 if (action_server_ ==
nullptr) {
367 RCLCPP_DEBUG(logger_,
"Action server unavailable. Canceling.");
370 if (!action_server_->is_server_active()) {
371 RCLCPP_DEBUG(logger_,
"Action server is inactive. Canceling.");
374 return action_server_->is_cancel_requested();
377 auto on_loop = [&]() {
378 if (action_server_->is_preempt_requested() && on_preempt_callback_) {
379 on_preempt_callback_(action_server_->get_pending_goal());
381 topic_logger_->flush();
386 nav2_behavior_tree::BtStatus rc = bt_->run(&tree_, on_loop, is_canceling, bt_loop_duration_);
390 bt_->haltAllActions(tree_);
394 auto result = std::make_shared<typename ActionT::Result>();
396 populateErrorCode(result);
398 on_completion_callback_(result, rc);
401 case nav2_behavior_tree::BtStatus::SUCCEEDED:
402 action_server_->succeeded_current(result);
403 RCLCPP_INFO(logger_,
"Goal succeeded");
406 case nav2_behavior_tree::BtStatus::FAILED:
407 action_server_->terminate_current(result);
408 RCLCPP_ERROR(logger_,
"Goal failed error_code:%d error_msg:'%s'", result->error_code,
409 result->error_msg.c_str());
412 case nav2_behavior_tree::BtStatus::CANCELED:
413 action_server_->terminate_all(result);
414 RCLCPP_INFO(logger_,
"Goal canceled");
421 template<
class ActionT,
class NodeT>
424 const std::string & error_msg)
426 internal_error_code_ = error_code;
427 internal_error_msg_ = error_msg;
428 RCLCPP_ERROR(logger_,
"Setting internal error error_code:%d, error_msg:%s",
429 internal_error_code_, internal_error_msg_.c_str());
432 template<
class ActionT,
class NodeT>
435 internal_error_code_ = ActionT::Result::NONE;
436 internal_error_msg_ =
"";
439 template<
class ActionT,
class NodeT>
441 typename std::shared_ptr<typename ActionT::Result> result)
443 if (internal_error_code_ != ActionT::Result::NONE) {
444 result->error_code = internal_error_code_;
445 result->error_msg = internal_error_msg_;
451 template<
class ActionT,
class NodeT>
453 typename std::shared_ptr<typename ActionT::Result> result)
455 int highest_priority_error_code = std::numeric_limits<int>::max();
456 std::string highest_priority_error_msg =
"";
459 if (internal_error_code_ != 0) {
460 highest_priority_error_code = internal_error_code_;
461 highest_priority_error_msg = internal_error_msg_;
464 for (
const auto & error_code_name_prefix : error_code_name_prefixes_) {
466 name = error_code_name_prefix +
"_error_code";
467 int current_error_code = blackboard_->get<
int>(name);
468 if (current_error_code != 0 && current_error_code < highest_priority_error_code) {
469 highest_priority_error_code = current_error_code;
470 name = error_code_name_prefix +
"_error_msg";
471 highest_priority_error_msg = blackboard_->get<std::string>(name);
476 "Failed to get error code name: %s from blackboard",
481 if (highest_priority_error_code != std::numeric_limits<int>::max()) {
482 result->error_code = highest_priority_error_code;
483 result->error_msg = highest_priority_error_msg;
487 template<
class ActionT,
class NodeT>
491 for (
const auto & error_code_name_prefix : error_code_name_prefixes_) {
492 name = error_code_name_prefix +
"_error_code";
493 blackboard_->set<
unsigned short>(name, 0);
494 name = error_code_name_prefix +
"_error_msg";
495 blackboard_->set<std::string>(name,
"");
497 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.