Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
behavior_tree_engine.cpp
1 // Copyright (c) 2018 Intel Corporation
2 // Copyright (c) 2020 Florian Gramss
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include "nav2_behavior_tree/behavior_tree_engine.hpp"
17 
18 #include <memory>
19 #include <string>
20 #include <vector>
21 #include "tinyxml2.h" //NOLINT
22 
23 #include "rclcpp/logger.hpp"
24 #include "rclcpp/logging.hpp"
25 #include "rclcpp/utilities.hpp"
26 #include "behaviortree_cpp/json_export.h"
27 #include "behaviortree_cpp/utils/shared_library.h"
28 #include "nav2_behavior_tree/json_utils.hpp"
29 #include "nav2_behavior_tree/utils/loop_rate.hpp"
30 #include "nav2_ros_common/rate.hpp"
31 
32 namespace nav2_behavior_tree
33 {
34 
36  const std::vector<std::string> & plugin_libraries,
37  const nav2::LifecycleNode::SharedPtr node)
38 {
39  BT::SharedLibrary loader;
40  for (const auto & p : plugin_libraries) {
41  factory_.registerFromPlugin(loader.getOSName(p));
42  }
43 
44  node_ = node;
45 }
46 
47 BtStatus
49  BT::Tree * tree,
50  std::function<void()> onLoop,
51  std::function<bool()> cancelRequested,
52  std::chrono::milliseconds loopTimeout)
53 {
54  auto node = node_.lock();
55  if (!node) {
56  RCLCPP_ERROR(
57  rclcpp::get_logger("BehaviorTreeEngine"),
58  "BehaviorTreeEngine node expired. Exiting with failure.");
59  return BtStatus::FAILED;
60  }
61 
62  auto rate_clock = nav2::selectSteadyOrSimClock(node);
63  nav2_behavior_tree::LoopRate loopRate(loopTimeout, tree, rate_clock);
64  BT::NodeStatus result = BT::NodeStatus::RUNNING;
65 
66  // Loop until something happens with ROS or the node completes
67  try {
68  while (rclcpp::ok() && result == BT::NodeStatus::RUNNING) {
69  if (cancelRequested()) {
70  tree->haltTree();
71  return BtStatus::CANCELED;
72  }
73 
74  result = tree->tickOnce();
75 
76  if (result == BT::NodeStatus::RUNNING || result == BT::NodeStatus::IDLE) {
77  onLoop();
78  }
79 
80  if (!loopRate.sleep()) {
81  RCLCPP_DEBUG_THROTTLE(
82  rclcpp::get_logger("BehaviorTreeEngine"),
83  *rate_clock, 1000,
84  "Behavior Tree tick rate %0.2f was exceeded!",
85  1.0 / (loopRate.period().count() * 1.0e-9));
86  }
87  }
88  } catch (const BT::NodeExecutionError & ex) {
89  RCLCPP_ERROR(
90  rclcpp::get_logger("BehaviorTreeEngine"),
91  "BT Exception at Node: [%s] (Path: %s). Original error: %s. Exiting with failure.",
92  ex.failedNode().registration_name.c_str(),
93  ex.failedNode().node_path.c_str(),
94  ex.originalMessage().c_str());
95  return BtStatus::FAILED;
96  } catch (const std::exception & ex) {
97  RCLCPP_ERROR(
98  rclcpp::get_logger("BehaviorTreeEngine"),
99  "Behavior tree threw exception: %s. Exiting with failure.", ex.what());
100  return BtStatus::FAILED;
101  }
102 
103  return (result == BT::NodeStatus::SUCCESS) ? BtStatus::SUCCEEDED : BtStatus::FAILED;
104 }
105 
106 BT::Tree
108  const std::string & xml_string,
109  BT::Blackboard::Ptr blackboard)
110 {
111  return factory_.createTreeFromText(xml_string, blackboard);
112 }
113 
114 BT::Tree
116  const std::string & file_path,
117  BT::Blackboard::Ptr blackboard)
118 {
119  return factory_.createTreeFromFile(file_path, blackboard);
120 }
121 
122 BTInfo BehaviorTreeEngine::parseTreeInfo(const std::string & filename)
123 {
124  BTInfo info;
125  if (filename.empty()) {
126  RCLCPP_ERROR(rclcpp::get_logger("BehaviorTreeEngine"), "Empty BT file path.");
127  return info;
128  }
129 
130  tinyxml2::XMLDocument doc;
131  if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) {
132  RCLCPP_ERROR(rclcpp::get_logger("BehaviorTreeEngine"), "Could not parse: %s", filename.c_str());
133  return info;
134  }
135 
136  tinyxml2::XMLElement * root = doc.RootElement();
137  if (!root) {
138  RCLCPP_ERROR(rclcpp::get_logger("BehaviorTreeEngine"), "No root element in: %s",
139  filename.c_str());
140  return info;
141  }
142 
143  // Loop through all BehaviorTree elements to get all IDs
144  for (auto * bt = root->FirstChildElement("BehaviorTree"); bt;
145  bt = bt->NextSiblingElement("BehaviorTree"))
146  {
147  const char * id = bt->Attribute("ID");
148  if (id) {
149  info.behavior_tree_ids.emplace_back(id);
150  }
151  }
152 
153  // First try to get main_tree_to_execute attribute
154  const char * main_attr = root->Attribute("main_tree_to_execute");
155  if (main_attr) {
156  info.main_id = main_attr;
157  }
158 
159  // If main_tree_to_execute attribute is not set, we first check the number of BehaviorTree tags
160  if (info.main_id.empty()) {
161  // If only one BehaviorTree tag is found, we can use that as the main ID
162  // If multiple are found, we throw an error since we don't know
163  // which one to use as the main tree
164  if (info.behavior_tree_ids.size() == 1) {
165  info.main_id = info.behavior_tree_ids[0];
166  } else if (info.behavior_tree_ids.size() > 1) {
167  throw std::runtime_error(
168  "Multiple BehaviorTree elements found in " + filename +
169  " but no main_tree_to_execute attribute specified. Unable to determine main tree.");
170  }
171  }
172 
173  return info;
174 }
175 
176 BT::Tree
178  const std::string & tree_id,
179  BT::Blackboard::Ptr blackboard)
180 {
181  return factory_.createTree(tree_id, blackboard);
182 }
183 
186  const std::string & file_path)
187 {
188  factory_.registerBehaviorTreeFromFile(file_path);
189 }
190 
191 void
193  BT::Tree * tree,
194  uint16_t server_port)
195 {
196  // This logger publish status changes using Groot2
197  groot_monitor_ = std::make_unique<BT::Groot2Publisher>(*tree, server_port);
198 
199  // Register common types JSON definitions
200  BT::RegisterJsonDefinition<builtin_interfaces::msg::Time>();
201  BT::RegisterJsonDefinition<std_msgs::msg::Header>();
202 }
203 
204 void
206 {
207  if (groot_monitor_) {
208  groot_monitor_.reset();
209  }
210 }
211 
212 // In order to re-run a Behavior Tree, we must be able to reset all nodes to the initial state
213 void
215 {
216  // this halt signal should propagate through the entire tree.
217  tree.haltTree();
218 }
219 
220 } // namespace nav2_behavior_tree
BT::Tree createTree(const std::string &tree_id, BT::Blackboard::Ptr blackboard)
Function to create a BT from a BehaviorTree ID.
BehaviorTreeEngine(const std::vector< std::string > &plugin_libraries, nav2::LifecycleNode::SharedPtr node)
A constructor for nav2_behavior_tree::BehaviorTreeEngine.
void addGrootMonitoring(BT::Tree *tree, uint16_t server_port)
Add Groot2 monitor to publish BT status changes.
BT::Tree createTreeFromFile(const std::string &file_path, BT::Blackboard::Ptr blackboard)
Function to create a BT from an XML file.
void registerTreeFromFile(const std::string &file_path)
Function to register a BT from an XML file.
BT::Tree createTreeFromText(const std::string &xml_string, BT::Blackboard::Ptr blackboard)
Function to create a BT from a XML string.
void haltAllActions(BT::Tree &tree)
Function to explicitly reset all BT nodes to initial state.
BtStatus run(BT::Tree *tree, std::function< void()> onLoop, std::function< bool()> cancelRequested, std::chrono::milliseconds loopTimeout=std::chrono::milliseconds(10))
Function to execute a BT at a specific rate.
BTInfo parseTreeInfo(const std::string &filename)
Function to parse Behavior Tree information from an XML file.
A struct to hold Behavior Tree ID information.