Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
robot_utils.cpp
1 // Copyright (c) 2018 Intel Corporation
2 // Copyright (c) 2019 Steven Macenski
3 // Copyright (c) 2019 Samsung Research America
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #include <string>
18 #include <cmath>
19 #include <memory>
20 
21 #include "tf2/convert.hpp"
22 #include "tf2/utils.hpp"
23 #include "nav2_ros_common/tf2_factories.hpp"
24 #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
25 
26 #include "nav2_util/robot_utils.hpp"
27 #include "rclcpp/logger.hpp"
28 
29 namespace nav2_util
30 {
31 
32 bool lookupTransformWithStalenessCheck(
33  nav2::TransformBuffer & tf_buffer,
34  const std::string & target_frame,
35  const std::string & source_frame,
36  const rclcpp::Time & current_time,
37  double staleness_threshold,
38  geometry_msgs::msg::TransformStamped & transform)
39 {
40  if (target_frame == source_frame) {
41  geometry_msgs::msg::TransformStamped identity;
42  identity.header.frame_id = target_frame;
43  identity.header.stamp = current_time;
44  identity.child_frame_id = source_frame;
45  identity.transform.rotation.w = 1.0;
46  transform = identity;
47  return true;
48  }
49 
50  geometry_msgs::msg::TransformStamped latest_transform;
51  try {
52  latest_transform = tf_buffer.lookupTransform(
53  target_frame, source_frame, tf2::TimePointZero);
54  } catch (const tf2::TransformException & ex) {
55  RCLCPP_ERROR(
56  rclcpp::get_logger("lookupTransformWithStalenessCheck"),
57  "Failed to get transform: %s", ex.what());
58  return false;
59  }
60 
61  const bool has_timestamp =
62  latest_transform.header.stamp.sec != 0 || latest_transform.header.stamp.nanosec != 0;
63  if (staleness_threshold > 0.0 && has_timestamp) {
64  const auto transform_time = rclcpp::Time(
65  latest_transform.header.stamp, current_time.get_clock_type());
66  const double transform_age = (current_time - transform_time).seconds();
67  if (transform_age > staleness_threshold) {
68  RCLCPP_ERROR(
69  rclcpp::get_logger("lookupTransformWithStalenessCheck"),
70  "Transform from frame '%s' to frame '%s' is stale: age %fs exceeds threshold %fs",
71  source_frame.c_str(), target_frame.c_str(), transform_age, staleness_threshold);
72  return false;
73  }
74  }
75  transform = latest_transform;
76  return true;
77 }
78 
79 geometry_msgs::msg::PoseStamped transformToPoseStamped(
80  const geometry_msgs::msg::TransformStamped & transform)
81 {
82  geometry_msgs::msg::PoseStamped pose;
83  pose.header = transform.header;
84  pose.pose.position.x = transform.transform.translation.x;
85  pose.pose.position.y = transform.transform.translation.y;
86  pose.pose.position.z = transform.transform.translation.z;
87  pose.pose.orientation = transform.transform.rotation;
88  return pose;
89 }
90 
91 geometry_msgs::msg::TransformStamped poseToTransformStamped(
92  const geometry_msgs::msg::PoseStamped & pose, const std::string & child_frame)
93 {
94  geometry_msgs::msg::TransformStamped transform;
95  transform.header = pose.header;
96  transform.child_frame_id = child_frame;
97  transform.transform.translation.x = pose.pose.position.x;
98  transform.transform.translation.y = pose.pose.position.y;
99  transform.transform.translation.z = pose.pose.position.z;
100  transform.transform.rotation = pose.pose.orientation;
101  return transform;
102 }
103 
104 bool getFreshPose(
105  nav2::TransformBuffer & tf_buffer,
106  const std::string & target_frame,
107  const std::string & source_frame,
108  const rclcpp::Time & current_time,
109  double staleness_threshold,
110  geometry_msgs::msg::PoseStamped & pose)
111 {
112  geometry_msgs::msg::TransformStamped transform;
113  if (lookupTransformWithStalenessCheck(
114  tf_buffer, target_frame, source_frame, current_time, staleness_threshold, transform))
115  {
116  pose = transformToPoseStamped(transform);
117  return true;
118  }
119  return false;
120 }
121 
122 bool getCurrentPose(
123  geometry_msgs::msg::PoseStamped & global_pose,
124  nav2::TransformBuffer & tf_buffer, const std::string global_frame,
125  const std::string robot_frame, const double transform_timeout,
126  const rclcpp::Time stamp)
127 {
128  tf2::toMsg(tf2::Transform::getIdentity(), global_pose.pose);
129  global_pose.header.frame_id = robot_frame;
130  global_pose.header.stamp = stamp;
131 
132  return transformPoseInTargetFrame(
133  global_pose, global_pose, tf_buffer, global_frame, transform_timeout);
134 }
135 
136 bool transformPoseInTargetFrame(
137  const geometry_msgs::msg::PoseStamped & input_pose,
138  geometry_msgs::msg::PoseStamped & transformed_pose,
139  nav2::TransformBuffer & tf_buffer, const std::string target_frame,
140  const double transform_timeout)
141 {
142  static rclcpp::Logger logger = rclcpp::get_logger("transformPoseInTargetFrame");
143 
144  if (input_pose.header.frame_id == target_frame) {
145  transformed_pose = input_pose;
146  return true;
147  }
148 
149  try {
150  transformed_pose = tf_buffer.transform(
151  input_pose, target_frame,
152  tf2::durationFromSec(transform_timeout));
153  return true;
154  } catch (tf2::LookupException & ex) {
155  RCLCPP_ERROR(
156  logger,
157  "No Transform available Error looking up target frame: %s\n", ex.what());
158  } catch (tf2::ConnectivityException & ex) {
159  RCLCPP_ERROR(
160  logger,
161  "Connectivity Error looking up target frame: %s\n", ex.what());
162  } catch (tf2::ExtrapolationException & ex) {
163  RCLCPP_ERROR(
164  logger,
165  "Extrapolation Error looking up target frame: %s\n", ex.what());
166  } catch (tf2::TimeoutException & ex) {
167  RCLCPP_ERROR(
168  logger,
169  "Transform timeout with tolerance: %.4f", transform_timeout);
170  } catch (tf2::TransformException & ex) {
171  RCLCPP_ERROR(
172  logger, "Failed to transform from %s to %s",
173  input_pose.header.frame_id.c_str(), target_frame.c_str());
174  }
175 
176  return false;
177 }
178 
179 bool getTransform(
180  const std::string & source_frame_id,
181  const std::string & target_frame_id,
182  const tf2::Duration & transform_tolerance,
183  const nav2::TransformBuffer::SharedPtr tf_buffer,
184  geometry_msgs::msg::TransformStamped & transform_msg)
185 {
186  if (source_frame_id == target_frame_id) {
187  // We are already in required frame
188  return true;
189  }
190 
191  try {
192  // Obtaining the transform to get data from source to target frame
193  transform_msg = tf_buffer->lookupTransform(
194  target_frame_id, source_frame_id,
195  tf2::TimePointZero, transform_tolerance);
196  } catch (tf2::TransformException & e) {
197  RCLCPP_ERROR(
198  rclcpp::get_logger("getTransform"),
199  "Failed to get \"%s\"->\"%s\" frame transform: %s",
200  source_frame_id.c_str(), target_frame_id.c_str(), e.what());
201  return false;
202  }
203  return true;
204 }
205 
206 bool getTransform(
207  const std::string & source_frame_id,
208  const std::string & target_frame_id,
209  const tf2::Duration & transform_tolerance,
210  const nav2::TransformBuffer::SharedPtr tf_buffer,
211  tf2::Transform & tf2_transform)
212 {
213  tf2_transform.setIdentity(); // initialize by identical transform
214  geometry_msgs::msg::TransformStamped transform;
215  if (getTransform(source_frame_id, target_frame_id, transform_tolerance, tf_buffer, transform)) {
216  // Convert TransformStamped to TF2 transform
217  tf2::fromMsg(transform.transform, tf2_transform);
218  return true;
219  }
220  return false;
221 }
222 
223 bool getTransform(
224  const std::string & source_frame_id,
225  const rclcpp::Time & source_time,
226  const std::string & target_frame_id,
227  const rclcpp::Time & target_time,
228  const std::string & fixed_frame_id,
229  const tf2::Duration & transform_tolerance,
230  const nav2::TransformBuffer::SharedPtr tf_buffer,
231  geometry_msgs::msg::TransformStamped & transform_msg)
232 {
233  try {
234  // Obtaining the transform to get data from source to target frame.
235  // This also considers the time shift between source and target.
236  transform_msg = tf_buffer->lookupTransform(
237  target_frame_id, target_time,
238  source_frame_id, source_time,
239  fixed_frame_id, transform_tolerance);
240  } catch (tf2::TransformException & ex) {
241  RCLCPP_ERROR(
242  rclcpp::get_logger("getTransform"),
243  "Failed to get \"%s\"->\"%s\" frame transform: %s",
244  source_frame_id.c_str(), target_frame_id.c_str(), ex.what());
245  return false;
246  }
247 
248  return true;
249 }
250 
251 bool getTransform(
252  const std::string & source_frame_id,
253  const rclcpp::Time & source_time,
254  const std::string & target_frame_id,
255  const rclcpp::Time & target_time,
256  const std::string & fixed_frame_id,
257  const tf2::Duration & transform_tolerance,
258  const nav2::TransformBuffer::SharedPtr tf_buffer,
259  tf2::Transform & tf2_transform)
260 {
261  geometry_msgs::msg::TransformStamped transform;
262  tf2_transform.setIdentity(); // initialize by identical transform
263  if (getTransform(
264  source_frame_id, source_time, target_frame_id, target_time, fixed_frame_id,
265  transform_tolerance, tf_buffer, transform))
266  {
267  // Convert TransformStamped to TF2 transform
268  tf2::fromMsg(transform.transform, tf2_transform);
269  return true;
270  }
271 
272  return false;
273 }
274 
275 bool validateTwist(const geometry_msgs::msg::Twist & msg)
276 {
277  if (std::isinf(msg.linear.x) || std::isnan(msg.linear.x)) {
278  return false;
279  }
280 
281  if (std::isinf(msg.linear.y) || std::isnan(msg.linear.y)) {
282  return false;
283  }
284 
285  if (std::isinf(msg.linear.z) || std::isnan(msg.linear.z)) {
286  return false;
287  }
288 
289  if (std::isinf(msg.angular.x) || std::isnan(msg.angular.x)) {
290  return false;
291  }
292 
293  if (std::isinf(msg.angular.y) || std::isnan(msg.angular.y)) {
294  return false;
295  }
296 
297  if (std::isinf(msg.angular.z) || std::isnan(msg.angular.z)) {
298  return false;
299  }
300 
301  return true;
302 }
303 
304 } // end namespace nav2_util