mrs_lib
Various reusable classes, functions and utilities for use in MRS projects
Loading...
Searching...
No Matches
transformer.hpp
1#ifndef TRANSFORMER_HPP
2#define TRANSFORMER_HPP
3
4#ifndef TRANSFORMER_H
5#include <mrs_lib/transformer.h>
6#endif
7
8namespace mrs_lib
9{
10
11// | --------------------- helper methods --------------------- |
12
13/* getHeader() overloads for different message types (pointers, pointclouds etc) //{ */
14
15template <typename msg_t>
16std_msgs::msg::Header Transformer::getHeader(const msg_t& msg) {
17 return msg.header;
18}
19
20#ifdef PCL_SPECIALIZATION
21template <typename pt_t>
22std_msgs::msg::Header Transformer::getHeader(const pcl::PointCloud<pt_t>& cloud) {
23 std_msgs::msg::Header ret;
24 pcl_conversions::fromPCL(cloud.header, ret);
25 return ret;
26}
27#endif
28
29//}
30
31/* setHeader() overloads for different message types (pointers, pointclouds etc) //{ */
32
33template <typename msg_t>
34void Transformer::setHeader(msg_t& msg, const std_msgs::msg::Header& header) {
35 msg.header = header;
36}
37
38#ifdef PCL_SPECIALIZATION
39template <typename pt_t>
40void Transformer::setHeader(pcl::PointCloud<pt_t>& cloud, const std_msgs::msg::Header& header) {
41 pcl_conversions::toPCL(header, cloud.header);
42}
43#endif
44
45//}
46
47/* copyChangeFrame() helper function //{ */
48
49template <typename T>
50T Transformer::copyChangeFrame(const T& what, const std::string& frame_id) {
51 T ret = what;
52 if constexpr (has_header_member_v<T>) {
53 std_msgs::msg::Header new_header = getHeader(what);
54 new_header.frame_id = frame_id;
55 setHeader(ret, new_header);
56 }
57 return ret;
58}
59
60//}
61
62/* transformImpl() //{ */
63
64template <class T>
65std::optional<T> Transformer::transformImpl(const geometry_msgs::msg::TransformStamped& tf, const T& what) {
66
67 const std::string from_frame = frame_from(tf);
68 const std::string to_frame = frame_to(tf);
69
70 if (from_frame == to_frame)
71 return copyChangeFrame(what, from_frame);
72
73 const std::string latlon_frame_name = resolveFrameImpl(LATLON_ORIGIN);
74
75 // First, check if the transformation is from/to the latlon frame
76 // if conversion between UVM and LatLon coordinates is defined for this message, it may be resolved
77 if constexpr (UTMLL_exists_v<Transformer, T>) {
78 // check for transformation from LAT-LON GPS
79 if (from_frame == latlon_frame_name) {
80 const std::optional<T> tmp = LLtoUTM(what, getFramePrefix(from_frame));
81 if (!tmp.has_value())
82 return std::nullopt;
83 return doTransform(tmp.value(), tf);
84 }
85 // check for transformation to LAT-LON GPS
86 else if (to_frame == latlon_frame_name) {
87 const std::optional<T> tmp = doTransform(what, tf);
88 if (!tmp.has_value())
89 return std::nullopt;
90 return UTMtoLL(tmp.value(), getFramePrefix(to_frame));
91 }
92 } else {
93 // by default, transformation from/to LATLON is undefined, so return nullopt if it's attempted
94 if (from_frame == latlon_frame_name || to_frame == latlon_frame_name) {
95 RCLCPP_ERROR_STREAM_THROTTLE(node_->get_logger(), *node_->get_clock(), 1e9,
96 "Transformer: cannot transform message of this type (" << typeid(T).name() << ") to/from latitude/longitude coordinates!");
97 return std::nullopt;
98 }
99 }
100
101 // otherwise it's just an ol' borin' transformation
102 return doTransform(what, tf);
103}
104
105//}
106
107/* doTransform() //{ */
108
109template <class T>
110std::optional<T> Transformer::doTransform(const T& what, const geometry_msgs::msg::TransformStamped& tf) {
111 try {
112 T result;
113 tf2::doTransform(what, result, tf);
114 return result;
115 }
116 catch (tf2::TransformException& ex) {
117 RCLCPP_WARN_THROTTLE(node_->get_logger(), *node_->get_clock(), 1e9, "Transformer: Error during transform from \"%s\" frame to \"%s\" frame.\n\tMSG: %s",
118 frame_from(tf).c_str(), frame_to(tf).c_str(), ex.what());
119 return std::nullopt;
120 }
121}
122
123//}
124
125// | ------------------ user-callable methods ----------------- |
126
127/* transformSingle() //{ */
128
129template <class T>
130std::optional<T> Transformer::transformSingle(const T& what, const std::string& to_frame_raw) {
131 const std_msgs::msg::Header orig_header = getHeader(what);
132 return transformSingle(orig_header.frame_id, what, to_frame_raw, orig_header.stamp);
133}
134
135template <class T>
136std::optional<T> Transformer::transformSingle(const std::string& from_frame_raw, const T& what, const std::string& to_frame_raw,
137 const rclcpp::Time& time_stamp) {
138 std::scoped_lock lck(mutex_);
139
140 if (!initialized_) {
141 RCLCPP_ERROR_THROTTLE(node_->get_logger(), *node_->get_clock(), 1e9, "Transformer: cannot transform, not initialized");
142 return std::nullopt;
143 }
144
145 const std::string from_frame = resolveFrameImpl(from_frame_raw);
146 const std::string to_frame = resolveFrameImpl(to_frame_raw);
147 const std::string latlon_frame = resolveFrameImpl(LATLON_ORIGIN);
148
149 // get the transform
150 const auto tf_opt = getTransformImpl(from_frame, to_frame, time_stamp, latlon_frame);
151 if (!tf_opt.has_value())
152 return std::nullopt;
153
154 const geometry_msgs::msg::TransformStamped& tf = tf_opt.value();
155
156 // do the transformation
157 const geometry_msgs::msg::TransformStamped tf_resolved = create_transform(from_frame, to_frame, tf.header.stamp, tf.transform);
158
159 return transformImpl(tf_resolved, what);
160}
161
162//}
163
164/* transform() //{ */
165
166template <class T>
167std::optional<T> Transformer::transform(const T& what, const geometry_msgs::msg::TransformStamped& tf) {
168 std::scoped_lock lck(mutex_);
169
170 if (!initialized_) {
171 RCLCPP_ERROR_THROTTLE(node_->get_logger(), *node_->get_clock(), 1e9, "Transformer: cannot transform, not initialized");
172 return std::nullopt;
173 }
174
175 const std::string from_frame = resolveFrameImpl(frame_from(tf));
176 const std::string to_frame = resolveFrameImpl(frame_to(tf));
177 const geometry_msgs::msg::TransformStamped tf_resolved = create_transform(from_frame, to_frame, tf.header.stamp, tf.transform);
178
179 return transformImpl(tf_resolved, what);
180}
181
182/* //} */
183
184} // namespace mrs_lib
185
186#endif // TRANSFORMER_HPP
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< 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
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
All mrs_lib functions, classes, variables and definitions are contained in this namespace.
Definition attitude_converter.h:24