15 #ifndef NAV2_BEHAVIOR_TREE__BT_UTILS_HPP_
16 #define NAV2_BEHAVIOR_TREE__BT_UTILS_HPP_
22 #include "rclcpp/time.hpp"
23 #include "rclcpp/node.hpp"
24 #include "behaviortree_cpp/behavior_tree.h"
25 #include "geometry_msgs/msg/point.hpp"
26 #include "geometry_msgs/msg/quaternion.hpp"
27 #include "geometry_msgs/msg/pose_stamped.hpp"
28 #include "nav_msgs/msg/path.hpp"
43 inline geometry_msgs::msg::Point convertFromString(
const StringView key)
46 if (StartWith(key,
"json:")) {
48 new_key.remove_prefix(5);
49 return convertFromJSON<geometry_msgs::msg::Point>(new_key);
53 auto parts = BT::splitString(key,
';');
54 if (parts.size() != 3) {
55 throw std::runtime_error(
"invalid number of fields for point attribute)");
57 geometry_msgs::msg::Point position;
58 position.x = BT::convertFromString<double>(parts[0]);
59 position.y = BT::convertFromString<double>(parts[1]);
60 position.z = BT::convertFromString<double>(parts[2]);
71 inline geometry_msgs::msg::Quaternion convertFromString(
const StringView key)
74 if (StartWith(key,
"json:")) {
76 new_key.remove_prefix(5);
77 return convertFromJSON<geometry_msgs::msg::Quaternion>(new_key);
81 auto parts = BT::splitString(key,
';');
82 if (parts.size() != 4) {
83 throw std::runtime_error(
"invalid number of fields for orientation attribute)");
85 geometry_msgs::msg::Quaternion orientation;
86 orientation.x = BT::convertFromString<double>(parts[0]);
87 orientation.y = BT::convertFromString<double>(parts[1]);
88 orientation.z = BT::convertFromString<double>(parts[2]);
89 orientation.w = BT::convertFromString<double>(parts[3]);
100 inline geometry_msgs::msg::PoseStamped convertFromString(
const StringView key)
103 if (StartWith(key,
"json:")) {
105 new_key.remove_prefix(5);
106 return convertFromJSON<geometry_msgs::msg::PoseStamped>(new_key);
110 auto parts = BT::splitString(key,
';');
111 if (parts.size() != 9) {
112 throw std::runtime_error(
"invalid number of fields for PoseStamped attribute)");
114 geometry_msgs::msg::PoseStamped pose_stamped;
115 pose_stamped.header.stamp = rclcpp::Time(BT::convertFromString<int64_t>(parts[0]));
116 pose_stamped.header.frame_id = BT::convertFromString<std::string>(parts[1]);
117 pose_stamped.pose.position.x = BT::convertFromString<double>(parts[2]);
118 pose_stamped.pose.position.y = BT::convertFromString<double>(parts[3]);
119 pose_stamped.pose.position.z = BT::convertFromString<double>(parts[4]);
120 pose_stamped.pose.orientation.x = BT::convertFromString<double>(parts[5]);
121 pose_stamped.pose.orientation.y = BT::convertFromString<double>(parts[6]);
122 pose_stamped.pose.orientation.z = BT::convertFromString<double>(parts[7]);
123 pose_stamped.pose.orientation.w = BT::convertFromString<double>(parts[8]);
134 inline std::vector<geometry_msgs::msg::PoseStamped> convertFromString(
const StringView key)
137 auto parts = BT::splitString(key,
';');
138 if (parts.size() % 9 != 0) {
139 throw std::runtime_error(
"invalid number of fields for std::vector<PoseStamped> attribute)");
141 std::vector<geometry_msgs::msg::PoseStamped> poses;
142 for (
size_t i = 0; i < parts.size(); i += 9) {
143 geometry_msgs::msg::PoseStamped pose_stamped;
144 pose_stamped.header.stamp = rclcpp::Time(BT::convertFromString<int64_t>(parts[i]));
145 pose_stamped.header.frame_id = BT::convertFromString<std::string>(parts[i + 1]);
146 pose_stamped.pose.position.x = BT::convertFromString<double>(parts[i + 2]);
147 pose_stamped.pose.position.y = BT::convertFromString<double>(parts[i + 3]);
148 pose_stamped.pose.position.z = BT::convertFromString<double>(parts[i + 4]);
149 pose_stamped.pose.orientation.x = BT::convertFromString<double>(parts[i + 5]);
150 pose_stamped.pose.orientation.y = BT::convertFromString<double>(parts[i + 6]);
151 pose_stamped.pose.orientation.z = BT::convertFromString<double>(parts[i + 7]);
152 pose_stamped.pose.orientation.w = BT::convertFromString<double>(parts[i + 8]);
153 poses.push_back(pose_stamped);
165 inline nav_msgs::msg::Path convertFromString(
const StringView key)
168 if (StartWith(key,
"json:")) {
170 new_key.remove_prefix(5);
171 return convertFromJSON<nav_msgs::msg::Path>(new_key);
175 auto parts = BT::splitString(key,
';');
176 if ((parts.size() - 2) % 9 != 0) {
177 throw std::runtime_error(
"invalid number of fields for Path attribute)");
179 nav_msgs::msg::Path path;
180 path.header.stamp = rclcpp::Time(BT::convertFromString<int64_t>(parts[0]));
181 path.header.frame_id = BT::convertFromString<std::string>(parts[1]);
182 for (
size_t i = 2; i < parts.size(); i += 9) {
183 geometry_msgs::msg::PoseStamped pose_stamped;
184 path.header.stamp = rclcpp::Time(BT::convertFromString<int64_t>(parts[i]));
185 pose_stamped.header.frame_id = BT::convertFromString<std::string>(parts[i + 1]);
186 pose_stamped.pose.position.x = BT::convertFromString<double>(parts[i + 2]);
187 pose_stamped.pose.position.y = BT::convertFromString<double>(parts[i + 3]);
188 pose_stamped.pose.position.z = BT::convertFromString<double>(parts[i + 4]);
189 pose_stamped.pose.orientation.x = BT::convertFromString<double>(parts[i + 5]);
190 pose_stamped.pose.orientation.y = BT::convertFromString<double>(parts[i + 6]);
191 pose_stamped.pose.orientation.z = BT::convertFromString<double>(parts[i + 7]);
192 pose_stamped.pose.orientation.w = BT::convertFromString<double>(parts[i + 8]);
193 path.poses.push_back(pose_stamped);
205 inline std::chrono::milliseconds convertFromString<std::chrono::milliseconds>(
const StringView key)
208 if (StartWith(key,
"json:")) {
210 new_key.remove_prefix(5);
211 return convertFromJSON<std::chrono::milliseconds>(new_key);
213 return std::chrono::milliseconds(std::stoul(key.data()));
222 inline std::set<int> convertFromString(StringView key)
225 auto parts = splitString(key,
';');
228 for (
const auto part : parts) {
229 set.insert(convertFromString<int>(part));
241 template<
typename T1,
typename T2 = BT::TreeNode>
242 T1 deconflictPortAndParamFrame(
243 rclcpp::Node::SharedPtr node,
244 std::string param_name,
245 const T2 * behavior_tree_node)
248 bool param_from_input = behavior_tree_node->getInput(param_name, param_value).has_value();
250 if constexpr (std::is_same_v<T1, std::string>) {
252 param_from_input &= !param_value.empty();
255 if (!param_from_input) {
258 "Parameter '%s' not provided by behavior tree xml file, "
259 "using parameter from ros2 parameter file",
261 node->get_parameter(param_name, param_value);
266 "Parameter '%s' provided by behavior tree xml file",
283 template<
typename T>
inline
284 bool getInputPortOrBlackboard(
285 const BT::TreeNode & bt_node,
286 const BT::Blackboard & blackboard,
287 const std::string & param_name,
290 if (bt_node.getInput<T>(param_name, value)) {
293 if (blackboard.get<T>(param_name, value)) {
300 #define getInputOrBlackboard(name, value) \
301 getInputPortOrBlackboard(*this, *(this->config().blackboard), name, value);