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