15#include "auto_apms_px4/behavior_mode_executor.hpp"
21#include "auto_apms_behavior_tree/executor/options.hpp"
22#include "auto_apms_behavior_tree_core/node/node_manifest.hpp"
23#include "auto_apms_px4/behavior_mode_executor_params.hpp"
24#include "px4_msgs/msg/vehicle_command.hpp"
33BehaviorOwnedMode::BehaviorOwnedMode(rclcpp::Node & node,
const px4_ros2::ModeBase::Settings & settings)
34: px4_ros2::ModeBase(node, settings)
36 actuator_setpoint_ptr_ = std::make_shared<px4_ros2::DirectActuatorsSetpointType>(*
this);
39void BehaviorOwnedMode::updateSetpoint(
float )
51 BehaviorOwnedMode & owned_mode,
const px4_ros2::ModeExecutorBase::Settings & settings,
53: px4_ros2::ModeExecutorBase(settings, owned_mode),
54 node_(owned_mode.node()),
55 owned_mode_(owned_mode),
56 behavior_executor_(engine),
57 config_provider_(std::move(config_provider)),
58 config_(config_provider_())
64 RCLCPP_INFO(node_.get_logger(),
"Behavior execution result: %s.", auto_apms_behavior_tree::toStr(result).c_str());
70 result == ExecutionResult::TREE_SUCCEEDED ? config_.on_completion : config_.on_failure;
71 performReaction(reaction, result);
73 RCLCPP_INFO(node_.get_logger(),
"No longer in charge. Skipping completion reaction");
81 }
catch (
const std::exception & e) {
82 RCLCPP_ERROR(node_.get_logger(),
"Failed to prepare behavior tree for the next activation: %s", e.what());
94 throw std::invalid_argument(
95 "Invalid completion reaction '" + str +
"' (expected one of: hold, rtl, land, disarm, complete, none)");
98void BehaviorModeExecutor::onActivate()
103 config_ = config_provider_();
105 if (!prepared_tree_ptr_) {
106 RCLCPP_ERROR(node_.get_logger(),
"Cannot start behavior: no pre-built behavior tree is available");
112 node_.get_logger(),
"Behavior executor put in charge. Starting behavior '%s'", config_.spec.
build_request.c_str());
114 if (config_.defer_failsafes) {
115 if (deferFailsafesSync(
true)) {
116 RCLCPP_INFO(node_.get_logger(),
"Failsafes are now being deferred while the behavior is running");
118 RCLCPP_WARN(node_.get_logger(),
"Failed to enable failsafe deferral");
127 const int source_component =
static_cast<int>(px4_msgs::msg::VehicleCommand::COMPONENT_MODE_EXECUTOR_START) +
id();
128 behavior_executor_.getGlobalBlackboardPtr()->set(AUTO_APMS_PX4_SOURCE_COMPONENT_GLOBAL_KEY, source_component);
132 const auto executor_params = behavior_executor_.getExecutorParameters();
134 behavior_executor_.startExecution(
135 std::move(prepared_tree_ptr_), executor_params.tick_rate, executor_params.groot2_port);
136 }
catch (
const std::exception & e) {
137 RCLCPP_ERROR(node_.get_logger(),
"Failed to start behavior: %s", e.what());
142void BehaviorModeExecutor::onDeactivate(DeactivateReason reason)
144 const char * reason_str = reason == DeactivateReason::FailsafeActivated ?
"failsafe activated" :
"other";
145 RCLCPP_INFO(node_.get_logger(),
"Behavior executor deactivating (reason: %s)", reason_str);
148 if (behavior_executor_.isBusy()) {
149 RCLCPP_INFO(node_.get_logger(),
"Behavior is still running. Terminating it now...");
153 if (config_.defer_failsafes) {
154 deferFailsafesSync(
false);
160 const Config config = config_provider_();
162 throw std::invalid_argument(
"Cannot prepare behavior tree: parameter 'behavior.build_request' must not be empty");
169 behavior_executor_.getGlobalBlackboardPtr()->set(AUTO_APMS_PX4_MODE_EXECUTOR_ACTIVE_GLOBAL_KEY,
true);
173 node_manifest = auto_apms_behavior_tree::core::NodeManifest::decode(config.spec.
node_manifest);
180 const auto_apms_behavior_tree::TreeConstructor make_tree =
182 const auto_apms_behavior_tree::TreeBlackboardSharedPtr main_tree_bb_ptr =
183 auto_apms_behavior_tree::TreeBlackboard::create(behavior_executor_.getGlobalBlackboardPtr());
184 prepared_tree_ptr_ = std::make_unique<auto_apms_behavior_tree::Tree>(make_tree(main_tree_bb_ptr));
187 node_.get_logger(),
"Behavior tree '%s' built and ready for activation", config.spec.
build_request.c_str());
190void BehaviorModeExecutor::performReaction(CompletionReaction reaction, ExecutionResult result)
192 px4_ros2::Result px4_result = px4_ros2::Result::Success;
194 case ExecutionResult::TREE_SUCCEEDED:
195 px4_result = px4_ros2::Result::Success;
197 case ExecutionResult::TERMINATED_PREMATURELY:
198 px4_result = px4_ros2::Result::Deactivated;
200 case ExecutionResult::TREE_FAILED:
201 case ExecutionResult::ERROR:
203 px4_result = px4_ros2::Result::ModeFailureOther;
207 const auto log_done = [
this](px4_ros2::Result r) {
208 RCLCPP_INFO(node_.get_logger(),
"Completion reaction finished (%s)", px4_ros2::resultToString(r));
213 RCLCPP_INFO(node_.get_logger(),
"Completion reaction: HOLD");
217 RCLCPP_INFO(node_.get_logger(),
"Completion reaction: RTL");
221 RCLCPP_INFO(node_.get_logger(),
"Completion reaction: LAND");
225 RCLCPP_INFO(node_.get_logger(),
"Completion reaction: DISARM");
229 RCLCPP_INFO(node_.get_logger(),
"Completion reaction: COMPLETE (reporting owned mode completion)");
230 owned_mode_.finish(px4_result);
233 RCLCPP_INFO(node_.get_logger(),
"Completion reaction: NONE");
238void BehaviorModeExecutor::scheduleLoiter()
240 scheduleMode(px4_ros2::ModeBase::kModeIDLoiter, [](px4_ros2::Result) {
249px4_ros2::ModeExecutorBase::Settings::Activation BehaviorModeExecutorNode::activationFromString(
const std::string & str)
251 using Activation = px4_ros2::ModeExecutorBase::Settings::Activation;
252 if (str ==
"armed")
return Activation::ActivateOnlyWhenArmed;
253 if (str ==
"always")
return Activation::ActivateAlways;
254 if (str ==
"immediately")
return Activation::ActivateImmediately;
255 throw std::invalid_argument(
"Invalid activation '" + str +
"' (expected one of: armed, always, immediately)");
258BehaviorModeExecutorNode::BehaviorModeExecutorNode(
const rclcpp::NodeOptions & options)
259: GenericTreeExecutorNode(
260 "behavior_mode_executor",
261 auto_apms_behavior_tree::TreeExecutorNodeOptions(options).enableStrictUnkownParameterRemoval(false)),
262 registration_handler_(getNodePtr())
270 const auto param_listener_ptr = std::make_shared<behavior_mode_executor_params::ParamListener>(getNodePtr());
271 const behavior_mode_executor_params::Params params = param_listener_ptr->get_params();
273 if (params.behavior.build_request.empty()) {
274 throw std::invalid_argument(
"Parameter 'behavior.build_request' must not be empty.");
280 auto config_provider = [param_listener_ptr]() -> BehaviorModeExecutor::Config {
281 const behavior_mode_executor_params::Params p = param_listener_ptr->get_params();
282 BehaviorModeExecutor::Config config;
283 config.spec.build_request = p.behavior.build_request;
284 config.spec.entry_point = p.behavior.entry_point;
285 config.spec.node_manifest = p.behavior.node_manifest;
288 config.defer_failsafes = p.defer_failsafes;
292 const px4_ros2::ModeExecutorBase::Settings settings{activationFromString(params.activation)};
294 owned_mode_ptr_ = std::make_unique<BehaviorOwnedMode>(*getNodePtr(), px4_ros2::ModeBase::Settings{params.mode_name});
296 executor_ptr_ = std::make_unique<BehaviorModeExecutor>(*owned_mode_ptr_, settings, std::move(config_provider), *
this);
301 executor_ptr_->prepareTree();
304 registration_handler_.registerMode(*executor_ptr_, params.mode_name);
307void BehaviorModeExecutorNode::onTermination(
const ExecutionResult & result)
309 executor_ptr_->onExecutionResult(result);
314#include "rclcpp_components/register_node_macro.hpp"
Flexible and configurable ROS 2 behavior tree executor node.
@ TERMINATE
Halt the currently executing tree and terminate the execution routine.
Data structure for information about which behavior tree node plugin to load and how to configure the...
ROS 2 component that hosts a BehaviorModeExecutor and its owned mode on an in-process behavior tree e...
static CompletionReaction reactionFromString(const std::string &str)
Parse a string into a CompletionReaction.
CompletionReaction
Reaction performed when a behavior terminates or fails, using the in-charge executor API.
@ HOLD
Schedule the owned mode (hold position) and stay in charge.
@ DISARM
Disarm the vehicle.
@ COMPLETE
Report the owned mode as completed to the FMU (relinquish charge).
@ LAND
Land at the current position.
@ NONE
Do nothing and stay in charge.
void onExecutionResult(ExecutionResult result)
Handle the termination of the behavior running on the in-process executor.
BehaviorModeExecutor(BehaviorOwnedMode &owned_mode, const px4_ros2::ModeExecutorBase::Settings &settings, std::function< Config()> config_provider, auto_apms_behavior_tree::GenericTreeExecutorNode &engine)
Constructor.
void prepareTree()
Build the configured behavior tree and keep it detached, ready to be executed on the next activation.
Registration placeholder PX4 mode owned by a BehaviorModeExecutor.
Implementation of PX4 mode peers offered by px4_ros2_cpp enabling integration with AutoAPMS.
std::string entry_point
Single point of entry for behavior execution.
std::string build_request
Behavior build request (e.g. a registered behavior resource identity or XML).
std::string node_manifest
Encoded node manifest specifying additional nodes to load.