moveit2
The MoveIt Motion Planning Framework for ROS 2.
robot_interaction.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2012-2013, Willow Garage, Inc.
5  * Copyright (c) 2013, Ioan A. Sucan
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of Willow Garage nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *********************************************************************/
35 
36 /* Author: Ioan Sucan, Adam Leeper */
37 
43 #include <interactive_markers/interactive_marker_server.hpp>
44 #include <interactive_markers/menu_handler.hpp>
45 #include <tf2_eigen/tf2_eigen.hpp>
46 #include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
47 // TODO: Remove conditional include when released to all active distros.
48 #if __has_include(<tf2/LinearMath/Transform.hpp>)
49 #include <tf2/LinearMath/Transform.hpp>
50 #else
51 #include <tf2/LinearMath/Transform.h>
52 #endif
53 
54 #include <algorithm>
55 #include <limits>
56 #include <string>
57 
58 #include <Eigen/Core>
59 #include <Eigen/Geometry>
60 
61 namespace robot_interaction
62 {
63 static const rclcpp::Logger LOGGER = rclcpp::get_logger("moveit_ros_robot_interaction.robot_interaction");
64 static const float END_EFFECTOR_UNREACHABLE_COLOR[4] = { 1.0, 0.3, 0.3, 1.0 };
65 static const float END_EFFECTOR_REACHABLE_COLOR[4] = { 0.2, 1.0, 0.2, 1.0 };
66 
67 const std::string RobotInteraction::INTERACTIVE_MARKER_TOPIC = "robot_interaction_interactive_marker_topic";
68 
69 RobotInteraction::RobotInteraction(const moveit::core::RobotModelConstPtr& robot_model,
70  const rclcpp::Node::SharedPtr& node, const std::string& ns)
71  : robot_model_(robot_model), kinematic_options_map_(std::make_shared<KinematicOptionsMap>())
72 {
73  topic_ = ns.empty() ? INTERACTIVE_MARKER_TOPIC : ns + "/" + INTERACTIVE_MARKER_TOPIC;
74  node_ = node;
75  int_marker_server_ = new interactive_markers::InteractiveMarkerServer(topic_, node_);
76 
77  // spin a thread that will process feedback events
78  run_processing_thread_ = true;
79  processing_thread_ = std::make_unique<std::thread>([this] { processingThread(); });
80 }
81 
83 {
84  run_processing_thread_ = false;
85  new_feedback_condition_.notify_all();
86  processing_thread_->join();
87 
88  clear();
89  delete int_marker_server_;
90 }
91 
93 {
95 }
96 
98 {
99  decideActiveEndEffectors(group, style);
100  decideActiveJoints(group);
101  if (!group.empty() && active_eef_.empty() && active_vj_.empty() && active_generic_.empty())
102  RCLCPP_INFO(LOGGER,
103  "No active joints or end effectors found for group '%s'. "
104  "Make sure that kinematics.yaml is loaded in this node's namespace.",
105  group.c_str());
106 }
107 
109  const ProcessFeedbackFn& process, const InteractiveMarkerUpdateFn& update,
110  const std::string& name)
111 {
112  std::unique_lock<std::mutex> ulock(marker_access_lock_);
114  g.construct_marker = construct;
115  g.update_pose = update;
116  g.process_feedback = process;
117  // compute the suffix that will be added to the generated markers
118  g.marker_name_suffix = "_" + name + "_" + std::to_string(active_generic_.size());
119  active_generic_.push_back(g);
120 }
121 
122 static const double DEFAULT_SCALE = 0.25;
123 double RobotInteraction::computeLinkMarkerSize(const std::string& link)
124 {
125  const moveit::core::LinkModel* lm = robot_model_->getLinkModel(link);
126  double size = 0;
127 
128  while (lm)
129  {
131  // drop largest extension and take norm of two remaining
132  Eigen::MatrixXd::Index max_index;
133  ext.maxCoeff(&max_index);
134  ext[max_index] = 0;
135  size = 1.01 * ext.norm();
136  if (size > 0)
137  break; // break, if a non-empty shape was found
138 
139  // process kinematic chain upwards (but only following fixed joints)
140  // to find a link with some non-empty shape (to ignore virtual links)
142  lm = lm->getParentLinkModel();
143  else
144  lm = nullptr;
145  }
146  if (!lm)
147  return DEFAULT_SCALE; // no link with non-zero shape extends found
148 
149  // the marker sphere will be half the size, so double the size here
150  return 2. * size;
151 }
152 
153 double RobotInteraction::computeGroupMarkerSize(const std::string& group)
154 {
155  if (group.empty())
156  return DEFAULT_SCALE;
157  const moveit::core::JointModelGroup* jmg = robot_model_->getJointModelGroup(group);
158  if (!jmg)
159  return 0.0;
160 
161  const std::vector<std::string>& links = jmg->getLinkModelNames();
162  if (links.empty())
163  return DEFAULT_SCALE;
164 
165  // compute the aabb of the links that make up the group
166  double size = 0;
167  for (const std::string& link : links)
168  {
169  const moveit::core::LinkModel* lm = robot_model_->getLinkModel(link);
170  if (!lm)
171  continue;
173 
174  // drop largest extension and take norm of two remaining
175  Eigen::MatrixXd::Index max_index;
176  ext.maxCoeff(&max_index);
177  ext[max_index] = 0;
178  size = std::max(size, 1.01 * ext.norm());
179  }
180 
181  // if size is zero, all links have empty shapes and are placed at same position
182  // in this case, fall back to link marker size
183  if (size == 0)
184  return computeLinkMarkerSize(links[0]);
185 
186  // the marker sphere will be half the size, so double the size here
187  return 2. * size;
188 }
189 
190 void RobotInteraction::decideActiveJoints(const std::string& group)
191 {
192  std::unique_lock<std::mutex> ulock(marker_access_lock_);
193  active_vj_.clear();
194 
195  if (group.empty())
196  return;
197 
198  RCLCPP_DEBUG(LOGGER, "Deciding active joints for group '%s'", group.c_str());
199 
200  const srdf::ModelConstSharedPtr& srdf = robot_model_->getSRDF();
201  const moveit::core::JointModelGroup* jmg = robot_model_->getJointModelGroup(group);
202 
203  if (!jmg || !srdf)
204  return;
205 
206  std::set<std::string> used;
207  if (jmg->hasJointModel(robot_model_->getRootJointName()))
208  {
209  moveit::core::RobotState default_state(robot_model_);
210  default_state.setToDefaultValues();
211  std::vector<double> aabb;
212  default_state.computeAABB(aabb);
213 
214  const std::vector<srdf::Model::VirtualJoint>& vj = srdf->getVirtualJoints();
215  for (const srdf::Model::VirtualJoint& joint : vj)
216  if (joint.name_ == robot_model_->getRootJointName())
217  {
218  if (joint.type_ == "planar" || joint.type_ == "floating")
219  {
220  JointInteraction v;
221  v.connecting_link = joint.child_link_;
222  v.parent_frame = joint.parent_frame_;
223  if (!v.parent_frame.empty() && v.parent_frame[0] == '/')
224  v.parent_frame = v.parent_frame.substr(1);
225  v.joint_name = joint.name_;
226  if (joint.type_ == "planar")
227  v.dof = 3;
228  else
229  v.dof = 6;
230  // take the max of the X, Y, Z extent
231  v.size = std::max(std::max(aabb[1] - aabb[0], aabb[3] - aabb[2]), aabb[5] - aabb[4]);
232  active_vj_.push_back(v);
233  used.insert(v.joint_name);
234  }
235  }
236  }
237 
238  const std::vector<const moveit::core::JointModel*>& joints = jmg->getJointModels();
239  for (const moveit::core::JointModel* joint : joints)
240  {
241  if ((joint->getType() == moveit::core::JointModel::PLANAR ||
242  joint->getType() == moveit::core::JointModel::FLOATING) &&
243  used.find(joint->getName()) == used.end())
244  {
245  JointInteraction v;
246  v.connecting_link = joint->getChildLinkModel()->getName();
247  if (joint->getParentLinkModel())
248  v.parent_frame = joint->getParentLinkModel()->getName();
249  v.joint_name = joint->getName();
250  if (joint->getType() == moveit::core::JointModel::PLANAR)
251  v.dof = 3;
252  else
253  v.dof = 6;
254  // take the max of the X, Y, Z extent
255  v.size = computeGroupMarkerSize(group);
256  active_vj_.push_back(v);
257  }
258  }
259 }
260 
261 void RobotInteraction::decideActiveEndEffectors(const std::string& group)
262 {
263  decideActiveEndEffectors(group, InteractionStyle::SIX_DOF);
264 }
265 
266 void RobotInteraction::decideActiveEndEffectors(const std::string& group, InteractionStyle::InteractionStyle style)
267 {
268  std::unique_lock<std::mutex> ulock(marker_access_lock_);
269  active_eef_.clear();
270 
271  if (group.empty())
272  return;
273 
274  RCLCPP_DEBUG(LOGGER, "Deciding active end-effectors for group '%s'", group.c_str());
275 
276  const srdf::ModelConstSharedPtr& srdf = robot_model_->getSRDF();
277  const moveit::core::JointModelGroup* jmg = robot_model_->getJointModelGroup(group);
278 
279  if (!jmg || !srdf)
280  {
281  RCLCPP_WARN(LOGGER, "Unable to decide active end effector: no joint model group or no srdf model");
282  return;
283  }
284 
285  const std::vector<srdf::Model::EndEffector>& eefs = srdf->getEndEffectors();
286  const std::pair<moveit::core::JointModelGroup::KinematicsSolver, moveit::core::JointModelGroup::KinematicsSolverMap>&
287  smap = jmg->getGroupKinematics();
288 
289  auto add_active_end_effectors_for_single_group = [&](const moveit::core::JointModelGroup* single_group) {
290  bool found_eef{ false };
291  for (const srdf::Model::EndEffector& eef : eefs)
292  {
293  if (single_group->hasLinkModel(eef.parent_link_) &&
294  (eef.parent_group_.empty() || single_group->getName() == eef.parent_group_) &&
295  single_group->canSetStateFromIK(eef.parent_link_))
296  {
297  // We found an end-effector whose parent is the group.
298  EndEffectorInteraction ee;
299  ee.parent_group = single_group->getName();
300  ee.parent_link = eef.parent_link_;
301  ee.eef_group = eef.component_group_;
302  ee.interaction = style;
303  active_eef_.push_back(ee);
304  found_eef = true;
305  }
306  }
307 
308  // No end effectors found. Use last link in group as the "end effector".
309  if (!found_eef && !single_group->getLinkModelNames().empty())
310  {
311  std::string last_link{ single_group->getLinkModelNames().back() };
312 
313  if (single_group->canSetStateFromIK(last_link))
314  {
315  EndEffectorInteraction ee;
316  ee.parent_group = single_group->getName();
317  ee.parent_link = last_link;
318  ee.eef_group = single_group->getName();
319  ee.interaction = style;
320  active_eef_.push_back(ee);
321  }
322  }
323  };
324 
325  // if we have an IK solver for the selected group, we check if there are any end effectors attached to this group
326  if (smap.first)
327  {
328  add_active_end_effectors_for_single_group(jmg);
329  }
330  // if the group contains subgroups with IK, add markers for them individually
331  else if (!smap.second.empty())
332  {
333  for (const std::pair<const moveit::core::JointModelGroup* const, moveit::core::JointModelGroup::KinematicsSolver>&
334  it : smap.second)
335  add_active_end_effectors_for_single_group(it.first);
336  }
337 
338  // lastly determine automatic marker sizes
339  for (EndEffectorInteraction& eef : active_eef_)
340  {
341  // if we have a separate group for the eef, we compute the scale based on it;
342  // otherwise, we use the size of the parent_link
343  eef.size = (eef.eef_group == eef.parent_group) ? computeLinkMarkerSize(eef.parent_link) :
344  computeGroupMarkerSize(eef.eef_group);
345  RCLCPP_DEBUG(LOGGER, "Found active end-effector '%s', of scale %lf", eef.eef_group.c_str(), eef.size);
346  }
347  // if there is only a single end effector marker, we can safely use a larger marker
348  if (active_eef_.size() == 1)
349  active_eef_[0].size *= 1.5;
350 }
351 
353 {
354  std::unique_lock<std::mutex> ulock(marker_access_lock_);
355  active_eef_.clear();
356  active_vj_.clear();
357  active_generic_.clear();
358  clearInteractiveMarkersUnsafe();
360 }
361 
363 {
364  std::unique_lock<std::mutex> ulock(marker_access_lock_);
365  clearInteractiveMarkersUnsafe();
366 }
367 
368 void RobotInteraction::clearInteractiveMarkersUnsafe()
369 {
370  handlers_.clear();
371  shown_markers_.clear();
372  int_marker_move_subscribers_.clear();
373  int_marker_move_topics_.clear();
374  int_marker_names_.clear();
375  int_marker_server_->clear();
376 }
377 
378 void RobotInteraction::addEndEffectorMarkers(const InteractionHandlerPtr& handler, const EndEffectorInteraction& eef,
379  visualization_msgs::msg::InteractiveMarker& im, bool position,
380  bool orientation)
381 {
382  geometry_msgs::msg::Pose pose;
383  pose.orientation.w = 1;
384  addEndEffectorMarkers(handler, eef, pose, im, position, orientation);
385 }
386 
387 void RobotInteraction::addEndEffectorMarkers(const InteractionHandlerPtr& handler, const EndEffectorInteraction& eef,
388  const geometry_msgs::msg::Pose& im_to_eef,
389  visualization_msgs::msg::InteractiveMarker& im, bool position,
390  bool orientation)
391 {
392  if (eef.parent_group == eef.eef_group || !robot_model_->hasLinkModel(eef.parent_link))
393  return;
394 
395  visualization_msgs::msg::InteractiveMarkerControl m_control;
396  m_control.always_visible = false;
397  if (position && orientation)
398  m_control.interaction_mode = m_control.MOVE_ROTATE_3D;
399  else if (orientation)
400  m_control.interaction_mode = m_control.ROTATE_3D;
401  else
402  m_control.interaction_mode = m_control.MOVE_3D;
403 
404  std_msgs::msg::ColorRGBA marker_color;
405  const float* color = handler->inError(eef) ? END_EFFECTOR_UNREACHABLE_COLOR : END_EFFECTOR_REACHABLE_COLOR;
406  marker_color.r = color[0];
407  marker_color.g = color[1];
408  marker_color.b = color[2];
409  marker_color.a = color[3];
410 
411  moveit::core::RobotStateConstPtr rstate = handler->getState();
412  const std::vector<std::string>& link_names = rstate->getJointModelGroup(eef.eef_group)->getLinkModelNames();
413  visualization_msgs::msg::MarkerArray marker_array;
414  rstate->getRobotMarkers(marker_array, link_names, marker_color, eef.eef_group, rclcpp::Duration::from_seconds(0));
415  tf2::Transform tf_root_to_link;
416  tf2::fromMsg(tf2::toMsg(rstate->getGlobalLinkTransform(eef.parent_link)), tf_root_to_link);
417  // Release the ptr count on the kinematic state
418  rstate.reset();
419 
420  for (visualization_msgs::msg::Marker& marker : marker_array.markers)
421  {
422  marker.header = im.header;
423  marker.mesh_use_embedded_materials = !marker.mesh_resource.empty();
424  // - - - - - - Do some math for the offset - - - - - -
425  tf2::Transform tf_root_to_im, tf_root_to_mesh, tf_im_to_eef;
426  tf2::fromMsg(im.pose, tf_root_to_im);
427  tf2::fromMsg(marker.pose, tf_root_to_mesh);
428  tf2::fromMsg(im_to_eef, tf_im_to_eef);
429  tf2::Transform tf_eef_to_mesh = tf_root_to_link.inverse() * tf_root_to_mesh;
430  tf2::Transform tf_im_to_mesh = tf_im_to_eef * tf_eef_to_mesh;
431  tf2::Transform tf_root_to_mesh_new = tf_root_to_im * tf_im_to_mesh;
432  tf2::toMsg(tf_root_to_mesh_new, marker.pose);
433  // - - - - - - - - - - - - - - - - - - - - - - - - - -
434  m_control.markers.push_back(marker);
435  }
436 
437  im.controls.push_back(m_control);
438 }
439 
440 static inline std::string getMarkerName(const InteractionHandlerPtr& handler, const EndEffectorInteraction& eef)
441 {
442  return "EE:" + handler->getName() + "_" + eef.parent_link;
443 }
444 
445 static inline std::string getMarkerName(const InteractionHandlerPtr& handler, const JointInteraction& vj)
446 {
447  return "JJ:" + handler->getName() + "_" + vj.connecting_link;
448 }
449 
450 static inline std::string getMarkerName(const InteractionHandlerPtr& handler, const GenericInteraction& g)
451 {
452  return "GG:" + handler->getName() + "_" + g.marker_name_suffix;
453 }
454 
455 void RobotInteraction::addInteractiveMarkers(const InteractionHandlerPtr& handler, const double marker_scale)
456 {
457  // If scale is left at default size of 0, scale will be based on end effector link size. a good value is between 0-1
458  std::vector<visualization_msgs::msg::InteractiveMarker> ims;
459  {
460  std::unique_lock<std::mutex> ulock(marker_access_lock_);
461  moveit::core::RobotStateConstPtr s = handler->getState();
462 
463  for (std::size_t i = 0; i < active_generic_.size(); ++i)
464  {
465  visualization_msgs::msg::InteractiveMarker im;
466  if (active_generic_[i].construct_marker(*s, im))
467  {
468  im.name = getMarkerName(handler, active_generic_[i]);
469  shown_markers_[im.name] = i;
470  ims.push_back(im);
471  RCLCPP_DEBUG(LOGGER, "Publishing interactive marker %s (size = %lf)", im.name.c_str(), im.scale);
472  }
473  }
474 
475  for (std::size_t i = 0; i < active_eef_.size(); ++i)
476  {
477  geometry_msgs::msg::PoseStamped pose;
478  geometry_msgs::msg::Pose control_to_eef_tf;
479  pose.header.frame_id = robot_model_->getModelFrame();
480  pose.header.stamp = node_->now();
481  computeMarkerPose(handler, active_eef_[i], *s, pose.pose, control_to_eef_tf);
482 
483  std::string marker_name = getMarkerName(handler, active_eef_[i]);
484  shown_markers_[marker_name] = i;
485 
486  // Determine interactive maker size
487  double mscale = marker_scale < std::numeric_limits<double>::epsilon() ? active_eef_[i].size : marker_scale;
488 
489  visualization_msgs::msg::InteractiveMarker im = makeEmptyInteractiveMarker(marker_name, pose, mscale);
490  if (handler && handler->getControlsVisible())
491  {
492  if (active_eef_[i].interaction & InteractionStyle::POSITION_ARROWS)
493  addPositionControl(im, active_eef_[i].interaction & InteractionStyle::FIXED);
494  if (active_eef_[i].interaction & InteractionStyle::ORIENTATION_CIRCLES)
495  addOrientationControl(im, active_eef_[i].interaction & InteractionStyle::FIXED);
496  if (active_eef_[i].interaction & (InteractionStyle::POSITION_SPHERE | InteractionStyle::ORIENTATION_SPHERE))
497  {
498  std_msgs::msg::ColorRGBA color;
499  color.r = 0;
500  color.g = 1;
501  color.b = 1;
502  color.a = 0.5;
503  addViewPlaneControl(im, mscale * 0.25, color, active_eef_[i].interaction & InteractionStyle::POSITION_SPHERE,
504  active_eef_[i].interaction & InteractionStyle::ORIENTATION_SPHERE);
505  }
506  }
507  if (handler && handler->getMeshesVisible() &&
508  (active_eef_[i].interaction & (InteractionStyle::POSITION_EEF | InteractionStyle::ORIENTATION_EEF)))
509  addEndEffectorMarkers(handler, active_eef_[i], control_to_eef_tf, im,
510  active_eef_[i].interaction & InteractionStyle::POSITION_EEF,
511  active_eef_[i].interaction & InteractionStyle::ORIENTATION_EEF);
512  ims.push_back(im);
513  registerMoveInteractiveMarkerTopic(marker_name, handler->getName() + "_" + active_eef_[i].parent_link);
514  RCLCPP_DEBUG(LOGGER, "Publishing interactive marker %s (size = %lf)", marker_name.c_str(), mscale);
515  }
516  for (std::size_t i = 0; i < active_vj_.size(); ++i)
517  {
518  geometry_msgs::msg::PoseStamped pose;
519  pose.header.frame_id = robot_model_->getModelFrame();
520  pose.header.stamp = node_->now();
521  pose.pose = tf2::toMsg(s->getGlobalLinkTransform(active_vj_[i].connecting_link));
522  std::string marker_name = getMarkerName(handler, active_vj_[i]);
523  shown_markers_[marker_name] = i;
524 
525  // Determine interactive maker size
526  double mscale = marker_scale < std::numeric_limits<double>::epsilon() ? active_vj_[i].size : marker_scale;
527 
528  visualization_msgs::msg::InteractiveMarker im = makeEmptyInteractiveMarker(marker_name, pose, mscale);
529  if (handler && handler->getControlsVisible())
530  {
531  if (active_vj_[i].dof == 3) // planar joint
532  addPlanarXYControl(im, false);
533  else
534  add6DOFControl(im, false);
535  }
536  ims.push_back(im);
537  registerMoveInteractiveMarkerTopic(marker_name, handler->getName() + "_" + active_vj_[i].connecting_link);
538  RCLCPP_DEBUG(LOGGER, "Publishing interactive marker %s (size = %lf)", marker_name.c_str(), mscale);
539  }
540  handlers_[handler->getName()] = handler;
541  }
542 
543  // we do this while marker_access_lock_ is unlocked because the interactive marker server locks
544  // for most function calls, and maintains that lock while the feedback callback is running
545  // that can cause a deadlock if we were to run the loop below while marker_access_lock_ is locked
546  for (const visualization_msgs::msg::InteractiveMarker& im : ims)
547  {
548  int_marker_server_->insert(im);
549  int_marker_server_->setCallback(im.name,
550  [this](const auto& feedback) { processInteractiveMarkerFeedback(feedback); });
551 
552  // Add menu handler to all markers that this interaction handler creates.
553  if (std::shared_ptr<interactive_markers::MenuHandler> mh = handler->getMenuHandler())
554  mh->apply(*int_marker_server_, im.name);
555  }
556 }
557 
558 void RobotInteraction::registerMoveInteractiveMarkerTopic(const std::string& marker_name, const std::string& name)
559 {
560  std::stringstream ss;
561  ss << "/rviz/moveit/move_marker/";
562  ss << name;
563  int_marker_move_topics_.push_back(ss.str());
564  int_marker_names_.push_back(marker_name);
565 }
566 
568 {
569  if (enable)
570  {
571  std::unique_lock<std::mutex> ulock(marker_access_lock_);
572  if (int_marker_move_subscribers_.empty())
573  {
574  for (size_t i = 0; i < int_marker_move_topics_.size(); ++i)
575  {
576  std::string topic_name = int_marker_move_topics_[i];
577  std::string marker_name = int_marker_names_[i];
578  std::function<void(const geometry_msgs::msg::PoseStamped::SharedPtr)> subscription_callback =
579  [this, marker_name](const geometry_msgs::msg::PoseStamped::SharedPtr& msg) {
580  moveInteractiveMarker(marker_name, *msg);
581  };
582  auto subscription = node_->create_subscription<geometry_msgs::msg::PoseStamped>(
583  topic_name, rclcpp::SystemDefaultsQoS(), subscription_callback);
584  int_marker_move_subscribers_.push_back(subscription);
585  }
586  }
587  }
588  else
589  {
590  std::unique_lock<std::mutex> ulock(marker_access_lock_);
591  int_marker_move_subscribers_.clear();
592  }
593 }
594 
595 void RobotInteraction::computeMarkerPose(const InteractionHandlerPtr& handler, const EndEffectorInteraction& eef,
596  const moveit::core::RobotState& robot_state, geometry_msgs::msg::Pose& pose,
597  geometry_msgs::msg::Pose& control_to_eef_tf) const
598 {
599  // Need to allow for control pose offsets
600  tf2::Transform tf_root_to_link, tf_root_to_control;
601  tf2::fromMsg(tf2::toMsg(robot_state.getGlobalLinkTransform(eef.parent_link)), tf_root_to_link);
602 
603  geometry_msgs::msg::Pose msg_link_to_control;
604  if (handler->getPoseOffset(eef, msg_link_to_control))
605  {
606  tf2::Transform tf_link_to_control;
607  tf2::fromMsg(msg_link_to_control, tf_link_to_control);
608 
609  tf_root_to_control = tf_root_to_link * tf_link_to_control;
610  tf2::toMsg(tf_link_to_control.inverse(), control_to_eef_tf);
611  }
612  else
613  {
614  tf_root_to_control = tf_root_to_link;
615  control_to_eef_tf.orientation.x = 0.0;
616  control_to_eef_tf.orientation.y = 0.0;
617  control_to_eef_tf.orientation.z = 0.0;
618  control_to_eef_tf.orientation.w = 1.0;
619  }
620 
621  tf2::toMsg(tf_root_to_control, pose);
622 }
623 
624 void RobotInteraction::updateInteractiveMarkers(const InteractionHandlerPtr& handler)
625 {
626  std::string root_link;
627  std::map<std::string, geometry_msgs::msg::Pose> pose_updates;
628  {
629  std::unique_lock<std::mutex> ulock(marker_access_lock_);
630 
631  moveit::core::RobotStateConstPtr s = handler->getState();
632  root_link = s->getRobotModel()->getModelFrame();
633 
634  for (const EndEffectorInteraction& eef : active_eef_)
635  {
636  std::string marker_name = getMarkerName(handler, eef);
637  geometry_msgs::msg::Pose control_to_eef_tf;
638  computeMarkerPose(handler, eef, *s, pose_updates[marker_name], control_to_eef_tf);
639  }
640 
641  for (JointInteraction& vj : active_vj_)
642  {
643  std::string marker_name = getMarkerName(handler, vj);
644  pose_updates[marker_name] = tf2::toMsg(s->getGlobalLinkTransform(vj.connecting_link));
645  }
646 
647  for (GenericInteraction& gi : active_generic_)
648  {
649  std::string marker_name = getMarkerName(handler, gi);
650  geometry_msgs::msg::Pose pose;
651  if (gi.update_pose && gi.update_pose(*s, pose))
652  pose_updates[marker_name] = pose;
653  }
654  }
655 
656  std_msgs::msg::Header header;
657  header.frame_id = root_link; // marker poses are give w.r.t. root frame
658  for (std::map<std::string, geometry_msgs::msg::Pose>::const_iterator it = pose_updates.begin();
659  it != pose_updates.end(); ++it)
660  int_marker_server_->setPose(it->first, it->second, header);
661  int_marker_server_->applyChanges();
662 }
663 
665 {
666  // the server locks internally, so we need not worry about locking
667  int_marker_server_->applyChanges();
668 }
669 
670 bool RobotInteraction::showingMarkers(const InteractionHandlerPtr& handler)
671 {
672  std::unique_lock<std::mutex> ulock(marker_access_lock_);
673 
674  for (const EndEffectorInteraction& eef : active_eef_)
675  if (shown_markers_.find(getMarkerName(handler, eef)) == shown_markers_.end())
676  return false;
677  for (const JointInteraction& vj : active_vj_)
678  if (shown_markers_.find(getMarkerName(handler, vj)) == shown_markers_.end())
679  return false;
680  for (const GenericInteraction& gi : active_generic_)
681  if (shown_markers_.find(getMarkerName(handler, gi)) == shown_markers_.end())
682  return false;
683  return true;
684 }
685 
686 void RobotInteraction::moveInteractiveMarker(const std::string& name, const geometry_msgs::msg::PoseStamped& msg)
687 {
688  std::map<std::string, std::size_t>::const_iterator it = shown_markers_.find(name);
689  if (it != shown_markers_.end())
690  {
691  auto feedback = std::make_shared<visualization_msgs::msg::InteractiveMarkerFeedback>();
692  feedback->header = msg.header;
693  feedback->marker_name = name;
694  feedback->pose = msg.pose;
695  feedback->event_type = visualization_msgs::msg::InteractiveMarkerFeedback::POSE_UPDATE;
696  processInteractiveMarkerFeedback(feedback);
697  {
698  std::unique_lock<std::mutex> ulock(marker_access_lock_);
699  int_marker_server_->setPose(name, msg.pose, msg.header); // move the interactive marker
700  int_marker_server_->applyChanges();
701  }
702  }
703 }
704 
705 void RobotInteraction::processInteractiveMarkerFeedback(
706  const visualization_msgs::msg::InteractiveMarkerFeedback::ConstSharedPtr& feedback)
707 {
708  // perform some validity checks
709  std::unique_lock<std::mutex> ulock(marker_access_lock_);
710  std::map<std::string, std::size_t>::const_iterator it = shown_markers_.find(feedback->marker_name);
711  if (it == shown_markers_.end())
712  {
713  RCLCPP_ERROR(LOGGER, "Unknown marker name: '%s' (not published by RobotInteraction class)",
714  feedback->marker_name.c_str());
715  return;
716  }
717 
718  std::size_t u = feedback->marker_name.find_first_of('_');
719  if (u == std::string::npos || u < 4)
720  {
721  RCLCPP_ERROR(LOGGER, "Invalid marker name: '%s'", feedback->marker_name.c_str());
722  return;
723  }
724 
725  feedback_map_[feedback->marker_name] = feedback;
726  new_feedback_condition_.notify_all();
727 }
728 
729 void RobotInteraction::processingThread()
730 {
731  std::unique_lock<std::mutex> ulock(marker_access_lock_);
732 
733  while (run_processing_thread_ && rclcpp::ok())
734  {
735  while (feedback_map_.empty() && run_processing_thread_ && rclcpp::ok())
736  new_feedback_condition_.wait(ulock);
737 
738  while (!feedback_map_.empty() && rclcpp::ok())
739  {
740  auto feedback = feedback_map_.begin()->second;
741  feedback_map_.erase(feedback_map_.begin());
742  RCLCPP_DEBUG(LOGGER, "Processing feedback from map for marker [%s]", feedback->marker_name.c_str());
743 
744  std::map<std::string, std::size_t>::const_iterator it = shown_markers_.find(feedback->marker_name);
745  if (it == shown_markers_.end())
746  {
747  RCLCPP_ERROR(LOGGER,
748  "Unknown marker name: '%s' (not published by RobotInteraction class) "
749  "(should never have ended up in the feedback_map!)",
750  feedback->marker_name.c_str());
751  continue;
752  }
753  std::size_t u = feedback->marker_name.find_first_of('_');
754  if (u == std::string::npos || u < 4)
755  {
756  RCLCPP_ERROR(LOGGER, "Invalid marker name: '%s' (should never have ended up in the feedback_map!)",
757  feedback->marker_name.c_str());
758  continue;
759  }
760  std::string marker_class = feedback->marker_name.substr(0, 2);
761  std::string handler_name = feedback->marker_name.substr(3, u - 3); // skip the ":"
762  std::map<std::string, InteractionHandlerPtr>::const_iterator jt = handlers_.find(handler_name);
763  if (jt == handlers_.end())
764  {
765  RCLCPP_ERROR(LOGGER, "Interactive Marker Handler '%s' is not known.", handler_name.c_str());
766  continue;
767  }
768 
769  // we put this in a try-catch because user specified callbacks may be triggered
770  try
771  {
772  if (marker_class == "EE")
773  {
774  // make a copy of the data, so we do not lose it while we are unlocked
775  EndEffectorInteraction eef = active_eef_[it->second];
776  InteractionHandlerPtr ih = jt->second;
777  marker_access_lock_.unlock();
778  try
779  {
780  ih->handleEndEffector(eef, feedback);
781  }
782  catch (std::exception& ex)
783  {
784  RCLCPP_ERROR(LOGGER, "Exception caught while handling end-effector update: %s", ex.what());
785  }
786  marker_access_lock_.lock();
787  }
788  else if (marker_class == "JJ")
789  {
790  // make a copy of the data, so we do not lose it while we are unlocked
791  JointInteraction vj = active_vj_[it->second];
792  InteractionHandlerPtr ih = jt->second;
793  marker_access_lock_.unlock();
794  try
795  {
796  ih->handleJoint(vj, feedback);
797  }
798  catch (std::exception& ex)
799  {
800  RCLCPP_ERROR(LOGGER, "Exception caught while handling joint update: %s", ex.what());
801  }
802  marker_access_lock_.lock();
803  }
804  else if (marker_class == "GG")
805  {
806  InteractionHandlerPtr ih = jt->second;
807  GenericInteraction g = active_generic_[it->second];
808  marker_access_lock_.unlock();
809  try
810  {
811  ih->handleGeneric(g, feedback);
812  }
813  catch (std::exception& ex)
814  {
815  RCLCPP_ERROR(LOGGER, "Exception caught while handling joint update: %s", ex.what());
816  }
817  marker_access_lock_.lock();
818  }
819  else
820  RCLCPP_ERROR(LOGGER, "Unknown marker class ('%s') for marker '%s'", marker_class.c_str(),
821  feedback->marker_name.c_str());
822  }
823  catch (std::exception& ex)
824  {
825  RCLCPP_ERROR(LOGGER, "Exception caught while processing event: %s", ex.what());
826  }
827  }
828  }
829 }
830 } // namespace robot_interaction
const std::vector< std::string > & getLinkModelNames() const
Get the names of the links that are part of this joint group.
const std::vector< const JointModel * > & getJointModels() const
Get all the joints in this group (including fixed and mimic joints).
bool hasJointModel(const std::string &joint) const
Check if a joint is part of this group.
const std::pair< KinematicsSolver, KinematicsSolverMap > & getGroupKinematics() const
A joint from the robot. Models the transform that this joint applies in the kinematic chain....
Definition: joint_model.h:117
JointType getType() const
Get the type of joint.
Definition: joint_model.h:151
A link from the robot. Contains the constant transform applied to the link and its geometry.
Definition: link_model.h:72
const Eigen::Vector3d & getShapeExtentsAtOrigin() const
Get the extents of the link's geometry (dimensions of axis-aligned bounding box around all shapes tha...
Definition: link_model.h:186
const JointModel * getParentJointModel() const
Get the joint model whose child this link is. There will always be a parent joint.
Definition: link_model.h:108
const LinkModel * getParentLinkModel() const
Get the link model whose child this link is (through some joint). There may not always be a parent li...
Definition: link_model.h:117
Representation of a robot's state. This includes position, velocity, acceleration and effort.
Definition: robot_state.h:90
const Eigen::Isometry3d & getGlobalLinkTransform(const std::string &link_name)
Get the link transform w.r.t. the root link (model frame) of the RobotModel. This is typically the ro...
Definition: robot_state.h:1339
void updateInteractiveMarkers(const InteractionHandlerPtr &handler)
void decideActiveComponents(const std::string &group)
RobotInteraction(const moveit::core::RobotModelConstPtr &robot_model, const rclcpp::Node::SharedPtr &node, const std::string &ns="")
bool showingMarkers(const InteractionHandlerPtr &handler)
void addActiveComponent(const InteractiveMarkerConstructorFn &construct, const ProcessFeedbackFn &process, const InteractiveMarkerUpdateFn &update=InteractiveMarkerUpdateFn(), const std::string &name="")
void addInteractiveMarkers(const InteractionHandlerPtr &handler, const double marker_scale=0.0)
static const std::string INTERACTIVE_MARKER_TOPIC
The topic name on which the internal Interactive Marker Server operates.
Vec3fX< details::Vec3Data< double > > Vector3d
Definition: fcl_compat.h:89
void addViewPlaneControl(visualization_msgs::msg::InteractiveMarker &int_marker, double radius, const std_msgs::msg::ColorRGBA &color, bool position=true, bool orientation=true)
void add6DOFControl(visualization_msgs::msg::InteractiveMarker &int_marker, bool orientation_fixed=false)
std::function< bool(moveit::core::RobotState &state, const visualization_msgs::msg::InteractiveMarkerFeedback::ConstSharedPtr &feedback)> ProcessFeedbackFn
Definition: interaction.h:103
std::function< bool(const moveit::core::RobotState &, geometry_msgs::msg::Pose &)> InteractiveMarkerUpdateFn
Definition: interaction.h:114
std::function< bool(const moveit::core::RobotState &state, visualization_msgs::msg::InteractiveMarker &marker)> InteractiveMarkerConstructorFn
Definition: interaction.h:88
void addOrientationControl(visualization_msgs::msg::InteractiveMarker &int_marker, bool orientation_fixed=false)
visualization_msgs::msg::InteractiveMarker makeEmptyInteractiveMarker(const std::string &name, const geometry_msgs::msg::PoseStamped &stamped, double scale)
void addPlanarXYControl(visualization_msgs::msg::InteractiveMarker &int_marker, bool orientation_fixed=false)
void addPositionControl(visualization_msgs::msg::InteractiveMarker &int_marker, bool orientation_fixed=false)
name
Definition: setup.py:7
const rclcpp::Logger LOGGER
Definition: async_test.h:31
std::string parent_link
The name of the link in the parent group that connects to the end-effector.
Definition: interaction.h:145
InteractiveMarkerConstructorFn construct_marker
Definition: interaction.h:122
InteractiveMarkerUpdateFn update_pose
Definition: interaction.h:130