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"
32:
TreeExecutorBase(std::make_shared<rclcpp::Node>(name, options.getROSNodeOptions())),
33 executor_options_(options),
37 rcl_interfaces::msg::ListParametersResult res =
node_ptr_->list_parameters({}, 0);
38 std::vector<std::string> unknown_param_names;
39 for (
const std::string & param_name : res.names) {
40 if (!stripPrefixFromParameterName(SCRIPTING_ENUM_PARAM_PREFIX, param_name).empty()) continue;
41 if (!stripPrefixFromParameterName(BLACKBOARD_PARAM_PREFIX, param_name).empty()) continue;
42 if (auto_apms_util::contains(TREE_EXECUTOR_EXPLICITLY_ALLOWED_PARAMETERS, param_name)) continue;
44 node_ptr_->undeclare_parameter(param_name);
45 } catch (
const rclcpp::exceptions::ParameterImmutableException & e) {
48 }
catch (
const rclcpp::exceptions::InvalidParameterTypeException & e) {
52 unknown_param_names.push_back(param_name);
54 if (!unknown_param_names.empty()) {
56 logger_,
"The following initial parameters are not supported and have been removed: [ %s ].",
57 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); });
186 const std::string & build_request,
const std::string & entry_point,
const core::NodeManifest & node_manifest)
188 const ExecutorParameters params = executor_param_listener_.get_params();
190 makeTreeConstructor(build_request, entry_point, node_manifest), params.tick_rate, params.groot2_port);
195 return executor_param_listener_.get_params();
199 const std::string & prefix)
201 const auto res =
node_ptr_->list_parameters({prefix}, 2);
202 std::map<std::string, rclcpp::ParameterValue> value_map;
203 for (
const std::string & name_with_prefix : res.names) {
205 value_map[suffix] =
node_ptr_->get_parameter(name_with_prefix).get_parameter_value();
212 const std::string & prefix,
const std::string & param_name)
214 const std::regex reg(
"^" + prefix +
"\\.(\\S+)");
215 if (std::smatch match; std::regex_match(param_name, match, reg))
return match[1].str();
220 const std::map<std::string, rclcpp::ParameterValue> & value_map,
bool simulate)
222 std::map<std::string, std::string> set_successfully_map;
223 for (
const auto & [enum_key, pval] : value_map) {
225 switch (pval.get_type()) {
226 case rclcpp::ParameterType::PARAMETER_BOOL:
227 if (simulate)
continue;
228 scripting_enums_[enum_key] =
static_cast<int>(pval.get<
bool>());
230 case rclcpp::ParameterType::PARAMETER_INTEGER:
231 if (simulate)
continue;
232 scripting_enums_[enum_key] =
static_cast<int>(pval.get<
int>());
235 if (simulate)
return false;
236 throw exceptions::ParameterConversionError(
"Parameter to scripting enum conversion is not allowed.");
238 set_successfully_map[enum_key] = rclcpp::to_string(pval);
239 }
catch (
const std::exception & e) {
241 logger_,
"Error setting scripting enum from parameter %s=%s (Type: %s): %s", enum_key.c_str(),
242 rclcpp::to_string(pval).c_str(), rclcpp::to_string(pval.get_type()).c_str(), e.what());
246 if (!set_successfully_map.empty()) {
248 logger_,
"Updated scripting enums from parameters: { %s }",
255 const std::map<std::string, rclcpp::ParameterValue> & value_map,
bool simulate)
258 std::map<std::string, std::string> set_successfully_map;
259 for (
const auto & [entry_key, pval] : value_map) {
262 BT::Any any(expected.value());
264 if (
const BT::TypeInfo * entry_info = bb.entryInfo(entry_key)) {
265 if (entry_info->isStronglyTyped() && entry_info->type() != any.type())
return false;
269 bb.set(entry_key, any);
272 throw exceptions::ParameterConversionError(expected.error());
274 translated_global_blackboard_entries_[entry_key] = pval;
275 set_successfully_map[entry_key] = rclcpp::to_string(pval);
276 }
catch (
const std::exception & e) {
278 logger_,
"Error updating blackboard from parameter %s=%s (Type: %s): %s", entry_key.c_str(),
279 rclcpp::to_string(pval).c_str(), rclcpp::to_string(pval.get_type()).c_str(), e.what());
283 if (!set_successfully_map.empty()) {
292 if (build_handler_ptr_ && !executor_param_listener_.get_params().allow_other_build_handlers) {
293 throw std::logic_error(
294 "Executor option 'Allow other build handlers' is disabled, but loadBuildHandler() was called again after "
296 current_build_handler_name_ +
"'.");
298 if (current_build_handler_name_ == name)
return;
300 build_handler_ptr_.reset();
304 build_handler_loader_ptr_->createUniqueInstance(name)->makeUnique(
node_ptr_, tree_node_loader_ptr_);
305 }
catch (
const pluginlib::CreateClassException & e) {
306 throw exceptions::TreeExecutorError(
307 "An error occurred when trying to create an instance of tree build handler class '" + name +
308 "'. This might be because you forgot to call the AUTO_APMS_BEHAVIOR_TREE_REGISTER_BUILD_HANDLER macro "
309 "in the source file: " +
311 }
catch (
const std::exception & e) {
312 throw exceptions::TreeExecutorError(
313 "An error occurred when trying to create an instance of tree build handler class '" + name +
"': " + e.what());
316 current_build_handler_name_ = name;
320 const std::string & build_request,
const std::string & entry_point,
const core::NodeManifest & node_manifest)
323 if (build_handler_ptr_ && !build_handler_ptr_->setBuildRequest(build_request, entry_point, node_manifest)) {
324 throw exceptions::TreeBuildError(
325 "Build request '" + build_request +
"' was denied by '" + current_build_handler_name_ +
326 "' (setBuildRequest() returned false).");
329 return [
this, build_request, entry_point, node_manifest](TreeBlackboardSharedPtr bb_ptr) {
335 this->tree_node_loader_ptr_));
338 this->
preBuild(*this->builder_ptr_, build_request, entry_point, node_manifest, *bb_ptr);
341 for (
const auto & [enum_key, val] : this->scripting_enums_) this->builder_ptr_->setScriptingEnum(enum_key, val);
344 std::string instantiate_name =
"";
345 if (this->build_handler_ptr_) {
346 instantiate_name = this->build_handler_ptr_->buildTree(*this->builder_ptr_, *bb_ptr).getName();
350 Tree tree = instantiate_name.empty() ? this->builder_ptr_->instantiate(bb_ptr)
351 : this->builder_ptr_->instantiate(instantiate_name, bb_ptr);
361 return core::TreeBuilder::make_shared(
363 this->tree_node_loader_ptr_);
369 if (executor_options_.blackboard_parameters_from_overrides_ || executor_options_.blackboard_parameters_dynamic_) {
370 const auto res =
node_ptr_->list_parameters({BLACKBOARD_PARAM_PREFIX}, 2);
371 for (
const std::string & name : res.names) {
380rcl_interfaces::msg::SetParametersResult GenericTreeExecutorNode::on_set_parameters_callback_(
381 const std::vector<rclcpp::Parameter> & parameters)
383 const ExecutorParameters params = executor_param_listener_.get_params();
385 for (
const rclcpp::Parameter & p : parameters) {
386 auto create_rejected = [&p](
const std::string msg) {
387 rcl_interfaces::msg::SetParametersResult result;
388 result.successful =
false;
389 result.reason =
"Rejected to set " + p.get_name() +
" = " + p.value_to_string() +
" (Type: " + p.get_type_name() +
393 const std::string param_name = p.get_name();
399 return create_rejected(
"Scripting enums cannot change while tree executor is running");
401 if (!executor_options_.scripting_enum_parameters_dynamic_ || !params.allow_dynamic_scripting_enums) {
402 return create_rejected(
403 "Cannot set scripting enum '" + enum_key +
"', because the 'Dynamic scripting enums' option is disabled");
406 return create_rejected(
407 "Type of scripting enum must be bool or int. Tried to set enum '" + enum_key +
"' with value '" +
408 p.value_to_string() +
"' (Type: " + p.get_type_name() +
")");
415 !entry_key.empty()) {
416 if (!executor_options_.blackboard_parameters_dynamic_ || !params.allow_dynamic_blackboard) {
417 return create_rejected(
418 "Cannot set blackboard entry '" + entry_key +
"', because the 'Dynamic blackboard' option is disabled");
421 return create_rejected(
422 "Type of blackboard entries must not change. Tried to set entry '" + entry_key +
424 p.value_to_string() +
"' (Type: " + p.get_type_name() +
")");
431 return create_rejected(
"Parameter is unknown");
436 return create_rejected(
"Parameter is not allowed to change while tree executor is running");
440 if (param_name == _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_BUILD_HANDLER) {
441 if (!params.allow_other_build_handlers) {
442 return create_rejected(
443 "This executor operates with tree build handler '" + executor_param_listener_.get_params().build_handler +
444 "' and doesn't allow other build handlers to be loaded since the 'Allow other build handlers' option is "
447 const std::string class_name = p.as_string();
449 return create_rejected(
450 "Cannot load build handler '" + class_name +
451 "' because no corresponding ament_index resource was found. Make sure that you spelled the build handler's "
453 "and registered it by calling auto_apms_behavior_tree_register_build_handlers() in the CMakeLists.txt of "
455 "corresponding package");
460 if (!
node_ptr_->has_parameter(param_name)) {
461 return create_rejected(
"Parameter '" + param_name +
"' is not supported");
465 rcl_interfaces::msg::SetParametersResult result;
466 result.successful =
true;
470void GenericTreeExecutorNode::parameter_event_callback_(
const rcl_interfaces::msg::ParameterEvent & event)
472 std::regex re(
node_ptr_->get_fully_qualified_name());
473 if (std::regex_match(event.node, re)) {
474 for (
const rclcpp::Parameter & p : rclcpp::ParameterEventHandler::get_parameters_from_event(event)) {
475 const std::string param_name = p.get_name();
483 !entry_key.empty()) {
487 if (param_name == _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_BUILD_HANDLER) {
494rclcpp_action::GoalResponse GenericTreeExecutorNode::handle_command_goal_(
495 const rclcpp_action::GoalUUID & , std::shared_ptr<const CommandActionContext::Goal> goal_ptr)
497 if (command_timer_ptr_ && !command_timer_ptr_->is_canceled()) {
498 RCLCPP_WARN(
logger_,
"Request for setting tree executor command rejected, because previous one is still busy.");
499 return rclcpp_action::GoalResponse::REJECT;
503 switch (goal_ptr->command) {
504 case CommandActionContext::Goal::COMMAND_RESUME:
509 logger_,
"Requested to RESUME with executor being in state %s. Rejecting request.",
510 toStr(execution_state).c_str());
511 return rclcpp_action::GoalResponse::REJECT;
514 case CommandActionContext::Goal::COMMAND_PAUSE:
519 logger_,
"Requested to PAUSE with executor already being inactive (State: %s).",
520 toStr(execution_state).c_str());
523 case CommandActionContext::Goal::COMMAND_HALT:
530 logger_,
"Requested to HALT with executor already being inactive (State: %s).",
531 toStr(execution_state).c_str());
534 case CommandActionContext::Goal::COMMAND_TERMINATE:
539 logger_,
"Requested to TERMINATE with executor already being inactive (State: %s).",
540 toStr(execution_state).c_str());
544 RCLCPP_WARN(
logger_,
"Executor command %i is undefined. Rejecting request.", goal_ptr->command);
545 return rclcpp_action::GoalResponse::REJECT;
547 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
550rclcpp_action::CancelResponse GenericTreeExecutorNode::handle_command_cancel_(
551 std::shared_ptr<CommandActionContext::GoalHandle> )
553 return rclcpp_action::CancelResponse::ACCEPT;
556void GenericTreeExecutorNode::handle_command_accept_(std::shared_ptr<CommandActionContext::GoalHandle> goal_handle_ptr)
558 const auto command_request = goal_handle_ptr->get_goal()->command;
560 switch (command_request) {
561 case CommandActionContext::Goal::COMMAND_RESUME:
565 case CommandActionContext::Goal::COMMAND_PAUSE:
569 case CommandActionContext::Goal::COMMAND_HALT:
573 case CommandActionContext::Goal::COMMAND_TERMINATE:
578 throw std::logic_error(
"command_request is unknown");
581 command_timer_ptr_ =
node_ptr_->create_wall_timer(
582 std::chrono::duration<double>(executor_param_listener_.get_params().tick_rate),
583 [
this, requested_state, goal_handle_ptr, action_result_ptr = std::make_shared<CommandActionContext::Result>()]() {
584 if (goal_handle_ptr->is_canceling()) {
585 goal_handle_ptr->canceled(action_result_ptr);
586 command_timer_ptr_->cancel();
594 logger_,
"Failed to reach requested state %s due to cancellation of execution timer. Aborting.",
595 toStr(requested_state).c_str());
596 goal_handle_ptr->abort(action_result_ptr);
597 command_timer_ptr_->cancel();
601 if (current_state != requested_state)
return;
603 goal_handle_ptr->succeed(action_result_ptr);
604 command_timer_ptr_->cancel();
608bool GenericTreeExecutorNode::onTick()
610 const ExecutorParameters params = executor_param_listener_.get_params();
615bool GenericTreeExecutorNode::afterTick()
617 const ExecutorParameters params = executor_param_listener_.get_params();
620 if (executor_options_.blackboard_parameters_dynamic_ && params.allow_dynamic_blackboard) {
622 std::vector<rclcpp::Parameter> new_parameters;
623 for (
const BT::StringView & str : bb_ptr->getKeys()) {
624 const std::string key = std::string(str);
625 const BT::TypeInfo * type_info = bb_ptr->entryInfo(key);
626 const BT::Any * any = bb_ptr->getAnyLocked(key).get();
628 if (any->empty())
continue;
630 if (translated_global_blackboard_entries_.find(key) == translated_global_blackboard_entries_.end()) {
631 const BT::Expected<rclcpp::ParameterValue> expected =
634 new_parameters.push_back(rclcpp::Parameter(BLACKBOARD_PARAM_PREFIX +
"." + key, expected.value()));
635 translated_global_blackboard_entries_[key] = expected.value();
638 logger_,
"Failed to translate new blackboard entry '%s' (Type: %s) to parameters: %s", key.c_str(),
639 type_info->typeName().c_str(), expected.error().c_str());
642 const BT::Expected<rclcpp::ParameterValue> expected =
645 if (expected.value() != translated_global_blackboard_entries_[key]) {
646 new_parameters.push_back(rclcpp::Parameter(BLACKBOARD_PARAM_PREFIX +
"." + key, expected.value()));
650 logger_,
"Failed to translate blackboard entry '%s' (Type: %s) to parameters: %s", key.c_str(),
651 type_info->typeName().c_str(), expected.error().c_str());
655 if (!new_parameters.empty()) {
656 const rcl_interfaces::msg::SetParametersResult result =
node_ptr_->set_parameters_atomically(new_parameters);
657 if (!result.successful) {
658 throw exceptions::TreeExecutorError(
659 "Unexpectedly failed to set parameters inferred from global blackboard. Reason: " + result.reason);
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.
GenericTreeExecutorNode(const std::string &name, Options options)
Constructor.
static std::string stripPrefixFromParameterName(const std::string &prefix, const std::string ¶m_name)
Get the name of a parameter without its prefix.
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.