AutoAPMS
Resilient Robot Mission Management
Loading...
Searching...
No Matches
run_behavior_tree.cpp
Go to the documentation of this file.
1// Copyright 2024 Robin Müller
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include <signal.h>
16
17#include <chrono>
18
19#include "behaviortree_cpp/loggers/bt_cout_logger.h"
20#include "behaviortree_cpp/loggers/groot2_publisher.h"
21#include "rclcpp/rclcpp.hpp"
25
26sig_atomic_t volatile termination_requested = 0;
27
28using namespace std::chrono_literals;
29using namespace auto_apms_behavior_tree;
30
31int main(int argc, char** argv)
32{
33 if (argc < 2)
34 {
35 std::cerr << "run_behavior_tree: Missing inputs! The program requires: \n\t1.) Name of the registered behavior "
36 "tree file (extension may be omitted).\n\t2.) Optional: Name of the tree to be executed "
37 "(Default is \"\": Using the main tree).\n\t3.) Optional: Name of the package to be searched "
38 "(Default is \"\": Searching in all packages).\n";
39 std::cerr << "Usage: run_behavior_tree <file_name> [<tree_name>] [<package_name>]\n";
40 return EXIT_FAILURE;
41 }
42 const std::string tree_file_name{ argv[1] };
43 const std::string tree_name{ argc > 2 ? argv[2] : "" };
44 const std::string package_name{ argc > 3 ? argv[3] : "" };
45
46 // Ensure that rclcpp is not shut down before the tree has been halted (on destruction) and all pending actions have
47 // been successfully canceled
48 rclcpp::init(argc, argv, rclcpp::InitOptions(), rclcpp::SignalHandlerOptions::SigTerm);
49 signal(SIGINT, [](int sig) {
50 (void)sig;
52 });
53 auto node_ptr = std::make_shared<rclcpp::Node>("run_behavior_tree");
54 auto_apms_core::exposeToDebugLogging(node_ptr->get_logger());
55
56 std::unique_ptr<TreeResource> tree_resource_ptr;
57 try
58 {
59 tree_resource_ptr = std::make_unique<TreeResource>(TreeResource::selectByFileName(tree_file_name, package_name));
60 }
61 catch (const std::exception& e)
62 {
63 RCLCPP_ERROR(node_ptr->get_logger(),
64 "ERROR searching for corresponding behavior tree resource with arguments tree_file_name: '%s', "
65 "package_name: '%s': %s",
66 tree_file_name.c_str(), package_name.c_str(), e.what());
67 return EXIT_FAILURE;
68 }
69
70 TreeBuilder builder;
71 try
72 {
73 builder.addTreeFromResource(*tree_resource_ptr, node_ptr);
74 }
75 catch (const std::exception& e)
76 {
77 RCLCPP_ERROR(node_ptr->get_logger(), "ERROR loading behavior tree '%s' from resource %s: %s", tree_name.c_str(),
78 tree_resource_ptr->tree_file_path.c_str(), e.what());
79 return EXIT_FAILURE;
80 }
81
82 TreeExecutor executor{ node_ptr };
83 auto future = executor.startExecution(
84 [&builder, &tree_name](TreeBlackboardSharedPtr bb) { return builder.buildTree(tree_name, bb); });
85
86 RCLCPP_INFO(node_ptr->get_logger(), "Executing tree with identity '%s::%s::%s'.",
87 tree_resource_ptr->tree_file_stem.c_str(), builder.getMainTreeName().c_str(),
88 tree_resource_ptr->package_name.c_str());
89
90 const auto termination_timeout = std::chrono::duration<double>(1.5);
91 std::chrono::steady_clock::time_point termination_start;
92 bool termination_started = false;
93 try
94 {
95 while (rclcpp::spin_until_future_complete(node_ptr, future, std::chrono::milliseconds(250)) !=
96 rclcpp::FutureReturnCode::SUCCESS)
97 {
98 if (termination_started)
99 {
100 if (std::chrono::steady_clock::now() - termination_start > termination_timeout)
101 {
102 RCLCPP_WARN(node_ptr->get_logger(), "Termination took too long. Aborted.");
103 return EXIT_FAILURE;
104 }
105 }
106 else if (termination_requested)
107 {
108 termination_start = std::chrono::steady_clock::now();
109 executor.setControlCommand(TreeExecutor::ControlCommand::TERMINATE);
110 termination_started = true;
111 RCLCPP_INFO(node_ptr->get_logger(), "Terminating tree execution...");
112 }
113 }
114 }
115 catch (const std::exception& e)
116 {
117 RCLCPP_ERROR(node_ptr->get_logger(), "ERROR during behavior tree execution: %s", e.what());
118 return EXIT_FAILURE;
119 }
120
121 // To prevent a deadlock, throw if future isn't ready at this point. However, this shouldn't happen.
122 if (future.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
123 {
124 throw std::logic_error("Future object is not ready.");
125 }
126
127 RCLCPP_INFO(node_ptr->get_logger(), "Finished with status %s.", toStr(future.get()).c_str());
128 rclcpp::shutdown();
129
130 return EXIT_SUCCESS;
131}
Class for creating behavior trees according to the builder design pattern.
TreeBuilder & addTreeFromResource(const TreeResource &resource, rclcpp::Node::SharedPtr node_ptr)
Tree buildTree(const std::string main_tree_name, TreeBlackboardSharedPtr root_bb_ptr=TreeBlackboard::create())
std::shared_future< ExecutionResult > startExecution(CreateTreeCallback create_tree_cb)
Definition executor.cpp:34
std::string toStr(TreeExecutor::ExecutionState state)
Definition executor.cpp:290
std::shared_ptr< TreeBlackboard > TreeBlackboardSharedPtr
void exposeToDebugLogging(const rclcpp::Logger &logger)
Definition logging.cpp:22
int main(int argc, char **argv)
sig_atomic_t volatile termination_requested
static TreeResource selectByFileName(const std::string &file_name, const std::string &package_name="")