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