AutoAPMS
Resilient Robot Mission Management
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Modules Pages
goto.cpp
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// https://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 <Eigen/Geometry>
16
17#include "auto_apms_behavior_tree_core/node.hpp"
18#include "auto_apms_interfaces/action/go_to.hpp"
19
20#define INPUT_KEY_FRAME "frame"
21#define INPUT_KEY_VEC "vector"
22#define INPUT_KEY_X "x"
23#define INPUT_KEY_Y "y"
24#define INPUT_KEY_Z "z"
25#define INPUT_KEY_YAW "yaw"
26#define INPUT_KEY_MAX_HOR_VEL "max_horizontal_vel"
27#define INPUT_KEY_MAX_VER_VEL "max_vertical_vel"
28#define INPUT_KEY_MAX_HEADING_RATE "max_heading_rate"
29#define INPUT_KEY_REACHED_THRESH_POS "reached_thresh_pos"
30#define INPUT_KEY_REACHED_THRESH_VEL "reached_thresh_vel"
31#define INPUT_KEY_REACHED_THRESH_YAW "reached_thresh_yaw"
32
33namespace auto_apms_px4
34{
35
36class GoToAction : public auto_apms_behavior_tree::core::RosActionNode<auto_apms_interfaces::action::GoTo>
37{
38public:
39 using RosActionNode::RosActionNode;
40
41 static BT::PortsList providedPorts()
42 {
43 return {
44 BT::InputPort<std::string>(
45 INPUT_KEY_FRAME, "global",
46 "Reference frame: 'global' (Latitude, longitude, altitude (AMSL)) or 'local' (North, east, down from start)"),
47 BT::InputPort<Eigen::MatrixXd>(INPUT_KEY_VEC, "Target position as a row vector (separated by ;)"),
48 BT::InputPort<double>(INPUT_KEY_X, "Override vector entry X"),
49 BT::InputPort<double>(INPUT_KEY_Y, "Override vector entry Y"),
50 BT::InputPort<double>(INPUT_KEY_Z, "Override vector entry Z"),
51 BT::InputPort<double>(INPUT_KEY_YAW, "Desired yaw position in degree from north (heading) [-180°, 180)"),
52 BT::InputPort<double>(INPUT_KEY_MAX_HOR_VEL, 10.0, "Maximum horizontal velocity [m/s]"),
53 BT::InputPort<double>(INPUT_KEY_MAX_VER_VEL, 5.0, "Maximum vertical velocity [m/s]"),
54 BT::InputPort<double>(INPUT_KEY_MAX_HEADING_RATE, 30.0, "Maximum heading rate [°/s]"),
55 BT::InputPort<double>(
56 INPUT_KEY_REACHED_THRESH_POS, .5, "Maximum position error [m] under which the position is considered reached"),
57 BT::InputPort<double>(
58 INPUT_KEY_REACHED_THRESH_VEL, .3,
59 "Maximum velocity error [m/s] under which the position is considered reached"),
60 BT::InputPort<double>(
61 INPUT_KEY_REACHED_THRESH_YAW, 7.0, "Maximum heading error [°] under which the position is considered reached")};
62 }
63
64 bool setGoal(Goal & goal)
65 {
66 // Node selects the correct topic under the hood using this field
67 if (const BT::Expected<std::string> expected = getInput<std::string>(INPUT_KEY_FRAME)) {
68 const std::string frame_str = expected.value();
69 if (frame_str != "global" && frame_str != "local") {
70 RCLCPP_ERROR(
71 logger_, "%s - Invalid reference frame '%s'", context_.getFullyQualifiedTreeNodeName(this).c_str(),
72 frame_str.c_str());
73 return false;
74 }
75 } else {
76 RCLCPP_ERROR(logger_, "%s - %s", context_.getFullyQualifiedTreeNodeName(this).c_str(), expected.error().c_str());
77 return false;
78 }
79
80 int position_valid = 0;
81 BT::PortsRemapping::iterator input_port_it = config().input_ports.find(INPUT_KEY_VEC);
82 if (input_port_it != config().input_ports.end() && !input_port_it->second.empty()) {
83 if (const BT::Expected<Eigen::MatrixXd> expected = getInput<Eigen::MatrixXd>(INPUT_KEY_VEC)) {
84 const Eigen::MatrixXd & mat = expected.value();
85 if (mat.rows() == 1 && mat.cols() == 3) {
86 goal.pos.x = mat(0, 0);
87 goal.pos.y = mat(0, 1);
88 goal.pos.z = mat(0, 2);
89 position_valid = 3;
90 } else {
91 RCLCPP_ERROR(
92 logger_, "%s - Input vector has wrong size (Required: 3 - Actual: %i)",
93 context_.getFullyQualifiedTreeNodeName(this).c_str(), static_cast<int>(mat.rows()));
94 return false;
95 }
96 } else {
97 RCLCPP_ERROR(
98 logger_, "%s - %s", context_.getFullyQualifiedTreeNodeName(this).c_str(), expected.error().c_str());
99 return false;
100 }
101 }
102
103 input_port_it = config().input_ports.find(INPUT_KEY_X);
104 if (input_port_it != config().input_ports.end() && !input_port_it->second.empty()) {
105 if (const BT::Expected<double> expected = getInput<double>(INPUT_KEY_X)) {
106 goal.pos.x = expected.value();
107 position_valid++;
108 } else {
109 RCLCPP_ERROR(
110 logger_, "%s - %s", context_.getFullyQualifiedTreeNodeName(this).c_str(), expected.error().c_str());
111 return false;
112 }
113 }
114
115 input_port_it = config().input_ports.find(INPUT_KEY_Y);
116 if (input_port_it != config().input_ports.end() && !input_port_it->second.empty()) {
117 if (const BT::Expected<double> expected = getInput<double>(INPUT_KEY_Y)) {
118 goal.pos.y = expected.value();
119 position_valid++;
120 } else {
121 RCLCPP_ERROR(
122 logger_, "%s - %s", context_.getFullyQualifiedTreeNodeName(this).c_str(), expected.error().c_str());
123 return false;
124 }
125 }
126
127 input_port_it = config().input_ports.find(INPUT_KEY_Z);
128 if (input_port_it != config().input_ports.end() && !input_port_it->second.empty()) {
129 if (const BT::Expected<double> expected = getInput<double>(INPUT_KEY_Z)) {
130 goal.pos.z = expected.value();
131 position_valid++;
132 } else {
133 RCLCPP_ERROR(
134 logger_, "%s - %s", context_.getFullyQualifiedTreeNodeName(this).c_str(), expected.error().c_str());
135 return false;
136 }
137 }
138
139 if (position_valid < 3) {
140 RCLCPP_ERROR(
141 logger_, "%s - Position is not valid. You must provide information for all three vector entries (%i missing)",
142 context_.getFullyQualifiedTreeNodeName(this).c_str(), static_cast<int>(3 - position_valid));
143 return false;
144 }
145
146 input_port_it = config().input_ports.find(INPUT_KEY_YAW);
147 if (input_port_it == config().input_ports.end() || input_port_it->second.empty()) {
148 goal.head_towards_destination = true;
149 } else {
150 if (const BT::Expected<double> expected = getInput<double>(INPUT_KEY_YAW)) {
151 goal.head_towards_destination = false;
152 goal.heading_clockwise_from_north_deg = expected.value();
153 } else {
154 RCLCPP_ERROR(
155 logger_, "%s - %s", context_.getFullyQualifiedTreeNodeName(this).c_str(), expected.error().c_str());
156 return false;
157 }
158 }
159
160 if (const BT::Expected<double> expected = getInput<double>(INPUT_KEY_MAX_HOR_VEL)) {
161 goal.max_horizontal_vel_m_s = expected.value();
162 } else {
163 RCLCPP_ERROR(logger_, "%s - %s", context_.getFullyQualifiedTreeNodeName(this).c_str(), expected.error().c_str());
164 return false;
165 }
166
167 if (const BT::Expected<double> expected = getInput<double>(INPUT_KEY_MAX_VER_VEL)) {
168 goal.max_vertical_vel_m_s = expected.value();
169 } else {
170 RCLCPP_ERROR(logger_, "%s - %s", context_.getFullyQualifiedTreeNodeName(this).c_str(), expected.error().c_str());
171 return false;
172 }
173
174 if (const BT::Expected<double> expected = getInput<double>(INPUT_KEY_MAX_HEADING_RATE)) {
175 goal.max_heading_rate_deg_s = expected.value();
176 } else {
177 RCLCPP_ERROR(logger_, "%s - %s", context_.getFullyQualifiedTreeNodeName(this).c_str(), expected.error().c_str());
178 return false;
179 }
180
181 if (const BT::Expected<double> expected = getInput<double>(INPUT_KEY_REACHED_THRESH_POS)) {
182 goal.reached_thresh_pos_m = expected.value();
183 } else {
184 RCLCPP_ERROR(logger_, "%s - %s", context_.getFullyQualifiedTreeNodeName(this).c_str(), expected.error().c_str());
185 return false;
186 }
187
188 if (const BT::Expected<double> expected = getInput<double>(INPUT_KEY_REACHED_THRESH_VEL)) {
189 goal.reached_thresh_vel_m_s = expected.value();
190 } else {
191 RCLCPP_ERROR(logger_, "%s - %s", context_.getFullyQualifiedTreeNodeName(this).c_str(), expected.error().c_str());
192 return false;
193 }
194
195 if (const BT::Expected<double> expected = getInput<double>(INPUT_KEY_REACHED_THRESH_YAW)) {
196 goal.reached_thresh_heading_deg = expected.value();
197 } else {
198 RCLCPP_ERROR(logger_, "%s - %s", context_.getFullyQualifiedTreeNodeName(this).c_str(), expected.error().c_str());
199 return false;
200 }
201
202 return true;
203 }
204};
205
206} // namespace auto_apms_px4
207
208AUTO_APMS_BEHAVIOR_TREE_DECLARE_NODE(auto_apms_px4::GoToAction)
#define AUTO_APMS_BEHAVIOR_TREE_DECLARE_NODE(type)
Macro for registering a behavior tree node plugin.
Definition node.hpp:43
Implementation of PX4 mode peers offered by px4_ros2_cpp enabling integration with AutoAPMS.
Definition mode.hpp:26