TransformPublisherDisplay.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2016, Bielefeld University, CITEC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Willow Garage, Inc. nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * Author: Robert Haschke <rhaschke@techfak.uni-bielefeld.de>
30  */
31 
33 #include "TransformBroadcaster.h"
34 #include "rotation_property.h"
35 
42 
43 #include <rviz/display_factory.h>
44 #include <rviz/display_context.h>
45 #include <rviz/frame_manager.h>
49 
50 namespace vm = visualization_msgs;
51 const std::string MARKER_NAME = "marker";
52 
54 
55 namespace agni_tf_tools {
56 
57 void static updatePose(geometry_msgs::Pose& pose,
58  const Eigen::Quaterniond& q,
59  Ogre::Vector3 p = Ogre::Vector3::ZERO) {
60  pose.orientation.w = q.w();
61  pose.orientation.x = q.x();
62  pose.orientation.y = q.y();
63  pose.orientation.z = q.z();
64 
65  pose.position.x = p.x;
66  pose.position.y = p.y;
67  pose.position.z = p.z;
68 }
69 
70 
71 TransformPublisherDisplay::TransformPublisherDisplay() : rviz::Display(), ignore_updates_(false) {
72  translation_property_ = new rviz::VectorProperty("translation", Ogre::Vector3::ZERO, "", this);
73  rotation_property_ = new RotationProperty(this, "rotation");
74 
77  nullptr, true, SLOT(onRefFrameChanged()), this);
79  new rviz::BoolProperty("adapt transformation", false,
80  "Adapt transformation when changing the parent frame? "
81  "If so, the marker will not move.",
82  this, SLOT(onAdaptTransformChanged()), this);
84 
85  broadcast_property_ = new rviz::BoolProperty("publish transform", true, "", this,
86  SLOT(onBroadcastEnableChanged()), this);
87  child_frame_property_ = new rviz::TfFrameProperty("child frame", "", "", broadcast_property_, nullptr,
88  false, SLOT(onFramesChanged()), this);
89 
96  tf_pub_ = new TransformBroadcaster("", "", this);
97  tf_pub_->setEnabled(false); // only enable with display
98 
99  marker_property_ = new rviz::EnumProperty("marker type", "interactive frame",
100  "Choose which type of interactive marker to show", this,
101  SLOT(onMarkerTypeChanged()), this);
102  marker_property_->addOption("none", NONE);
103  marker_property_->addOption("static frame", FRAME);
104  marker_property_->addOption("interactive frame", IFRAME);
105  marker_property_->addOption("6 DoF handles", DOF6);
106 
107  marker_scale_property_ = new rviz::FloatProperty("marker scale", 0.2, "", marker_property_,
108  SLOT(onMarkerScaleChanged()), this);
109 }
110 
112  context_->getTF2BufferPtr()->removeTransformableCallback(tf_callback_handle_);
113 }
114 
116  Display::onInitialize();
119  tf_callback_handle_ = context_->getTF2BufferPtr()->addTransformableCallback(
120  boost::bind(&TransformPublisherDisplay::onFramesChanged, this));
121 
122  // show some children by default
123  this->expand();
125 }
126 
128  Display::reset();
129 }
130 
132  Display::onEnable();
133  onFramesChanged();
135 }
136 
138  Display::onDisable();
141 }
142 
143 void TransformPublisherDisplay::update(float wall_dt, float ros_dt) {
144  if (!this->isEnabled())
145  return;
146 
147  Display::update(wall_dt, ros_dt);
148  // create marker if not yet done
149  if (!imarker_ && marker_property_->getOptionInt() != NONE &&
152  else if (imarker_)
153  imarker_->update(wall_dt); // get online marker updates
154 }
155 
156 
157 static vm::Marker createArrowMarker(double scale, const Eigen::Vector3d& dir, const QColor& color) {
158  vm::Marker marker;
159 
160  marker.type = vm::Marker::ARROW;
161  marker.scale.x = scale;
162  marker.scale.y = 0.1 * scale;
163  marker.scale.z = 0.1 * scale;
164 
165  updatePose(marker.pose, Eigen::Quaterniond::FromTwoVectors(Eigen::Vector3d::UnitX(), dir));
166 
167  marker.color.r = color.redF();
168  marker.color.g = color.greenF();
169  marker.color.b = color.blueF();
170  marker.color.a = color.alphaF();
171 
172  return marker;
173 }
174 
175 inline void setOrientation(geometry_msgs::Quaternion& q, double w, double x, double y, double z) {
176  q.w = w;
177  q.x = x;
178  q.y = y;
179  q.z = z;
180 }
181 
182 void TransformPublisherDisplay::addFrameControls(vm::InteractiveMarker& im,
183  double scale,
184  bool interactive) {
185  vm::InteractiveMarkerControl ctrl;
186  setOrientation(ctrl.orientation, 1, 0, 0, 0);
187  ctrl.always_visible = true;
188  if (interactive) {
189  ctrl.orientation_mode = vm::InteractiveMarkerControl::VIEW_FACING;
190  ctrl.independent_marker_orientation = true;
191  ctrl.interaction_mode = vm::InteractiveMarkerControl::MOVE_ROTATE_3D;
192  }
193  ctrl.name = "frame";
194 
195  ctrl.markers.push_back(createArrowMarker(im.scale * scale, Eigen::Vector3d::UnitX(), QColor("red")));
196  ctrl.markers.push_back(createArrowMarker(im.scale * scale, Eigen::Vector3d::UnitY(), QColor("green")));
197  ctrl.markers.push_back(createArrowMarker(im.scale * scale, Eigen::Vector3d::UnitZ(), QColor("blue")));
198 
199  im.controls.push_back(ctrl);
200 }
201 
202 void TransformPublisherDisplay::add6DOFControls(vm::InteractiveMarker& im) {
203  vm::InteractiveMarkerControl ctrl;
204  ctrl.always_visible = false;
205 
206  setOrientation(ctrl.orientation, 1, 1, 0, 0);
207  ctrl.interaction_mode = vm::InteractiveMarkerControl::MOVE_AXIS;
208  ctrl.name = "x pos";
209  im.controls.push_back(ctrl);
210  ctrl.interaction_mode = vm::InteractiveMarkerControl::ROTATE_AXIS;
211  ctrl.name = "x rot";
212  im.controls.push_back(ctrl);
213 
214  setOrientation(ctrl.orientation, 1, 0, 1, 0);
215  ctrl.interaction_mode = vm::InteractiveMarkerControl::MOVE_AXIS;
216  ctrl.name = "y pos";
217  im.controls.push_back(ctrl);
218  ctrl.interaction_mode = vm::InteractiveMarkerControl::ROTATE_AXIS;
219  ctrl.name = "y rot";
220  im.controls.push_back(ctrl);
221 
222  setOrientation(ctrl.orientation, 1, 0, 0, 1);
223  ctrl.interaction_mode = vm::InteractiveMarkerControl::MOVE_AXIS;
224  ctrl.name = "z pos";
225  im.controls.push_back(ctrl);
226  ctrl.interaction_mode = vm::InteractiveMarkerControl::ROTATE_AXIS;
227  ctrl.name = "z rot";
228  im.controls.push_back(ctrl);
229 }
230 
232  if (type == NONE || !isEnabled()) {
233  if (imarker_)
234  imarker_.reset();
235  return true;
236  }
237 
238  float scale = marker_scale_property_->getFloat();
239 
240  vm::InteractiveMarker im;
241  im.name = MARKER_NAME;
242  im.scale = scale;
243  if (!fillPoseStamped(im.header, im.pose))
244  return false;
245 
246  if (type == FRAME || type == IFRAME)
247  addFrameControls(im, 1.0, type == IFRAME);
248  else if (type == DOF6) {
249  addFrameControls(im, 0.5, type == IFRAME);
250  add6DOFControls(im);
251  }
252 
254  connect(imarker_.get(), &rviz::InteractiveMarker::userFeedback, this,
256  connect(imarker_.get(), &rviz::InteractiveMarker::statusUpdate, this,
258 
260 
261  // fill in default controls
263 
264  imarker_->processMessage(im);
265  imarker_->setShowVisualAids(false);
266  imarker_->setShowAxes(false);
267  imarker_->setShowDescription(false);
268 
269  return true;
270 }
271 
272 
273 bool TransformPublisherDisplay::fillPoseStamped(std_msgs::Header& header, geometry_msgs::Pose& pose) {
274  const std::string& parent_frame = parent_frame_property_->getFrameStd();
275  std::string error;
276  if (context_->getFrameManager()->transformHasProblems(parent_frame, ros::Time(), error)) {
277  // on failure, listen to TF changes
278  auto tf = context_->getTF2BufferPtr();
279  tf->cancelTransformableRequest(tf_request_handle_);
280  tf_request_handle_ = tf->addTransformableRequest(tf_callback_handle_, fixed_frame_.toStdString(),
281  parent_frame, ros::Time());
283  return false;
284  }
286 
287  const Eigen::Quaterniond& q = rotation_property_->getQuaternion();
288  const Ogre::Vector3& p = translation_property_->getVector();
289  updatePose(pose, q, p);
290  header.frame_id = parent_frame;
291  // frame-lock marker to update marker pose with frame updates
292  header.stamp = ros::Time();
293  return true;
294 }
295 
297  const QString& name,
298  const QString& text) {
299  if (level == rviz::StatusProperty::Ok && text.isEmpty()) {
300  Display::setStatus(level, name, text);
301  Display::deleteStatus(name);
302  } else
303  Display::setStatus(level, name, text);
304 }
305 
307  const std::string& name,
308  const std::string& text) {
309  setStatus(level, QString::fromStdString(name), QString::fromStdString(text));
310 }
311 
312 static bool getTransform(rviz::FrameManager& fm, const std::string& frame, Eigen::Affine3d& tf) {
313  Ogre::Vector3 p = Ogre::Vector3::ZERO;
314  Ogre::Quaternion q = Ogre::Quaternion::IDENTITY;
315 
316  bool success = fm.getTransform(frame, ros::Time(), p, q);
317  tf = Eigen::Translation3d(p.x, p.y, p.z) * Eigen::Quaterniond(q.w, q.x, q.y, q.z);
318  return success || frame == rviz::TfFrameProperty::FIXED_FRAME_STRING.toStdString();
319 }
320 
322  // update pose to be relative to new reference frame
323  Eigen::Affine3d prevRef, nextRef;
325  if (getTransform(fm, prev_parent_frame_, prevRef) &&
327  const Ogre::Vector3& p = translation_property_->getVector();
328  Eigen::Affine3d curPose = Eigen::Translation3d(p.x, p.y, p.z) * rotation_property_->getQuaternion();
329  Eigen::Affine3d newPose = nextRef.inverse() * prevRef * curPose;
330  Eigen::Vector3d t = newPose.translation();
331  ignore_updates_ = true;
332  translation_property_->setVector(Ogre::Vector3(t.x(), t.y(), t.z()));
333  rotation_property_->setQuaternion(Eigen::Quaterniond(newPose.rotation()));
334  ignore_updates_ = false;
335  }
337  onFramesChanged();
338 }
339 
343  else
344  prev_parent_frame_ = "";
345 }
346 
348  // update marker pose
349  vm::InteractiveMarkerPose marker_pose;
350  if (!fillPoseStamped(marker_pose.header, marker_pose.pose))
351  return;
352  if (imarker_)
353  imarker_->processMessage(marker_pose);
354 
355  // prepare transform for broadcasting
356  geometry_msgs::TransformStamped tf;
357  tf.header.frame_id = parent_frame_property_->getFrameStd();
358  tf.child_frame_id = child_frame_property_->getFrameStd();
359  tf.transform.translation.x = marker_pose.pose.position.x;
360  tf.transform.translation.y = marker_pose.pose.position.y;
361  tf.transform.translation.z = marker_pose.pose.position.z;
362  tf.transform.rotation = marker_pose.pose.orientation;
363  tf_pub_->setValue(tf);
364 }
365 
367  if (ignore_updates_)
368  return;
369 
370  vm::InteractiveMarkerPose marker_pose;
371  if (!fillPoseStamped(marker_pose.header, marker_pose.pose))
372  return;
373 
374  // update marker pose + broadcast pose
375  ignore_updates_ = true;
376  if (imarker_)
377  imarker_->processMessage(marker_pose);
378  ignore_updates_ = false;
379  tf_pub_->setPose(marker_pose.pose);
380 }
381 
382 void TransformPublisherDisplay::onMarkerFeedback(vm::InteractiveMarkerFeedback& feedback) {
383  if (ignore_updates_)
384  return;
385  if (feedback.event_type != vm::InteractiveMarkerFeedback::POSE_UPDATE)
386  return;
387 
388  // convert feedpack.pose to parent frame
389  geometry_msgs::Pose pose;
390  try {
391  tf2::doTransform(feedback.pose, pose,
392  context_->getTF2BufferPtr()->lookupTransform(parent_frame_property_->getFrameStd(),
393  feedback.header.frame_id,
394  feedback.header.stamp));
395  } catch (const std::runtime_error& e) {
396  ROS_DEBUG("Error transforming from frame '%s' to frame '%s': %s", feedback.header.frame_id.c_str(),
397  parent_frame_property_->getFrameStd().c_str(), e.what());
398  return;
399  }
400 
401  const geometry_msgs::Point& p = pose.position;
402  const geometry_msgs::Quaternion& q = pose.orientation;
403 
404  ignore_updates_ = true;
405  translation_property_->setVector(Ogre::Vector3(p.x, p.y, p.z));
406  rotation_property_->setQuaternion(Eigen::Quaterniond(q.w, q.x, q.y, q.z));
407  ignore_updates_ = false;
408 
410  tf_pub_->setPose(feedback.pose);
411 }
412 
415 }
416 
419 }
420 
422  if (marker_scale_property_->getFloat() <= 0)
425 }
426 
427 } // namespace agni_tf_tools
const std::string MARKER_NAME
virtual void expand()
virtual bool setVector(const Ogre::Vector3 &vector)
bool transformHasProblems(const std::string &frame, ros::Time time, std::string &error)
void setStatus(rviz::StatusProperty::Level level, const QString &name, const QString &text) override
DisplayContext * context_
Ogre::SceneNode * getSceneNode() const
virtual float getFloat() const
tf2::TransformableCallbackHandle tf_callback_handle_
void quaternionChanged(Eigen::Quaterniond q)
void add6DOFControls(visualization_msgs::InteractiveMarker &im)
INTERACTIVE_MARKERS_PUBLIC void autoComplete(visualization_msgs::InteractiveMarker &msg, bool enable_autocomplete_transparency=true)
static vm::Marker createArrowMarker(double scale, const Eigen::Vector3d &dir, const QColor &color)
void doTransform(const T &data_in, T &data_out, const geometry_msgs::TransformStamped &transform)
geometry_msgs::TransformStamped t
void setOrientation(geometry_msgs::Quaternion &q, double w, double x, double y, double z)
void onMarkerFeedback(visualization_msgs::InteractiveMarkerFeedback &feedback)
bool isEnabled() const
virtual bool getBool() const
QString fixed_frame_
bool setFloat(float new_value)
tf2::TransformableRequestHandle tf_request_handle_
Eigen::Quaterniond getQuaternion() const
virtual void addOption(const QString &option, int value=0)
std::string getFrameStd() const
void update(float wall_dt, float ros_dt) override
boost::shared_ptr< rviz::InteractiveMarker > imarker_
void setQuaternion(const Eigen::Quaterniond &q)
virtual FrameManager * getFrameManager() const =0
void statusUpdate(rviz::StatusProperty::Level, const QString &, const QString &)
void addFrameControls(visualization_msgs::InteractiveMarker &im, double scale, bool interactive)
void userFeedback(visualization_msgs::InteractiveMarkerFeedback &feedback)
void setEnabled(bool bEnabled=true)
void setStatusStd(rviz::StatusProperty::Level, const std::string &name, const std::string &text)
virtual Ogre::Vector3 getVector() const
static const QString FIXED_FRAME_STRING
static bool getTransform(rviz::FrameManager &fm, const std::string &frame, Eigen::Affine3d &tf)
void statusUpdate(StatusProperty::Level level, const std::string &name, const std::string &text)
bool getTransform(const Header &header, Ogre::Vector3 &position, Ogre::Quaternion &orientation)
void setFrameManager(FrameManager *frame_manager)
void setPose(const geometry_msgs::Pose &pose)
static void updatePose(geometry_msgs::Pose &pose, const Eigen::Quaterniond &q, Ogre::Vector3 p=Ogre::Vector3::ZERO)
void setValue(const geometry_msgs::TransformStamped &tf)
virtual int getOptionInt()
#define ROS_DEBUG(...)
bool fillPoseStamped(std_msgs::Header &header, geometry_msgs::Pose &pose)


agni_tf_tools
Author(s): Robert Haschke
autogenerated on Tue Apr 13 2021 02:29:55