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
47template <typename pt_t>
48void doTransform(const pcl::PointCloud<pt_t>& cloud_in, pcl::PointCloud<pt_t>& cloud_out, const geometry_msgs::msg::TransformStamped& transform) {
49 pcl_ros::transformPointCloud(cloud_in, cloud_out, transform.transform);
50 pcl_conversions::toPCL(transform.header.stamp, cloud_out.header.stamp);
51 cloud_out.header.frame_id = transform.header.frame_id;
52}
53
54} // namespace tf2
55
56#endif
57
58namespace mrs_lib
59{
60
61static const std::string UTM_ORIGIN = "utm_origin";
62static const std::string LATLON_ORIGIN = "latlon_origin";
63
70/* class Transformer //{ */
71
73
74public:
75 /* Constructor, assignment operator and overloads //{ */
76
87
95 Transformer(const rclcpp::Node::SharedPtr& node);
96
105 /* Transformer(const rclcpp::Node::SharedPtr& node, const rclcpp::Duration& cache_time = rclcpp::Duration(tf2::BUFFER_CORE_DEFAULT_CACHE_TIME)); */
106
115 Transformer(const rclcpp::Node::SharedPtr& node, const rclcpp::Clock::SharedPtr& clock,
116 const rclcpp::Duration& cache_time = rclcpp::Duration(tf2::BUFFER_CORE_DEFAULT_CACHE_TIME), const rclcpp::QoS& qos = rclcpp::ServicesQoS());
117
127
128 //}
129
130 // | ------------------ Configuration methods ----------------- |
131
132 /* setDefaultFrame() //{ */
133
143 void setDefaultFrame(const std::string& frame_id) {
144 std::scoped_lock lck(mutex_);
145 default_frame_id_ = frame_id;
146 }
147
148 //}
149
150 /* setDefaultPrefix() //{ */
151
165 void setDefaultPrefix(const std::string& prefix) {
166 std::scoped_lock lck(mutex_);
167 if (prefix.empty())
168 prefix_ = "";
169 else
170 prefix_ = prefix + "/";
171 }
172
173 //}
174
175 /* setLatLon() //{ */
176
187 void setLatLon(const double lat, const double lon);
188
189 //}
190
191 /* setLookupTimeout() //{ */
192
202 void setLookupTimeout(const rclcpp::Duration timeout = rclcpp::Duration(0, 0)) {
203 std::scoped_lock lck(mutex_);
204 lookup_timeout_ = timeout;
205 }
206
207 //}
208
209 /* retryLookupNewest() //{ */
210
221 void retryLookupNewest(const bool retry = true) {
222 std::scoped_lock lck(mutex_);
223 retry_lookup_newest_ = retry;
224 }
225
226 //}
227
228 /* beQuiet() //{ */
229
237 void beQuiet(const bool quiet = true) {
238 std::scoped_lock lck(mutex_);
239 quiet_ = quiet;
240 }
241
242 //}
243
244 /* resolveFrame() //{ */
256 std::string resolveFrame(const std::string& frame_id) {
257 std::scoped_lock lck(mutex_);
258 return resolveFrameImpl(frame_id);
259 }
260 //}
261
262 /* transformSingle() //{ */
263
272 template <class T>
273 [[nodiscard]] std::optional<T> transformSingle(const T& what, const std::string& to_frame);
274
285 template <class T>
286 [[nodiscard]] std::optional<std::shared_ptr<T>> transformSingle(const std::shared_ptr<T>& what, const std::string& to_frame) {
287 return transformSingle(std::shared_ptr<const T>(what), to_frame);
288 }
289
300 template <class T>
301 [[nodiscard]] std::optional<std::shared_ptr<T>> transformSingle(const std::shared_ptr<const T>& what, const std::string& to_frame) {
302 auto ret = transformSingle(*what, to_frame);
303 if (ret == std::nullopt)
304 return std::nullopt;
305 else
306 return std::make_shared<T>(std::move(ret.value()));
307 }
308
321 template <class T>
322 [[nodiscard]] std::optional<T> transformSingle(const std::string& from_frame, const T& what, const std::string& to_frame,
323 const rclcpp::Time& time_stamp = rclcpp::Time(0));
324
337 template <class T>
338 [[nodiscard]] std::optional<std::shared_ptr<T>> transformSingle(const std::string& from_frame, const std::shared_ptr<T>& what, const std::string& to_frame,
339 const rclcpp::Time& time_stamp = rclcpp::Time(0)) {
340 return transformSingle(from_frame, std::shared_ptr<const T>(what), to_frame, time_stamp);
341 }
342
355 template <class T>
356 [[nodiscard]] std::optional<std::shared_ptr<T>> transformSingle(const std::string& from_frame, const std::shared_ptr<const T>& what,
357 const std::string& to_frame, const rclcpp::Time& time_stamp = rclcpp::Time(0)) {
358 auto ret = transformSingle(from_frame, *what, to_frame, time_stamp);
359 if (ret == std::nullopt)
360 return std::nullopt;
361 else
362 return std::make_shared<T>(std::move(ret.value()));
363 }
364
365 //}
366
367 /* transform() //{ */
368
377 template <class T>
378 [[nodiscard]] std::optional<T> transform(const T& what, const geometry_msgs::msg::TransformStamped& tf);
379
390 template <class T>
391 [[nodiscard]] std::optional<std::shared_ptr<T>> transform(const std::shared_ptr<const T>& what, const geometry_msgs::msg::TransformStamped& tf) {
392 auto ret = transform(*what, tf);
393 if (ret == std::nullopt)
394 return std::nullopt;
395 else
396 return std::make_shared<T>(std::move(ret.value()));
397 }
398
409 template <class T>
410 [[nodiscard]] std::optional<std::shared_ptr<T>> transform(const std::shared_ptr<T>& what, const geometry_msgs::msg::TransformStamped& tf) {
411 return transform(std::shared_ptr<const T>(what), tf);
412 }
413
414 //}
415
416 /* transformAsVector() method //{ */
427 [[nodiscard]] std::optional<Eigen::Vector3d> transformAsVector(const Eigen::Vector3d& what, const geometry_msgs::msg::TransformStamped& tf);
428
441 [[nodiscard]] std::optional<Eigen::Vector3d> transformAsVector(const std::string& from_frame, const Eigen::Vector3d& what, const std::string& to_frame,
442 const rclcpp::Time& time_stamp = rclcpp::Time(0));
443 //}
444
445 /* transformAsPoint() method //{ */
456 [[nodiscard]] std::optional<Eigen::Vector3d> transformAsPoint(const Eigen::Vector3d& what, const geometry_msgs::msg::TransformStamped& tf);
457
470 [[nodiscard]] std::optional<Eigen::Vector3d> transformAsPoint(const std::string& from_frame, const Eigen::Vector3d& what, const std::string& to_frame,
471 const rclcpp::Time& time_stamp = rclcpp::Time(0));
472 //}
473
474 /* getTransform() //{ */
475
485 [[nodiscard]] std::optional<geometry_msgs::msg::TransformStamped> getTransform(const std::string& from_frame, const std::string& to_frame,
486 const rclcpp::Time& time_stamp = rclcpp::Time(0));
487
503 [[nodiscard]] std::optional<geometry_msgs::msg::TransformStamped> getTransform(const std::string& from_frame, const rclcpp::Time& from_stamp,
504 const std::string& to_frame, const rclcpp::Time& to_stamp,
505 const std::string& fixed_frame);
506
507 //}
508
509private:
510 /* private members, methods etc //{ */
511
512 std::mutex mutex_;
513
514 // keeps track whether a non-basic constructor was called and the transform listener is initialized
515 bool initialized_ = false;
516 rclcpp::Node::SharedPtr node_;
517
518 std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
519 std::unique_ptr<tf2_ros::TransformListener> tf_listener_ptr_;
520
521 // user-configurable options
522 std::string default_frame_id_ = "";
523 std::string prefix_ = ""; // if not empty, includes the forward slash
524 bool quiet_ = false;
525 rclcpp::Duration lookup_timeout_ = rclcpp::Duration(0, 0);
526 bool retry_lookup_newest_ = false;
527
528 bool got_utm_zone_ = false;
529 std::array<char, 10> utm_zone_ = {};
530
531 // returns the first namespace prefix of the frame (if any) includin the forward slash
532 std::string getFramePrefix(const std::string& frame_id);
533
534 template <class T>
535 std::optional<T> transformImpl(const geometry_msgs::msg::TransformStamped& tf, const T& what);
536 std::optional<mrs_msgs::msg::ReferenceStamped> transformImpl(const geometry_msgs::msg::TransformStamped& tf, const mrs_msgs::msg::ReferenceStamped& what);
537 std::optional<Eigen::Vector3d> transformImpl(const geometry_msgs::msg::TransformStamped& tf, const Eigen::Vector3d& what);
538
539 [[nodiscard]] std::optional<geometry_msgs::msg::TransformStamped> getTransformImpl(const std::string& from_frame, const std::string& to_frame,
540 const rclcpp::Time& time_stamp, const std::string& latlon_frame);
541
542 [[nodiscard]] std::optional<geometry_msgs::msg::TransformStamped> getTransformImpl(const std::string& from_frame, const rclcpp::Time& from_stamp,
543 const std::string& to_frame, const rclcpp::Time& to_stamp,
544 const std::string& fixed_frame, const std::string& latlon_frame);
545
546 std::string resolveFrameImpl(const std::string& frame_id);
547
548 template <class T>
549 std::optional<T> doTransform(const T& what, const geometry_msgs::msg::TransformStamped& tf);
550
551 //}
552
553 // | ------------------- some helper methods ------------------ |
554public:
555 /* frame_from(), frame_to() and inverse() methods //{ */
556
563 static constexpr const std::string& frame_from(const geometry_msgs::msg::TransformStamped& msg) {
564 return msg.child_frame_id;
565 }
566
575 static constexpr std::string& frame_from(geometry_msgs::msg::TransformStamped& msg) {
576 return msg.child_frame_id;
577 }
578
585 static constexpr const std::string& frame_to(const geometry_msgs::msg::TransformStamped& msg) {
586 return msg.header.frame_id;
587 }
588
597 static constexpr std::string& frame_to(geometry_msgs::msg::TransformStamped& msg) {
598 return msg.header.frame_id;
599 }
600
607 static geometry_msgs::msg::TransformStamped inverse(const geometry_msgs::msg::TransformStamped& msg) {
608 tf2::Transform tf2;
609 tf2::fromMsg(msg.transform, tf2);
610 tf2 = tf2.inverse();
611 return create_transform(frame_to(msg), frame_from(msg), msg.header.stamp, tf2::toMsg(tf2));
612 }
613 //}
614
615private:
616 /* create_transform() method //{ */
617 static geometry_msgs::msg::TransformStamped create_transform(const std::string& from_frame, const std::string& to_frame, const rclcpp::Time& time_stamp) {
618 geometry_msgs::msg::TransformStamped ret;
619 frame_from(ret) = from_frame;
620 frame_to(ret) = to_frame;
621 ret.header.stamp = time_stamp;
622 return ret;
623 }
624
625 static geometry_msgs::msg::TransformStamped create_transform(const std::string& from_frame, const std::string& to_frame, const rclcpp::Time& time_stamp,
626 const geometry_msgs::msg::Transform& tf) {
627 geometry_msgs::msg::TransformStamped ret;
628 frame_from(ret) = from_frame;
629 frame_to(ret) = to_frame;
630 ret.header.stamp = time_stamp;
631 ret.transform = tf;
632 return ret;
633 }
634 //}
635
636 /* copyChangeFrame() and related methods //{ */
637
638 // helper type and member for detecting whether a message has a header using SFINAE
639 template <typename T>
640 using has_header_member_chk = decltype(std::declval<T&>().header);
641
642 template <typename T>
643 static constexpr bool has_header_member_v = std::experimental::is_detected<has_header_member_chk, T>::value;
644
645 template <typename msg_t>
646 std_msgs::msg::Header getHeader(const msg_t& msg);
647
648#ifdef PCL_SPECIALIZATION
649 template <typename pt_t>
650 std_msgs::msg::Header getHeader(const pcl::PointCloud<pt_t>& cloud);
651
652 template <typename pt_t>
653 void setHeader(pcl::PointCloud<pt_t>& cloud, const std_msgs::msg::Header& header);
654#endif
655
656 template <typename msg_t>
657 void setHeader(msg_t& msg, const std_msgs::msg::Header& header);
658
659 template <typename T>
660 T copyChangeFrame(const T& what, const std::string& frame_id);
661
662 //}
663
664 /* methods for converting between lattitude/longitude and UTM coordinates //{ */
665 geometry_msgs::msg::Point LLtoUTM(const geometry_msgs::msg::Point& what, const std::string& prefix);
666 geometry_msgs::msg::PointStamped LLtoUTM(const geometry_msgs::msg::PointStamped& what, const std::string& prefix);
667 geometry_msgs::msg::Pose LLtoUTM(const geometry_msgs::msg::Pose& what, const std::string& prefix);
668 geometry_msgs::msg::PoseStamped LLtoUTM(const geometry_msgs::msg::PoseStamped& what, const std::string& prefix);
669
670 std::optional<geometry_msgs::msg::Point> UTMtoLL(const geometry_msgs::msg::Point& what, const std::string& prefix);
671 std::optional<geometry_msgs::msg::PointStamped> UTMtoLL(const geometry_msgs::msg::PointStamped& what, const std::string& prefix);
672 std::optional<geometry_msgs::msg::Pose> UTMtoLL(const geometry_msgs::msg::Pose& what, const std::string& prefix);
673 std::optional<geometry_msgs::msg::PoseStamped> UTMtoLL(const geometry_msgs::msg::PoseStamped& what, const std::string& prefix);
674
675 // helper types and member for detecting whether the UTMtoLL and LLtoUTM methods are defined for a certain message
676 template <class Class, typename Message>
677 using UTMLL_method_chk = decltype(std::declval<Class>().UTMtoLL(std::declval<const Message&>(), ""));
678
679 template <class Class, typename Message>
680 using LLUTM_method_chk = decltype(std::declval<Class>().LLtoUTM(std::declval<const Message&>(), ""));
681
682 template <class Class, typename Message>
683 static constexpr bool UTMLL_exists_v =
684 std::experimental::is_detected<UTMLL_method_chk, Class, Message>::value && std::experimental::is_detected<LLUTM_method_chk, Class, Message>::value;
685 //}
686};
687
688//}
689
690} // namespace mrs_lib
691
692#ifndef TRANSFORMER_HPP
693#include <mrs_lib/impl/transformer.hpp>
694#endif
695
696#endif // TRANSFORMER_H
A convenience wrapper class for ROS's native TF2 API to simplify transforming of various messages.
Definition transformer.h:72
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:301
void setDefaultFrame(const std::string &frame_id)
Sets the default frame ID to be used instead of any empty frame ID.
Definition transformer.h:143
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:167
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:585
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:356
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:338
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:607
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:90
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:575
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:410
void setLatLon(const double lat, const double lon)
Sets the curret lattitude and longitude for UTM zone calculation.
Definition transformer.cpp:128
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:193
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:165
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:130
Transformer & operator=(Transformer &&other)
A convenience move assignment operator.
Definition transformer.cpp:66
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:391
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:140
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:563
void setLookupTimeout(const rclcpp::Duration timeout=rclcpp::Duration(0, 0))
Set a timeout for transform lookup.
Definition transformer.h:202
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:597
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:256
void beQuiet(const bool quiet=true)
Enable/disable some prints that may be too noisy.
Definition transformer.h:237
void retryLookupNewest(const bool retry=true)
Enable/disable retry of a failed transform lookup with rclcpp::Time(0).
Definition transformer.h:221
Transformer()
A convenience constructor that doesn't initialize anything.
Definition transformer.cpp:42
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:286
Defines useful geometry utilities and functions.
All mrs_lib functions, classes, variables and definitions are contained in this namespace.
Definition attitude_converter.h:24