15#include "auto_apms_behavior_tree/executor/generic_executor_node.hpp"
21#include "auto_apms_behavior_tree/exceptions.hpp"
22#include "auto_apms_behavior_tree/util/parameter.hpp"
23#include "auto_apms_behavior_tree_core/definitions.hpp"
24#include "auto_apms_util/container.hpp"
25#include "auto_apms_util/string.hpp"
26#include "pluginlib/exceptions.hpp"
34 if (executor_options_.strict_unkown_parameter_removal_) {
36 rcl_interfaces::msg::ListParametersResult res = node_ptr_->list_parameters({}, 0);
37 std::vector<std::string> unknown_param_names;
38 for (
const std::string & param_name : res.names) {
39 if (!stripPrefixFromParameterName(SCRIPTING_ENUM_PARAM_PREFIX, param_name).empty()) continue;
40 if (!stripPrefixFromParameterName(BLACKBOARD_PARAM_PREFIX, param_name).empty()) continue;
41 if (auto_apms_util::contains(TREE_EXECUTOR_EXPLICITLY_ALLOWED_PARAMETERS, param_name)) continue;
43 node_ptr_->undeclare_parameter(param_name);
44 } catch (
const rclcpp::exceptions::ParameterImmutableException & e) {
47 }
catch (
const rclcpp::exceptions::InvalidParameterTypeException & e) {
51 unknown_param_names.push_back(param_name);
53 if (!unknown_param_names.empty()) {
55 logger_,
"The following initial parameters are not supported and have been removed: [ %s ].",
56 auto_apms_util::join(unknown_param_names,
", ").c_str());
61 std::vector<rclcpp::Parameter> new_default_parameters;
62 std::map<std::string, rclcpp::ParameterValue> effective_param_overrides =
63 node_ptr_->get_node_parameters_interface()->get_parameter_overrides();
64 for (
const auto & [name, value] : executor_options_.custom_default_parameters_) {
65 if (effective_param_overrides.find(name) == effective_param_overrides.end()) {
66 new_default_parameters.push_back(rclcpp::Parameter(name, value));
69 if (!new_default_parameters.empty()) node_ptr_->set_parameters_atomically(new_default_parameters);
71 const ExecutorParameters initial_params = executor_param_listener_.get_params();
74 tree_node_loader_ptr_ = core::NodeRegistrationLoader::make_shared(
75 std::set<std::string>(initial_params.node_exclude_packages.begin(), initial_params.node_exclude_packages.end()));
78 build_handler_loader_ptr_ = TreeBuildHandlerLoader::make_unique(
79 std::set<std::string>(
80 initial_params.build_handler_exclude_packages.begin(), initial_params.build_handler_exclude_packages.end()));
84 initial_params.build_handler != PARAM_VALUE_NO_BUILD_HANDLER &&
85 !build_handler_loader_ptr_->isClassAvailable(initial_params.build_handler)) {
86 throw exceptions::TreeExecutorError(
87 "Cannot load build handler '" + initial_params.build_handler +
88 "' because no corresponding ament_index resource was found. Make sure that you spelled the build handler's "
90 "and registered it by calling auto_apms_behavior_tree_register_build_handlers() in the CMakeLists.txt of the "
91 "corresponding package.");
93 loadBuildHandler(initial_params.build_handler);
96 const auto initial_scripting_enums = getParameterValuesWithPrefix(SCRIPTING_ENUM_PARAM_PREFIX);
97 if (!initial_scripting_enums.empty()) {
98 if (executor_options_.scripting_enum_parameters_from_overrides_) {
99 updateScriptingEnumsWithParameterValues(initial_scripting_enums);
103 "Initial scripting enums have been provided, but the 'Scripting enums from overrides' option is disabled. "
107 const auto initial_blackboard = getParameterValuesWithPrefix(BLACKBOARD_PARAM_PREFIX);
108 if (!initial_blackboard.empty()) {
109 if (executor_options_.blackboard_parameters_from_overrides_) {
110 updateGlobalBlackboardWithParameterValues(initial_blackboard);
114 "Initial blackboard entries have been provided, but the 'Blackboard from overrides' option is disabled. "
119 using namespace std::placeholders;
122 const std::string command_action_name =
123 executor_options_.command_action_name_.empty()
124 ? std::string(node_ptr_->get_name()) + _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_COMMAND_ACTION_NAME_SUFFIX
125 : executor_options_.command_action_name_;
126 const std::string clear_bb_service_name =
127 executor_options_.clear_blackboard_service_name_.empty()
128 ? std::string(node_ptr_->get_name()) + _AUTO_APMS_BEHAVIOR_TREE__CLEAR_BLACKBOARD_SERVICE_NAME_SUFFIX
129 : executor_options_.clear_blackboard_service_name_;
132 if (executor_options_.enable_command_action_) {
133 command_action_ptr_ = rclcpp_action::create_server<CommandActionContext::Type>(
134 node_ptr_, command_action_name, std::bind(&GenericTreeExecutorNode::handle_command_goal_,
this, _1, _2),
135 std::bind(&GenericTreeExecutorNode::handle_command_cancel_,
this, _1),
136 std::bind(&GenericTreeExecutorNode::handle_command_accept_,
this, _1));
140 if (executor_options_.enable_clear_blackboard_service_) {
141 clear_blackboard_service_ptr_ = node_ptr_->create_service<std_srvs::srv::Trigger>(
142 clear_bb_service_name, [
this](
143 const std::shared_ptr<std_srvs::srv::Trigger::Request> ,
144 std::shared_ptr<std_srvs::srv::Trigger::Response> response) {
145 response->success = this->clearGlobalBlackboard();
146 if (response->success) {
147 response->message =
"Blackboard was cleared successfully";
149 response->message =
"Blackboard cannot be cleared, because executor is in state " +
150 toStr(this->getExecutionState()) +
" but must be idling";
152 RCLCPP_DEBUG_STREAM(this->logger_, response->message);
158 executor_options_.scripting_enum_parameters_from_overrides_ ||
159 executor_options_.scripting_enum_parameters_dynamic_ || executor_options_.blackboard_parameters_from_overrides_ ||
160 executor_options_.blackboard_parameters_dynamic_) {
161 on_set_parameters_callback_handle_ptr_ =
162 node_ptr_->add_on_set_parameters_callback([
this](
const std::vector<rclcpp::Parameter> & parameters) {
163 return this->on_set_parameters_callback_(parameters);
166 parameter_event_handler_ptr_ = std::make_shared<rclcpp::ParameterEventHandler>(node_ptr_);
167 parameter_event_callback_handle_ptr_ = parameter_event_handler_ptr_->add_parameter_event_callback(
168 [
this](
const rcl_interfaces::msg::ParameterEvent & event) { this->parameter_event_callback_(event); });
188 const std::string & build_request,
const std::string & entry_point,
const core::NodeManifest & node_manifest)
190 const ExecutorParameters params = executor_param_listener_.get_params();
192 makeTreeConstructor(build_request, entry_point, node_manifest), params.tick_rate, params.groot2_port);
195bool GenericTreeExecutorNode::onTick()
197 const ExecutorParameters params = executor_param_listener_.get_params();
202bool GenericTreeExecutorNode::afterTick()
204 const ExecutorParameters params = executor_param_listener_.get_params();
207 if (executor_options_.blackboard_parameters_dynamic_ && params.allow_dynamic_blackboard) {
209 std::vector<rclcpp::Parameter> new_parameters;
210 for (
const BT::StringView & str : bb_ptr->getKeys()) {
211 const std::string key = std::string(str);
212 const BT::TypeInfo * type_info = bb_ptr->entryInfo(key);
213 const BT::Any * any = bb_ptr->getAnyLocked(key).get();
215 if (any->empty())
continue;
217 if (translated_global_blackboard_entries_.find(key) == translated_global_blackboard_entries_.end()) {
218 const BT::Expected<rclcpp::ParameterValue> expected =
221 new_parameters.push_back(rclcpp::Parameter(BLACKBOARD_PARAM_PREFIX +
"." + key, expected.value()));
222 translated_global_blackboard_entries_[key] = expected.value();
225 logger_,
"Failed to translate new blackboard entry '%s' (Type: %s) to parameters: %s", key.c_str(),
226 type_info->typeName().c_str(), expected.error().c_str());
229 const BT::Expected<rclcpp::ParameterValue> expected =
232 if (expected.value() != translated_global_blackboard_entries_[key]) {
233 new_parameters.push_back(rclcpp::Parameter(BLACKBOARD_PARAM_PREFIX +
"." + key, expected.value()));
237 logger_,
"Failed to translate blackboard entry '%s' (Type: %s) to parameters: %s", key.c_str(),
238 type_info->typeName().c_str(), expected.error().c_str());
242 if (!new_parameters.empty()) {
243 const rcl_interfaces::msg::SetParametersResult result =
node_ptr_->set_parameters_atomically(new_parameters);
244 if (!result.successful) {
245 throw exceptions::TreeExecutorError(
246 "Unexpectedly failed to set parameters inferred from global blackboard. Reason: " + result.reason);
256 return executor_param_listener_.get_params();
260 const std::string & prefix)
262 const auto res =
node_ptr_->list_parameters({prefix}, 2);
263 std::map<std::string, rclcpp::ParameterValue> value_map;
264 for (
const std::string & name_with_prefix : res.names) {
266 value_map[suffix] =
node_ptr_->get_parameter(name_with_prefix).get_parameter_value();
273 const std::string & prefix,
const std::string & param_name)
275 const std::regex reg(
"^" + prefix +
"\\.(\\S+)");
276 if (std::smatch match; std::regex_match(param_name, match, reg))
return match[1].str();
281 const std::map<std::string, rclcpp::ParameterValue> & value_map,
bool simulate)
283 std::map<std::string, std::string> set_successfully_map;
284 for (
const auto & [enum_key, pval] : value_map) {
286 switch (pval.get_type()) {
287 case rclcpp::ParameterType::PARAMETER_BOOL:
288 if (simulate)
continue;
289 scripting_enums_[enum_key] =
static_cast<int>(pval.get<
bool>());
291 case rclcpp::ParameterType::PARAMETER_INTEGER:
292 if (simulate)
continue;
293 scripting_enums_[enum_key] =
static_cast<int>(pval.get<
int>());
296 if (simulate)
return false;
297 throw exceptions::ParameterConversionError(
"Parameter to scripting enum conversion is not allowed.");
299 set_successfully_map[enum_key] = rclcpp::to_string(pval);
300 }
catch (
const std::exception & e) {
302 logger_,
"Error setting scripting enum from parameter %s=%s (Type: %s): %s", enum_key.c_str(),
303 rclcpp::to_string(pval).c_str(), rclcpp::to_string(pval.get_type()).c_str(), e.what());
307 if (!set_successfully_map.empty()) {
309 logger_,
"Updated scripting enums from parameters: { %s }",
316 const std::map<std::string, rclcpp::ParameterValue> & value_map,
bool simulate)
319 std::map<std::string, std::string> set_successfully_map;
320 for (
const auto & [entry_key, pval] : value_map) {
323 BT::Any any(expected.value());
325 if (
const BT::TypeInfo * entry_info = bb.entryInfo(entry_key)) {
326 if (entry_info->isStronglyTyped() && entry_info->type() != any.type())
return false;
330 bb.set(entry_key, any);
333 throw exceptions::ParameterConversionError(expected.error());
335 translated_global_blackboard_entries_[entry_key] = pval;
336 set_successfully_map[entry_key] = rclcpp::to_string(pval);
337 }
catch (
const std::exception & e) {
339 logger_,
"Error updating blackboard from parameter %s=%s (Type: %s): %s", entry_key.c_str(),
340 rclcpp::to_string(pval).c_str(), rclcpp::to_string(pval.get_type()).c_str(), e.what());
344 if (!set_successfully_map.empty()) {
353 if (build_handler_ptr_ && !executor_param_listener_.get_params().allow_other_build_handlers) {
354 throw std::logic_error(
355 "Executor option 'Allow other build handlers' is disabled, but loadBuildHandler() was called again after "
357 current_build_handler_name_ +
"'.");
359 if (current_build_handler_name_ == name)
return;
361 build_handler_ptr_.reset();
365 build_handler_loader_ptr_->createUniqueInstance(name)->makeUnique(
node_ptr_, tree_node_loader_ptr_);
366 }
catch (
const pluginlib::CreateClassException & e) {
367 throw exceptions::TreeExecutorError(
368 "An error occurred when trying to create an instance of tree build handler class '" + name +
369 "'. This might be because you forgot to call the AUTO_APMS_BEHAVIOR_TREE_REGISTER_BUILD_HANDLER macro "
370 "in the source file: " +
372 }
catch (
const std::exception & e) {
373 throw exceptions::TreeExecutorError(
374 "An error occurred when trying to create an instance of tree build handler class '" + name +
"': " + e.what());
377 current_build_handler_name_ = name;
381 const std::string & build_request,
const std::string & entry_point,
const core::NodeManifest & node_manifest)
384 if (build_handler_ptr_ && !build_handler_ptr_->setBuildRequest(build_request, entry_point, node_manifest)) {
385 throw exceptions::TreeBuildError(
386 "Build request '" + build_request +
"' was denied by '" + current_build_handler_name_ +
387 "' (setBuildRequest() returned false).");
390 return [
this, build_request, entry_point, node_manifest](TreeBlackboardSharedPtr bb_ptr) {
396 this->tree_node_loader_ptr_));
399 this->
preBuild(*this->builder_ptr_, build_request, entry_point, node_manifest, *bb_ptr);
402 for (
const auto & [enum_key, val] : this->scripting_enums_) this->builder_ptr_->setScriptingEnum(enum_key, val);
405 std::string instantiate_name =
"";
406 if (this->build_handler_ptr_) {
407 instantiate_name = this->build_handler_ptr_->buildTree(*this->builder_ptr_, *bb_ptr).getName();
411 Tree tree = instantiate_name.empty() ? this->builder_ptr_->instantiate(bb_ptr)
412 : this->builder_ptr_->instantiate(instantiate_name, bb_ptr);
422 return core::TreeBuilder::make_shared(
424 this->tree_node_loader_ptr_);
430 if (executor_options_.blackboard_parameters_from_overrides_ || executor_options_.blackboard_parameters_dynamic_) {
431 const auto res =
node_ptr_->list_parameters({BLACKBOARD_PARAM_PREFIX}, 2);
432 for (
const std::string & name : res.names) {
441rcl_interfaces::msg::SetParametersResult GenericTreeExecutorNode::on_set_parameters_callback_(
442 const std::vector<rclcpp::Parameter> & parameters)
444 const ExecutorParameters params = executor_param_listener_.get_params();
446 for (
const rclcpp::Parameter & p : parameters) {
447 auto create_rejected = [&p](
const std::string msg) {
448 rcl_interfaces::msg::SetParametersResult result;
449 result.successful =
false;
450 result.reason =
"Rejected to set " + p.get_name() +
" = " + p.value_to_string() +
" (Type: " + p.get_type_name() +
454 const std::string param_name = p.get_name();
460 return create_rejected(
"Scripting enums cannot change while tree executor is running");
462 if (!executor_options_.scripting_enum_parameters_dynamic_ || !params.allow_dynamic_scripting_enums) {
463 return create_rejected(
464 "Cannot set scripting enum '" + enum_key +
"', because the 'Dynamic scripting enums' option is disabled");
467 return create_rejected(
468 "Type of scripting enum must be bool or int. Tried to set enum '" + enum_key +
"' with value '" +
469 p.value_to_string() +
"' (Type: " + p.get_type_name() +
")");
476 !entry_key.empty()) {
477 if (!executor_options_.blackboard_parameters_dynamic_ || !params.allow_dynamic_blackboard) {
478 return create_rejected(
479 "Cannot set blackboard entry '" + entry_key +
"', because the 'Dynamic blackboard' option is disabled");
482 return create_rejected(
483 "Type of blackboard entries must not change. Tried to set entry '" + entry_key +
485 p.value_to_string() +
"' (Type: " + p.get_type_name() +
")");
496 if (!executor_options_.strict_unkown_parameter_removal_)
continue;
497 return create_rejected(
"Parameter is unknown");
502 return create_rejected(
"Parameter is not allowed to change while tree executor is running");
506 if (param_name == _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_BUILD_HANDLER) {
507 if (!params.allow_other_build_handlers) {
508 return create_rejected(
509 "This executor operates with tree build handler '" + executor_param_listener_.get_params().build_handler +
510 "' and doesn't allow other build handlers to be loaded since the 'Allow other build handlers' option is "
513 const std::string class_name = p.as_string();
515 return create_rejected(
516 "Cannot load build handler '" + class_name +
517 "' because no corresponding ament_index resource was found. Make sure that you spelled the build handler's "
519 "and registered it by calling auto_apms_behavior_tree_register_build_handlers() in the CMakeLists.txt of "
521 "corresponding package");
526 if (!
node_ptr_->has_parameter(param_name)) {
527 return create_rejected(
"Parameter '" + param_name +
"' is not supported");
531 rcl_interfaces::msg::SetParametersResult result;
532 result.successful =
true;
536void GenericTreeExecutorNode::parameter_event_callback_(
const rcl_interfaces::msg::ParameterEvent & event)
538 std::regex re(
node_ptr_->get_fully_qualified_name());
539 if (std::regex_match(event.node, re)) {
540 for (
const rclcpp::Parameter & p : rclcpp::ParameterEventHandler::get_parameters_from_event(event)) {
541 const std::string param_name = p.get_name();
549 !entry_key.empty()) {
553 if (param_name == _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_BUILD_HANDLER) {
560rclcpp_action::GoalResponse GenericTreeExecutorNode::handle_command_goal_(
561 const rclcpp_action::GoalUUID & , std::shared_ptr<const CommandActionContext::Goal> goal_ptr)
563 if (command_timer_ptr_ && !command_timer_ptr_->is_canceled()) {
564 RCLCPP_WARN(
logger_,
"Request for setting tree executor command rejected, because previous one is still busy.");
565 return rclcpp_action::GoalResponse::REJECT;
569 switch (goal_ptr->command) {
570 case CommandActionContext::Goal::COMMAND_RESUME:
575 logger_,
"Requested to RESUME with executor being in state %s. Rejecting request.",
576 toStr(execution_state).c_str());
577 return rclcpp_action::GoalResponse::REJECT;
580 case CommandActionContext::Goal::COMMAND_PAUSE:
585 logger_,
"Requested to PAUSE with executor already being inactive (State: %s).",
586 toStr(execution_state).c_str());
589 case CommandActionContext::Goal::COMMAND_HALT:
596 logger_,
"Requested to HALT with executor already being inactive (State: %s).",
597 toStr(execution_state).c_str());
600 case CommandActionContext::Goal::COMMAND_TERMINATE:
605 logger_,
"Requested to TERMINATE with executor already being inactive (State: %s).",
606 toStr(execution_state).c_str());
610 RCLCPP_WARN(
logger_,
"Executor command %i is undefined. Rejecting request.", goal_ptr->command);
611 return rclcpp_action::GoalResponse::REJECT;
613 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
616rclcpp_action::CancelResponse GenericTreeExecutorNode::handle_command_cancel_(
617 std::shared_ptr<CommandActionContext::GoalHandle> )
619 return rclcpp_action::CancelResponse::ACCEPT;
622void GenericTreeExecutorNode::handle_command_accept_(std::shared_ptr<CommandActionContext::GoalHandle> goal_handle_ptr)
624 const auto command_request = goal_handle_ptr->get_goal()->command;
626 switch (command_request) {
627 case CommandActionContext::Goal::COMMAND_RESUME:
631 case CommandActionContext::Goal::COMMAND_PAUSE:
635 case CommandActionContext::Goal::COMMAND_HALT:
639 case CommandActionContext::Goal::COMMAND_TERMINATE:
644 throw std::logic_error(
"command_request is unknown");
647 command_timer_ptr_ =
node_ptr_->create_wall_timer(
648 std::chrono::duration<double>(executor_param_listener_.get_params().tick_rate),
649 [
this, requested_state, goal_handle_ptr, action_result_ptr = std::make_shared<CommandActionContext::Result>()]() {
650 if (goal_handle_ptr->is_canceling()) {
651 goal_handle_ptr->canceled(action_result_ptr);
652 command_timer_ptr_->cancel();
660 logger_,
"Failed to reach requested state %s due to cancellation of execution timer. Aborting.",
661 toStr(requested_state).c_str());
662 goal_handle_ptr->abort(action_result_ptr);
663 command_timer_ptr_->cancel();
667 if (current_state != requested_state)
return;
669 goal_handle_ptr->succeed(action_result_ptr);
670 command_timer_ptr_->cancel();
bool updateGlobalBlackboardWithParameterValues(const std::map< std::string, rclcpp::ParameterValue > &value_map, bool simulate=false)
Update the global blackboard using parameter values.
static const std::string PARAM_VALUE_NO_BUILD_HANDLER
Value indicating that no build handler is loaded.
static std::string stripPrefixFromParameterName(const std::string &prefix, const std::string ¶m_name)
Get the name of a parameter without its prefix.
GenericTreeExecutorNode(rclcpp::Node::SharedPtr node_ptr, Options options)
Constructor using an existing ROS 2 node.
virtual bool clearGlobalBlackboard() override
Reset the global blackboard and clear all entries.
core::TreeBuilder::SharedPtr createTreeBuilder()
Create a tree builder for building the behavior tree.
std::map< std::string, rclcpp::ParameterValue > getParameterValuesWithPrefix(const std::string &prefix)
Assemble all parameters of this node that have a specific prefix.
bool updateScriptingEnumsWithParameterValues(const std::map< std::string, rclcpp::ParameterValue > &value_map, bool simulate=false)
Update the internal buffer of scripting enums.
ExecutorParameters getExecutorParameters() const
Get a copy of the current executor parameters.
std::shared_future< ExecutionResult > startExecution(const std::string &build_request, const std::string &entry_point="", const core::NodeManifest &node_manifest={})
Start the behavior tree specified by a particular build request.
virtual void postBuild(Tree &tree)
Callback invoked after the behavior tree has been instantiated.
virtual void preBuild(core::TreeBuilder &builder, const std::string &build_request, const std::string &entry_point, const core::NodeManifest &node_manifest, TreeBlackboard &bb)
Callback invoked before building the behavior tree.
void loadBuildHandler(const std::string &name)
Load a particular behavior tree build handler plugin.
TreeConstructor makeTreeConstructor(const std::string &build_request, const std::string &entry_point="", const core::NodeManifest &node_manifest={})
Create a callback that builds a behavior tree according to a specific request.
ExecutionState
Enum representing possible behavior tree execution states.
@ STARTING
Initializing execution.
@ RUNNING
Executor is busy and tree has been ticked at least once.
@ PAUSED
Execution routine is active, but tree is not being ticked.
@ HALTED
Execution routine is active, but tree is not being ticked and has been halted before.
ExecutionState getExecutionState()
Get a status code indicating the current state of execution.
bool isBusy()
Determine whether this executor is currently executing a behavior tree.
rclcpp::Node::SharedPtr node_ptr_
Shared pointer to the parent ROS 2 node.
rclcpp::executors::SingleThreadedExecutor::SharedPtr getTreeNodeWaitablesExecutorPtr()
Get the ROS 2 executor instance used for spinning waitables registered by behavior tree nodes.
rclcpp::CallbackGroup::SharedPtr getTreeNodeWaitablesCallbackGroupPtr()
Get the callback group used for all waitables registered by behavior tree nodes.
void setControlCommand(ControlCommand cmd)
Set the command that handles the control flow of the execution routine.
virtual bool clearGlobalBlackboard()
Reset the global blackboard and clear all entries.
@ TERMINATE
Halt the currently executing tree and terminate the execution routine.
@ PAUSE
Pause the execution routine.
@ RUN
Start/Resume the execution routine.
@ HALT
Halt the currently executing tree and pause the execution routine.
TreeStateObserver & getStateObserver()
Get a reference to the current behavior tree state observer.
TreeExecutorBase(rclcpp::Node::SharedPtr node_ptr, rclcpp::CallbackGroup::SharedPtr tree_node_callback_group_ptr=nullptr)
Constructor.
TreeBlackboardSharedPtr getGlobalBlackboardPtr()
Get a shared pointer to the global blackboard instance.
std::string getTreeName()
Get the name of the tree that is currently executing.
const rclcpp::Logger logger_
Logger associated with the parent ROS 2 node.
void setLogging(bool active)
Configure whether the observer should write to the logger.
Data structure for information about which behavior tree node plugin to load and how to configure the...
Class for configuring and instantiating behavior trees.
bool contains(const ContainerT< ValueT, AllocatorT > &c, const ValueT &val)
Check whether a particular container structure contains a value.
std::string printMap(const std::map< std::string, std::string > &map, const std::string &key_val_sep="=", const std::string &entry_sep=", ")
Converts a map to a string representation that is suited for printing to console.
const char * toStr(const ActionNodeErrorCode &err)
Convert the action error code to string.
Powerful tooling for incorporating behavior trees for task development.
BT::Expected< BT::Any > createAnyFromParameterValue(const rclcpp::ParameterValue &val)
Convert a ROS 2 parameter value to a BT::Any object.
BT::Expected< rclcpp::ParameterValue > createParameterValueFromAny(const BT::Any &any, rclcpp::ParameterType type)
Convert a BT::Any object to a ROS 2 parameter value.