mrs_lib
Various reusable classes, functions and utilities for use in MRS projects
Loading...
Searching...
No Matches
transformer.h
1
6#ifndef TRANSFORMER_H
7#define TRANSFORMER_H
8
9/* #define PCL_SPECIALIZATION true */
10/* #define OPENCV_SPECIALIZATION true */
11
12/* #define EIGEN_DONT_VECTORIZE */
13/* #define EIGEN_DISABLE_UNALIGNED_ARRAY_ASSERT */
14
15/* includes //{ */
16
17#include <rclcpp/rclcpp.hpp>
18
19#include <tf2_ros/transform_listener.h>
20#include <tf2_ros/buffer.h>
21#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
22
23#include <mrs_msgs/msg/reference_stamped.hpp>
24#include <geometry_msgs/msg/pose_stamped.hpp>
25#include <geometry_msgs/msg/vector3_stamped.hpp>
26
27#include <std_msgs/msg/header.hpp>
28
30
31#ifdef PCL_SPECIALIZATION
32#include <pcl_ros/point_cloud.hpp>
33#include <pcl_ros/transforms.hpp>
34#include <pcl_conversions/pcl_conversions.h>
35#endif
36
37#include <mutex>
38#include <experimental/type_traits>
39
40//}
41
42#ifdef PCL_SPECIALIZATION
43
44namespace tf2
45{
46
47 template <typename pt_t>
48 void doTransform(const pcl::PointCloud<pt_t>& cloud_in, pcl::PointCloud<pt_t>& cloud_out, const geometry_msgs::msg::TransformStamped& transform)
49 {
50 pcl_ros::transformPointCloud(cloud_in, cloud_out, transform.transform);
51 pcl_conversions::toPCL(transform.header.stamp, cloud_out.header.stamp);
52 cloud_out.header.frame_id = transform.header.frame_id;
53 }
54
55} // namespace tf2
56
57#endif
58
59namespace mrs_lib
60{
61
62 static const std::string UTM_ORIGIN = "utm_origin";
63 static const std::string LATLON_ORIGIN = "latlon_origin";
64
71 /* class Transformer //{ */
72
74 {
75
76 public:
77 /* Constructor, assignment operator and overloads //{ */
78
89
97 Transformer(const rclcpp::Node::SharedPtr& node);
98
107 /* Transformer(const rclcpp::Node::SharedPtr& node, const rclcpp::Duration& cache_time = rclcpp::Duration(tf2::BUFFER_CORE_DEFAULT_CACHE_TIME)); */
108
117 Transformer(const rclcpp::Node::SharedPtr& node, const rclcpp::Clock::SharedPtr& clock,
118 const rclcpp::Duration& cache_time = rclcpp::Duration(tf2::BUFFER_CORE_DEFAULT_CACHE_TIME), const rclcpp::QoS& qos = rclcpp::ServicesQoS());
119
129
130 //}
131
132 // | ------------------ Configuration methods ----------------- |
133
134 /* setDefaultFrame() //{ */
135
145 void setDefaultFrame(const std::string& frame_id)
146 {
147 std::scoped_lock lck(mutex_);
148 default_frame_id_ = frame_id;
149 }
150
151 //}
152
153 /* setDefaultPrefix() //{ */
154
168 void setDefaultPrefix(const std::string& prefix)
169 {
170 std::scoped_lock lck(mutex_);
171 if (prefix.empty())
172 prefix_ = "";
173 else
174 prefix_ = prefix + "/";
175 }
176
177 //}
178
179 /* setLatLon() //{ */
180
191 void setLatLon(const double lat, const double lon);
192
193 //}
194
195 /* setLookupTimeout() //{ */
196
206 void setLookupTimeout(const rclcpp::Duration timeout = rclcpp::Duration(0, 0))
207 {
208 std::scoped_lock lck(mutex_);
209 lookup_timeout_ = timeout;
210 }
211
212 //}
213
214 /* retryLookupNewest() //{ */
215
226 void retryLookupNewest(const bool retry = true)
227 {
228 std::scoped_lock lck(mutex_);
229 retry_lookup_newest_ = retry;
230 }
231
232 //}
233
234 /* beQuiet() //{ */
235
243 void beQuiet(const bool quiet = true)
244 {
245 std::scoped_lock lck(mutex_);
246 quiet_ = quiet;
247 }
248
249 //}
250
251 /* resolveFrame() //{ */
263 std::string resolveFrame(const std::string& frame_id)
264 {
265 std::scoped_lock lck(mutex_);
266 return resolveFrameImpl(frame_id);
267 }
268 //}
269
270 /* transformSingle() //{ */
271
280 template <class T>
281 [[nodiscard]] std::optional<T> transformSingle(const T& what, const std::string& to_frame);
282
293 template <class T>
294 [[nodiscard]] std::optional<std::shared_ptr<T>> transformSingle(const std::shared_ptr<T>& what, const std::string& to_frame)
295 {
296 return transformSingle(std::shared_ptr<const T>(what), to_frame);
297 }
298
309 template <class T>
310 [[nodiscard]] std::optional<std::shared_ptr<T>> transformSingle(const std::shared_ptr<const T>& what, const std::string& to_frame)
311 {
312 auto ret = transformSingle(*what, to_frame);
313 if (ret == std::nullopt)
314 return std::nullopt;
315 else
316 return std::make_shared<T>(std::move(ret.value()));
317 }
318
331 template <class T>
332 [[nodiscard]] std::optional<T> transformSingle(const std::string& from_frame, const T& what, const std::string& to_frame,
333 const rclcpp::Time& time_stamp = rclcpp::Time(0));
334
347 template <class T>
348 [[nodiscard]] std::optional<std::shared_ptr<T>> transformSingle(const std::string& from_frame, const std::shared_ptr<T>& what, const std::string& to_frame,
349 const rclcpp::Time& time_stamp = rclcpp::Time(0))
350 {
351 return transformSingle(from_frame, std::shared_ptr<const T>(what), to_frame, time_stamp);
352 }
353
366 template <class T>
367 [[nodiscard]] std::optional<std::shared_ptr<T>> transformSingle(const std::string& from_frame, const std::shared_ptr<const T>& what,
368 const std::string& to_frame, const rclcpp::Time& time_stamp = rclcpp::Time(0))
369 {
370 auto ret = transformSingle(from_frame, *what, to_frame, time_stamp);
371 if (ret == std::nullopt)
372 return std::nullopt;
373 else
374 return std::make_shared<T>(std::move(ret.value()));
375 }
376
377 //}
378
379 /* transform() //{ */
380
389 template <class T>
390 [[nodiscard]] std::optional<T> transform(const T& what, const geometry_msgs::msg::TransformStamped& tf);
391
402 template <class T>
403 [[nodiscard]] std::optional<std::shared_ptr<T>> transform(const std::shared_ptr<const T>& what, const geometry_msgs::msg::TransformStamped& tf)
404 {
405 auto ret = transform(*what, tf);
406 if (ret == std::nullopt)
407 return std::nullopt;
408 else
409 return std::make_shared<T>(std::move(ret.value()));
410 }
411
422 template <class T>
423 [[nodiscard]] std::optional<std::shared_ptr<T>> transform(const std::shared_ptr<T>& what, const geometry_msgs::msg::TransformStamped& tf)
424 {
425 return transform(std::shared_ptr<const T>(what), tf);
426 }
427
428 //}
429
430 /* transformAsVector() method //{ */
441 [[nodiscard]] std::optional<Eigen::Vector3d> transformAsVector(const Eigen::Vector3d& what, const geometry_msgs::msg::TransformStamped& tf);
442
455 [[nodiscard]] std::optional<Eigen::Vector3d> transformAsVector(const std::string& from_frame, const Eigen::Vector3d& what, const std::string& to_frame,
456 const rclcpp::Time& time_stamp = rclcpp::Time(0));
457 //}
458
459 /* transformAsPoint() method //{ */
470 [[nodiscard]] std::optional<Eigen::Vector3d> transformAsPoint(const Eigen::Vector3d& what, const geometry_msgs::msg::TransformStamped& tf);
471
484 [[nodiscard]] std::optional<Eigen::Vector3d> transformAsPoint(const std::string& from_frame, const Eigen::Vector3d& what, const std::string& to_frame,
485 const rclcpp::Time& time_stamp = rclcpp::Time(0));
486 //}
487
488 /* getTransform() //{ */
489
499 [[nodiscard]] std::optional<geometry_msgs::msg::TransformStamped> getTransform(const std::string& from_frame, const std::string& to_frame,
500 const rclcpp::Time& time_stamp = rclcpp::Time(0));
501
517 [[nodiscard]] std::optional<geometry_msgs::msg::TransformStamped> getTransform(const std::string& from_frame, const rclcpp::Time& from_stamp,
518 const std::string& to_frame, const rclcpp::Time& to_stamp,
519 const std::string& fixed_frame);
520
521 //}
522
523 private:
524 /* private members, methods etc //{ */
525
526 std::mutex mutex_;
527
528 // keeps track whether a non-basic constructor was called and the transform listener is initialized
529 bool initialized_ = false;
530 rclcpp::Node::SharedPtr node_;
531
532 std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
533 std::unique_ptr<tf2_ros::TransformListener> tf_listener_ptr_;
534
535 // user-configurable options
536 std::string default_frame_id_ = "";
537 std::string prefix_ = ""; // if not empty, includes the forward slash
538 bool quiet_ = false;
539 rclcpp::Duration lookup_timeout_ = rclcpp::Duration(0, 0);
540 bool retry_lookup_newest_ = false;
541
542 bool got_utm_zone_ = false;
543 std::array<char, 10> utm_zone_ = {};
544
545 // returns the first namespace prefix of the frame (if any) includin the forward slash
546 std::string getFramePrefix(const std::string& frame_id);
547
548 template <class T>
549 std::optional<T> transformImpl(const geometry_msgs::msg::TransformStamped& tf, const T& what);
550 std::optional<mrs_msgs::msg::ReferenceStamped> transformImpl(const geometry_msgs::msg::TransformStamped& tf, const mrs_msgs::msg::ReferenceStamped& what);
551 std::optional<Eigen::Vector3d> transformImpl(const geometry_msgs::msg::TransformStamped& tf, const Eigen::Vector3d& what);
552
553 [[nodiscard]] std::optional<geometry_msgs::msg::TransformStamped> getTransformImpl(const std::string& from_frame, const std::string& to_frame,
554 const rclcpp::Time& time_stamp, const std::string& latlon_frame);
555
556 [[nodiscard]] std::optional<geometry_msgs::msg::TransformStamped> getTransformImpl(const std::string& from_frame, const rclcpp::Time& from_stamp,
557 const std::string& to_frame, const rclcpp::Time& to_stamp,
558 const std::string& fixed_frame, const std::string& latlon_frame);
559
560 std::string resolveFrameImpl(const std::string& frame_id);
561
562 template <class T>
563 std::optional<T> doTransform(const T& what, const geometry_msgs::msg::TransformStamped& tf);
564
565 //}
566
567 // | ------------------- some helper methods ------------------ |
568 public:
569 /* frame_from(), frame_to() and inverse() methods //{ */
570
577 static constexpr const std::string& frame_from(const geometry_msgs::msg::TransformStamped& msg)
578 {
579 return msg.child_frame_id;
580 }
581
590 static constexpr std::string& frame_from(geometry_msgs::msg::TransformStamped& msg)
591 {
592 return msg.child_frame_id;
593 }
594
601 static constexpr const std::string& frame_to(const geometry_msgs::msg::TransformStamped& msg)
602 {
603 return msg.header.frame_id;
604 }
605
614 static constexpr std::string& frame_to(geometry_msgs::msg::TransformStamped& msg)
615 {
616 return msg.header.frame_id;
617 }
618
625 static geometry_msgs::msg::TransformStamped inverse(const geometry_msgs::msg::TransformStamped& msg)
626 {
627 tf2::Transform tf2;
628 tf2::fromMsg(msg.transform, tf2);
629 tf2 = tf2.inverse();
630 return create_transform(frame_to(msg), frame_from(msg), msg.header.stamp, tf2::toMsg(tf2));
631 }
632 //}
633
634 private:
635 /* create_transform() method //{ */
636 static geometry_msgs::msg::TransformStamped create_transform(const std::string& from_frame, const std::string& to_frame, const rclcpp::Time& time_stamp)
637 {
638 geometry_msgs::msg::TransformStamped ret;
639 frame_from(ret) = from_frame;
640 frame_to(ret) = to_frame;
641 ret.header.stamp = time_stamp;
642 return ret;
643 }
644
645 static geometry_msgs::msg::TransformStamped create_transform(const std::string& from_frame, const std::string& to_frame, const rclcpp::Time& time_stamp,
646 const geometry_msgs::msg::Transform& tf)
647 {
648 geometry_msgs::msg::TransformStamped ret;
649 frame_from(ret) = from_frame;
650 frame_to(ret) = to_frame;
651 ret.header.stamp = time_stamp;
652 ret.transform = tf;
653 return ret;
654 }
655 //}
656
657 /* copyChangeFrame() and related methods //{ */
658
659 // helper type and member for detecting whether a message has a header using SFINAE
660 template <typename T>
661 using has_header_member_chk = decltype(std::declval<T&>().header);
662
663 template <typename T>
664 static constexpr bool has_header_member_v = std::experimental::is_detected<has_header_member_chk, T>::value;
665
666 template <typename msg_t>
667 std_msgs::msg::Header getHeader(const msg_t& msg);
668
669#ifdef PCL_SPECIALIZATION
670 template <typename pt_t>
671 std_msgs::msg::Header getHeader(const pcl::PointCloud<pt_t>& cloud);
672
673 template <typename pt_t>
674 void setHeader(pcl::PointCloud<pt_t>& cloud, const std_msgs::msg::Header& header);
675#endif
676
677 template <typename msg_t>
678 void setHeader(msg_t& msg, const std_msgs::msg::Header& header);
679
680 template <typename T>
681 T copyChangeFrame(const T& what, const std::string& frame_id);
682
683 //}
684
685 /* methods for converting between lattitude/longitude and UTM coordinates //{ */
686 geometry_msgs::msg::Point LLtoUTM(const geometry_msgs::msg::Point& what, const std::string& prefix);
687 geometry_msgs::msg::PointStamped LLtoUTM(const geometry_msgs::msg::PointStamped& what, const std::string& prefix);
688 geometry_msgs::msg::Pose LLtoUTM(const geometry_msgs::msg::Pose& what, const std::string& prefix);
689 geometry_msgs::msg::PoseStamped LLtoUTM(const geometry_msgs::msg::PoseStamped& what, const std::string& prefix);
690
691 std::optional<geometry_msgs::msg::Point> UTMtoLL(const geometry_msgs::msg::Point& what, const std::string& prefix);
692 std::optional<geometry_msgs::msg::PointStamped> UTMtoLL(const geometry_msgs::msg::PointStamped& what, const std::string& prefix);
693 std::optional<geometry_msgs::msg::Pose> UTMtoLL(const geometry_msgs::msg::Pose& what, const std::string& prefix);
694 std::optional<geometry_msgs::msg::PoseStamped> UTMtoLL(const geometry_msgs::msg::PoseStamped& what, const std::string& prefix);
695
696 // helper types and member for detecting whether the UTMtoLL and LLtoUTM methods are defined for a certain message
697 template <class Class, typename Message>
698 using UTMLL_method_chk = decltype(std::declval<Class>().UTMtoLL(std::declval<const Message&>(), ""));
699
700 template <class Class, typename Message>
701 using LLUTM_method_chk = decltype(std::declval<Class>().LLtoUTM(std::declval<const Message&>(), ""));
702
703 template <class Class, typename Message>
704 static constexpr bool UTMLL_exists_v =
705 std::experimental::is_detected<UTMLL_method_chk, Class, Message>::value && std::experimental::is_detected<LLUTM_method_chk, Class, Message>::value;
706 //}
707 };
708
709 //}
710
711} // namespace mrs_lib
712
713#ifndef TRANSFORMER_HPP
714#include <mrs_lib/impl/transformer.hpp>
715#endif
716
717#endif // TRANSFORMER_H
A convenience wrapper class for ROS's native TF2 API to simplify transforming of various messages.
Definition transformer.h:74
std::optional< std::shared_ptr< T > > transformSingle(const std::shared_ptr< const T > &what, const std::string &to_frame)
Transforms a single variable to a new frame.
Definition transformer.h:310
void setDefaultFrame(const std::string &frame_id)
Sets the default frame ID to be used instead of any empty frame ID.
Definition transformer.h:145
std::optional< T > transform(const T &what, const geometry_msgs::msg::TransformStamped &tf)
Transform a variable to new frame using a particular transformation.
Definition transformer.hpp:185
static constexpr const std::string & frame_to(const geometry_msgs::msg::TransformStamped &msg)
A convenience function that returns the frame to which the message transforms.
Definition transformer.h:601
std::optional< std::shared_ptr< T > > transformSingle(const std::string &from_frame, const std::shared_ptr< const T > &what, const std::string &to_frame, const rclcpp::Time &time_stamp=rclcpp::Time(0))
Transforms a single variable to a new frame and returns it or std::nullopt if transformation fails.
Definition transformer.h:367
std::optional< std::shared_ptr< T > > transformSingle(const std::string &from_frame, const std::shared_ptr< T > &what, const std::string &to_frame, const rclcpp::Time &time_stamp=rclcpp::Time(0))
Transforms a single variable to a new frame and returns it or std::nullopt if transformation fails.
Definition transformer.h:348
static geometry_msgs::msg::TransformStamped inverse(const geometry_msgs::msg::TransformStamped &msg)
A convenience function implements returns the inverse of the transform message as a one-liner.
Definition transformer.h:625
std::optional< geometry_msgs::msg::TransformStamped > getTransform(const std::string &from_frame, const std::string &to_frame, const rclcpp::Time &time_stamp=rclcpp::Time(0))
Obtains a transform between two frames in a given time.
Definition transformer.cpp:96
static constexpr std::string & frame_from(geometry_msgs::msg::TransformStamped &msg)
A convenience function that returns the frame from which the message transforms.
Definition transformer.h:590
std::optional< std::shared_ptr< T > > transform(const std::shared_ptr< T > &what, const geometry_msgs::msg::TransformStamped &tf)
Transform a variable to new frame using a particular transformation.
Definition transformer.h:423
void setLatLon(const double lat, const double lon)
Sets the curret lattitude and longitude for UTM zone calculation.
Definition transformer.cpp:138
std::optional< Eigen::Vector3d > transformAsPoint(const Eigen::Vector3d &what, const geometry_msgs::msg::TransformStamped &tf)
Transform an Eigen::Vector3d (interpreting it as a point).
Definition transformer.cpp:208
void setDefaultPrefix(const std::string &prefix)
Sets the default frame ID prefix to be used if no prefix is present in the frame.
Definition transformer.h:168
std::optional< T > transformSingle(const T &what, const std::string &to_frame)
Transforms a single variable to a new frame and returns it or std::nullopt if transformation fails.
Definition transformer.hpp:145
Transformer & operator=(Transformer &&other)
A convenience move assignment operator.
Definition transformer.cpp:71
std::optional< std::shared_ptr< T > > transform(const std::shared_ptr< const T > &what, const geometry_msgs::msg::TransformStamped &tf)
Transform a variable to new frame using a particular transformation.
Definition transformer.h:403
std::optional< Eigen::Vector3d > transformAsVector(const Eigen::Vector3d &what, const geometry_msgs::msg::TransformStamped &tf)
Transform an Eigen::Vector3d (interpreting it as a vector).
Definition transformer.cpp:151
static constexpr const std::string & frame_from(const geometry_msgs::msg::TransformStamped &msg)
A convenience function that returns the frame from which the message transforms.
Definition transformer.h:577
void setLookupTimeout(const rclcpp::Duration timeout=rclcpp::Duration(0, 0))
Set a timeout for transform lookup.
Definition transformer.h:206
static constexpr std::string & frame_to(geometry_msgs::msg::TransformStamped &msg)
A convenience function that returns the frame to which the message transforms.
Definition transformer.h:614
std::string resolveFrame(const std::string &frame_id)
Deduce the full frame ID from a shortened or empty string using current default prefix and default fr...
Definition transformer.h:263
void beQuiet(const bool quiet=true)
Enable/disable some prints that may be too noisy.
Definition transformer.h:243
void retryLookupNewest(const bool retry=true)
Enable/disable retry of a failed transform lookup with rclcpp::Time(0).
Definition transformer.h:226
Transformer()
A convenience constructor that doesn't initialize anything.
Definition transformer.cpp:43
std::optional< std::shared_ptr< T > > transformSingle(const std::shared_ptr< T > &what, const std::string &to_frame)
Transforms a single variable to a new frame.
Definition transformer.h:294
Defines useful geometry utilities and functions.
All mrs_lib functions, classes, variables and definitions are contained in this namespace.
Definition attitude_converter.h:24