15 #ifndef NAV2_BEHAVIOR_TREE__BT_ACTION_SERVER_IMPL_HPP_
16 #define NAV2_BEHAVIOR_TREE__BT_ACTION_SERVER_IMPL_HPP_
24 #include <unordered_set>
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();
65 if (!node->has_parameter(
"bt_loop_duration")) {
66 node->declare_parameter(
"bt_loop_duration", 10);
68 if (!node->has_parameter(
"default_server_timeout")) {
69 node->declare_parameter(
"default_server_timeout", 20);
71 if (!node->has_parameter(
"always_reload_bt_xml")) {
72 node->declare_parameter(
"always_reload_bt_xml",
false);
74 if (!node->has_parameter(
"wait_for_service_timeout")) {
75 node->declare_parameter(
"wait_for_service_timeout", 1000);
78 std::vector<std::string> error_code_name_prefixes = {
92 if (node->has_parameter(
"error_code_names")) {
93 throw std::runtime_error(
"parameter 'error_code_names' has been replaced by "
94 " 'error_code_name_prefixes' and MUST be removed.\n"
95 " Please review migration guide and update your configuration.");
98 if (!node->has_parameter(
"error_code_name_prefixes")) {
99 const rclcpp::ParameterValue value = node->declare_parameter(
100 "error_code_name_prefixes",
101 rclcpp::PARAMETER_STRING_ARRAY);
102 if (value.get_type() == rclcpp::PARAMETER_NOT_SET) {
103 std::string error_code_name_prefixes_str;
104 for (
const auto & error_code_name_prefix : error_code_name_prefixes) {
105 error_code_name_prefixes_str +=
" " + error_code_name_prefix;
108 logger_,
"error_code_name_prefixes parameters were not set. Using default values of:"
109 << error_code_name_prefixes_str +
"\n"
110 <<
"Make sure these match your BT and there are not other sources of error codes you"
111 "reported to your application");
112 rclcpp::Parameter error_code_name_prefixes_param(
"error_code_name_prefixes",
113 error_code_name_prefixes);
114 node->set_parameter(error_code_name_prefixes_param);
116 error_code_name_prefixes = value.get<std::vector<std::string>>();
117 std::string error_code_name_prefixes_str;
118 for (
const auto & error_code_name_prefix : error_code_name_prefixes) {
119 error_code_name_prefixes_str +=
" " + error_code_name_prefix;
121 RCLCPP_INFO_STREAM(logger_,
"Error_code parameters were set to:"
122 << error_code_name_prefixes_str);
127 template<
class ActionT,
class NodeT>
131 template<
class ActionT,
class NodeT>
134 auto node = node_.lock();
136 throw std::runtime_error{
"Failed to lock node"};
140 std::string client_node_name = action_name_;
141 std::replace(client_node_name.begin(), client_node_name.end(),
'/',
'_');
144 auto new_arguments = node->get_node_options().arguments();
145 nav2::replaceOrAddArgument(new_arguments,
"-r",
"__node", std::string(
"__node:=") +
146 std::string(node->get_name()) +
"_" + client_node_name +
"_rclcpp_node");
147 auto options = node->get_node_options();
148 options = options.arguments(new_arguments);
151 client_node_ = std::make_shared<nav2::LifecycleNode>(
"_", options);
152 client_node_->configure();
153 client_node_->activate();
158 nav2::declare_parameter_if_not_declared(
159 node,
"global_frame", rclcpp::ParameterValue(std::string(
"map")));
160 nav2::declare_parameter_if_not_declared(
161 node,
"robot_base_frame", rclcpp::ParameterValue(std::string(
"base_link")));
162 nav2::declare_parameter_if_not_declared(
163 node,
"transform_tolerance", rclcpp::ParameterValue(0.1));
164 rclcpp::copy_all_parameter_values(node, client_node_);
168 action_server_ = nav2::interfaces::create_action_server<ActionT>(
170 nullptr, std::chrono::milliseconds(500),
false);
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_);
185 error_code_name_prefixes_ = node->get_parameter(
"error_code_name_prefixes").as_string_array();
188 bt_ = std::make_unique<nav2_behavior_tree::BehaviorTreeEngine>(plugin_lib_names_, client_node_);
191 blackboard_ = BT::Blackboard::create();
194 blackboard_->set<nav2::LifecycleNode::SharedPtr>(
"node", client_node_);
195 blackboard_->set<std::chrono::milliseconds>(
"server_timeout", default_server_timeout_);
196 blackboard_->set<std::chrono::milliseconds>(
"bt_loop_duration", bt_loop_duration_);
197 blackboard_->set<std::chrono::milliseconds>(
198 "wait_for_service_timeout",
199 wait_for_service_timeout_);
204 template<
class ActionT,
class NodeT>
207 resetInternalError();
208 if (!loadBehaviorTree(default_bt_xml_filename_or_id_)) {
209 RCLCPP_ERROR(logger_,
"Error loading BT: %s", default_bt_xml_filename_or_id_.c_str());
212 action_server_->activate();
216 template<
class ActionT,
class NodeT>
219 action_server_->deactivate();
223 template<
class ActionT,
class NodeT>
226 client_node_->deactivate();
227 client_node_->cleanup();
228 client_node_.reset();
229 action_server_.reset();
230 topic_logger_.reset();
231 plugin_lib_names_.clear();
232 current_bt_file_or_id_.clear();
234 bt_->haltAllActions(tree_);
235 bt_->resetGrootMonitor();
240 template<
class ActionT,
class NodeT>
243 const unsigned server_port)
245 enable_groot_monitoring_ = enable;
246 groot_server_port_ = server_port;
249 template<
class ActionT,
class NodeT>
252 namespace fs = std::filesystem;
256 bt_xml_filename_or_id.empty() ? default_bt_xml_filename_or_id_ : bt_xml_filename_or_id;
259 if (!always_reload_bt_ && current_bt_file_or_id_ == file_or_id) {
260 RCLCPP_DEBUG(logger_,
"BT will not be reloaded as the given xml or ID is already loaded");
265 bt_->resetGrootMonitor();
267 bool is_bt_id =
false;
268 if ((file_or_id.length() < 4) ||
269 file_or_id.substr(file_or_id.length() - 4) !=
".xml")
274 std::unordered_set<std::string> used_bt_id;
275 for (
const auto & directory : search_directories_) {
277 for (
const auto & entry : fs::directory_iterator(directory)) {
278 if (entry.path().extension() ==
".xml") {
279 auto current_bt_id = bt_->extractBehaviorTreeID(entry.path().string());
280 if (current_bt_id.empty()) {
281 RCLCPP_ERROR(logger_,
"Skipping BT file %s (missing ID)",
282 entry.path().string().c_str());
285 auto [it, inserted] = used_bt_id.insert(current_bt_id);
289 "Warning: Duplicate BT IDs found. Make sure to have all BT IDs unique! "
291 current_bt_id.c_str(), entry.path().string().c_str());
293 bt_->registerTreeFromFile(entry.path().string());
296 }
catch (
const std::exception & e) {
297 setInternalError(ActionT::Result::FAILED_TO_LOAD_BEHAVIOR_TREE,
298 "Exception reading behavior tree directory: " + std::string(e.what()));
305 tree_ = bt_->createTreeFromFile(file_or_id, blackboard_);
307 tree_ = bt_->createTree(file_or_id, blackboard_);
310 for (
auto & subtree : tree_.subtrees) {
311 auto & blackboard = subtree->blackboard;
312 blackboard->set(
"node", client_node_);
313 blackboard->set<std::chrono::milliseconds>(
"server_timeout", default_server_timeout_);
314 blackboard->set<std::chrono::milliseconds>(
"bt_loop_duration", bt_loop_duration_);
315 blackboard->set<std::chrono::milliseconds>(
316 "wait_for_service_timeout", wait_for_service_timeout_);
318 }
catch (
const std::exception & e) {
319 setInternalError(ActionT::Result::FAILED_TO_LOAD_BEHAVIOR_TREE,
320 std::string(
"Exception when creating BT tree from file: ") + e.what());
325 topic_logger_ = std::make_unique<RosTopicLogger>(client_node_, tree_);
326 current_bt_file_or_id_ = file_or_id;
328 if (enable_groot_monitoring_) {
329 bt_->addGrootMonitoring(&tree_, groot_server_port_);
331 logger_,
"Enabling Groot2 monitoring for %s: %d",
332 action_name_.c_str(), groot_server_port_);
338 template<
class ActionT,
class NodeT>
341 if (!on_goal_received_callback_(action_server_->get_current_goal())) {
344 auto result = std::make_shared<typename ActionT::Result>();
345 populateErrorCode(result);
346 action_server_->terminate_current(result);
351 auto is_canceling = [&]() {
352 if (action_server_ ==
nullptr) {
353 RCLCPP_DEBUG(logger_,
"Action server unavailable. Canceling.");
356 if (!action_server_->is_server_active()) {
357 RCLCPP_DEBUG(logger_,
"Action server is inactive. Canceling.");
360 return action_server_->is_cancel_requested();
363 auto on_loop = [&]() {
364 if (action_server_->is_preempt_requested() && on_preempt_callback_) {
365 on_preempt_callback_(action_server_->get_pending_goal());
367 topic_logger_->flush();
372 nav2_behavior_tree::BtStatus rc = bt_->run(&tree_, on_loop, is_canceling, bt_loop_duration_);
376 bt_->haltAllActions(tree_);
380 auto result = std::make_shared<typename ActionT::Result>();
382 populateErrorCode(result);
384 on_completion_callback_(result, rc);
387 case nav2_behavior_tree::BtStatus::SUCCEEDED:
388 action_server_->succeeded_current(result);
389 RCLCPP_INFO(logger_,
"Goal succeeded");
392 case nav2_behavior_tree::BtStatus::FAILED:
393 action_server_->terminate_current(result);
394 RCLCPP_ERROR(logger_,
"Goal failed error_code:%d error_msg:'%s'", result->error_code,
395 result->error_msg.c_str());
398 case nav2_behavior_tree::BtStatus::CANCELED:
399 action_server_->terminate_all(result);
400 RCLCPP_INFO(logger_,
"Goal canceled");
407 template<
class ActionT,
class NodeT>
410 const std::string & error_msg)
412 internal_error_code_ = error_code;
413 internal_error_msg_ = error_msg;
414 RCLCPP_ERROR(logger_,
"Setting internal error error_code:%d, error_msg:%s",
415 internal_error_code_, internal_error_msg_.c_str());
418 template<
class ActionT,
class NodeT>
421 internal_error_code_ = ActionT::Result::NONE;
422 internal_error_msg_ =
"";
425 template<
class ActionT,
class NodeT>
427 typename std::shared_ptr<typename ActionT::Result> result)
429 if (internal_error_code_ != ActionT::Result::NONE) {
430 result->error_code = internal_error_code_;
431 result->error_msg = internal_error_msg_;
437 template<
class ActionT,
class NodeT>
439 typename std::shared_ptr<typename ActionT::Result> result)
441 int highest_priority_error_code = std::numeric_limits<int>::max();
442 std::string highest_priority_error_msg =
"";
445 if (internal_error_code_ != 0) {
446 highest_priority_error_code = internal_error_code_;
447 highest_priority_error_msg = internal_error_msg_;
450 for (
const auto & error_code_name_prefix : error_code_name_prefixes_) {
452 name = error_code_name_prefix +
"_error_code";
453 int current_error_code = blackboard_->get<
int>(name);
454 if (current_error_code != 0 && current_error_code < highest_priority_error_code) {
455 highest_priority_error_code = current_error_code;
456 name = error_code_name_prefix +
"_error_msg";
457 highest_priority_error_msg = blackboard_->get<std::string>(name);
462 "Failed to get error code name: %s from blackboard",
467 if (highest_priority_error_code != std::numeric_limits<int>::max()) {
468 result->error_code = highest_priority_error_code;
469 result->error_msg = highest_priority_error_msg;
473 template<
class ActionT,
class NodeT>
477 for (
const auto & error_code_name_prefix : error_code_name_prefixes_) {
478 name = error_code_name_prefix +
"_error_code";
479 blackboard_->set<
unsigned short>(name, 0);
480 name = error_code_name_prefix +
"_error_msg";
481 blackboard_->set<std::string>(name,
"");
483 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.