moveit2
The MoveIt Motion Planning Framework for ROS 2.
kinematic_constraint.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2011, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Ioan Sucan */
36 
38 #include <geometric_shapes/body_operations.h>
39 #include <geometric_shapes/shape_operations.h>
42 #include <geometric_shapes/check_isometry.h>
43 #include <rclcpp/logger.hpp>
44 #include <rclcpp/logging.hpp>
45 #include <rclcpp/time.hpp>
46 #include <tf2_eigen/tf2_eigen.hpp>
47 #include <functional>
48 #include <limits>
49 #include <math.h>
50 #include <memory>
51 #include <typeinfo>
52 #include <moveit/utils/logger.hpp>
53 
54 #include <rclcpp/clock.hpp>
55 #include <rclcpp/duration.hpp>
56 
57 namespace kinematic_constraints
58 {
59 namespace
60 {
61 rclcpp::Logger getLogger()
62 {
63  return moveit::getLogger("kinematic_constraints");
64 }
65 } // namespace
66 
67 static double normalizeAngle(double angle)
68 {
69  double v = fmod(angle, 2.0 * M_PI);
70  if (v < -M_PI)
71  {
72  v += 2.0 * M_PI;
73  }
74  else if (v > M_PI)
75  {
76  v -= 2.0 * M_PI;
77  }
78  return v;
79 }
80 
81 // Normalizes an angle to the interval [-pi, +pi] and then take the absolute value
82 // The returned values will be in the following range [0, +pi]
83 static double normalizeAbsoluteAngle(const double angle)
84 {
85  const double normalized_angle = std::fmod(std::abs(angle), 2 * M_PI);
86  return std::min(2 * M_PI - normalized_angle, normalized_angle);
87 }
88 
96 template <typename Derived>
97 std::tuple<Eigen::Matrix<typename Eigen::MatrixBase<Derived>::Scalar, 3, 1>, bool>
98 calcEulerAngles(const Eigen::MatrixBase<Derived>& R)
99 {
100  using std::atan2;
101  using std::sqrt;
102  EIGEN_STATIC_ASSERT_MATRIX_SPECIFIC_SIZE(Derived, 3, 3)
103  using Index = EIGEN_DEFAULT_DENSE_INDEX_TYPE;
104  using Scalar = typename Eigen::MatrixBase<Derived>::Scalar;
105  const Index i = 0;
106  const Index j = 1;
107  const Index k = 2;
108  Eigen::Matrix<Scalar, 3, 1> res;
109  const Scalar rsum = sqrt((R(i, i) * R(i, i) + R(i, j) * R(i, j) + R(j, k) * R(j, k) + R(k, k) * R(k, k)) / 2);
110  res[1] = atan2(R(i, k), rsum);
111  // There is a singularity when cos(beta) == 0
112  if (rsum > 4 * Eigen::NumTraits<Scalar>::epsilon())
113  { // cos(beta) != 0
114  res[0] = atan2(-R(j, k), R(k, k));
115  res[2] = atan2(-R(i, j), R(i, i));
116  return { res, true };
117  }
118  else if (R(i, k) > 0)
119  { // cos(beta) == 0 and sin(beta) == 1
120  const Scalar spos = R(j, i) + R(k, j); // 2*sin(alpha + gamma)
121  const Scalar cpos = R(j, j) - R(k, i); // 2*cos(alpha + gamma)
122  res[0] = atan2(spos, cpos);
123  res[2] = 0;
124  return { res, false };
125  } // cos(beta) == 0 and sin(beta) == -1
126  const Scalar sneg = R(k, j) - R(j, i); // 2*sin(alpha + gamma)
127  const Scalar cneg = R(j, j) + R(k, i); // 2*cos(alpha + gamma)
128  res[0] = atan2(sneg, cneg);
129  res[2] = 0;
130  return { res, false };
131 }
132 
133 KinematicConstraint::KinematicConstraint(const moveit::core::RobotModelConstPtr& model)
134  : type_(UNKNOWN_CONSTRAINT), robot_model_(model), constraint_weight_(std::numeric_limits<double>::epsilon())
135 {
136 }
137 
139 
140 bool JointConstraint::configure(const moveit_msgs::msg::JointConstraint& jc)
141 {
142  // clearing before we configure to get rid of any old data
143  clear();
144 
145  // testing tolerances first
146  if (jc.tolerance_above < 0.0 || jc.tolerance_below < 0.0)
147  {
148  RCLCPP_WARN(getLogger(), "JointConstraint tolerance values must be positive.");
149  joint_model_ = nullptr;
150  return false;
151  }
152 
153  joint_variable_name_ = jc.joint_name;
154  local_variable_name_.clear();
155  if (robot_model_->hasJointModel(joint_variable_name_))
156  {
158  }
159  else
160  {
161  std::size_t pos = jc.joint_name.find_last_of('/');
162  if (pos != std::string::npos)
163  {
164  joint_model_ = robot_model_->getJointModel(jc.joint_name.substr(0, pos));
165  if (pos + 1 < jc.joint_name.length())
166  local_variable_name_ = jc.joint_name.substr(pos + 1);
167  }
168  else
169  joint_model_ = robot_model_->getJointModel(jc.joint_name);
170  }
171 
172  if (joint_model_)
173  {
174  if (local_variable_name_.empty())
175  {
176  // check if the joint has 1 DOF (the only kind we can handle)
177  if (joint_model_->getVariableCount() == 0)
178  {
179  RCLCPP_ERROR(getLogger(), "Joint '%s' has no parameters to constrain", jc.joint_name.c_str());
180  joint_model_ = nullptr;
181  }
182  else if (joint_model_->getVariableCount() > 1)
183  {
184  RCLCPP_ERROR(getLogger(),
185  "Joint '%s' has more than one parameter to constrain. "
186  "This type of constraint is not supported.",
187  jc.joint_name.c_str());
188  joint_model_ = nullptr;
189  }
190  }
191  else
192  {
193  int found = -1;
194  const std::vector<std::string>& local_var_names = joint_model_->getLocalVariableNames();
195  for (std::size_t i = 0; i < local_var_names.size(); ++i)
196  {
197  if (local_var_names[i] == local_variable_name_)
198  {
199  found = i;
200  break;
201  }
202  }
203  if (found < 0)
204  {
205  RCLCPP_ERROR(getLogger(), "Local variable name '%s' is not known to joint '%s'", local_variable_name_.c_str(),
206  joint_model_->getName().c_str());
207  joint_model_ = nullptr;
208  }
209  }
210  }
211 
212  if (joint_model_)
213  {
214  joint_is_continuous_ = false;
215  joint_tolerance_above_ = jc.tolerance_above;
216  joint_tolerance_below_ = jc.tolerance_below;
218 
219  // check if we have to wrap angles when computing distances
220  joint_is_continuous_ = false;
222  {
223  const moveit::core::RevoluteJointModel* rjoint =
224  static_cast<const moveit::core::RevoluteJointModel*>(joint_model_);
225  if (rjoint->isContinuous())
226  joint_is_continuous_ = true;
227  }
229  {
230  if (local_variable_name_ == "theta")
231  joint_is_continuous_ = true;
232  }
233 
235  {
236  joint_position_ = normalizeAngle(jc.position);
237  }
238  else
239  {
240  joint_position_ = jc.position;
242 
244  {
246  joint_tolerance_above_ = std::numeric_limits<double>::epsilon();
247  RCLCPP_WARN(getLogger(),
248  "Joint %s is constrained to be below the minimum bounds. "
249  "Assuming minimum bounds instead.",
250  jc.joint_name.c_str());
251  }
253  {
255  joint_tolerance_below_ = std::numeric_limits<double>::epsilon();
256  RCLCPP_WARN(getLogger(),
257  "Joint %s is constrained to be above the maximum bounds. "
258  "Assuming maximum bounds instead.",
259  jc.joint_name.c_str());
260  }
261  }
262 
263  if (jc.weight <= std::numeric_limits<double>::epsilon())
264  {
265  RCLCPP_WARN(getLogger(), "The weight on constraint for joint '%s' is very near zero. Setting to 1.0.",
266  jc.joint_name.c_str());
267  constraint_weight_ = 1.0;
268  }
269  else
270  constraint_weight_ = jc.weight;
271  }
272  return joint_model_ != nullptr;
273 }
274 
275 bool JointConstraint::equal(const KinematicConstraint& other, double margin) const
276 {
277  if (other.getType() != type_)
278  return false;
279  const JointConstraint& o = static_cast<const JointConstraint&>(other);
281  {
282  return fabs(joint_position_ - o.joint_position_) <= margin &&
283  fabs(joint_tolerance_above_ - o.joint_tolerance_above_) <= margin &&
285  }
286  return false;
287 }
288 
290 {
291  if (!joint_model_)
292  return ConstraintEvaluationResult(true, 0.0);
293 
294  double current_joint_position = state.getVariablePosition(joint_variable_index_);
295  double dif = 0.0;
296 
297  // compute signed shortest distance for continuous joints
299  {
300  dif = normalizeAngle(current_joint_position) - joint_position_;
301 
302  if (dif > M_PI)
303  {
304  dif = 2.0 * M_PI - dif;
305  }
306  else if (dif < -M_PI)
307  {
308  dif += 2.0 * M_PI; // we include a sign change to have dif > 0
309  }
310  }
311  else
312  dif = current_joint_position - joint_position_;
313 
314  // check bounds
315  bool result = dif <= (joint_tolerance_above_ + 2.0 * std::numeric_limits<double>::epsilon()) &&
316  dif >= (-joint_tolerance_below_ - 2.0 * std::numeric_limits<double>::epsilon());
317  if (verbose)
318  {
319  RCLCPP_INFO(getLogger(),
320  "Constraint %s:: Joint name: '%s', actual value: %f, desired value: %f, "
321  "tolerance_above: %f, tolerance_below: %f",
322  result ? "satisfied" : "violated", joint_variable_name_.c_str(), current_joint_position,
324  }
325  return ConstraintEvaluationResult(result, constraint_weight_ * fabs(dif));
326 }
327 
329 {
330  return joint_model_;
331 }
332 
334 {
335  joint_model_ = nullptr;
337  joint_is_continuous_ = false;
341 }
342 
343 void JointConstraint::print(std::ostream& out) const
344 {
345  if (joint_model_)
346  {
347  out << "Joint constraint for joint " << joint_variable_name_ << ": \n";
348  out << " value = ";
349  out << joint_position_ << "; ";
350  out << " tolerance below = ";
351  out << joint_tolerance_below_ << "; ";
352  out << " tolerance above = ";
353  out << joint_tolerance_above_ << "; ";
354  out << '\n';
355  }
356  else
357  out << "No constraint" << '\n';
358 }
359 
360 bool PositionConstraint::configure(const moveit_msgs::msg::PositionConstraint& pc, const moveit::core::Transforms& tf)
361 {
362  // clearing before we configure to get rid of any old data
363  clear();
364 
365  link_model_ = robot_model_->getLinkModel(pc.link_name);
366  if (link_model_ == nullptr)
367  {
368  RCLCPP_WARN(getLogger(), "Position constraint link model %s not found in kinematic model. Constraint invalid.",
369  pc.link_name.c_str());
370  return false;
371  }
372 
373  if (pc.header.frame_id.empty())
374  {
375  RCLCPP_WARN(getLogger(), "No frame specified for position constraint on link '%s'!", pc.link_name.c_str());
376  return false;
377  }
378 
379  offset_ = Eigen::Vector3d(pc.target_point_offset.x, pc.target_point_offset.y, pc.target_point_offset.z);
380  has_offset_ = offset_.squaredNorm() > std::numeric_limits<double>::epsilon();
381 
382  if (tf.isFixedFrame(pc.header.frame_id))
383  {
385  mobile_frame_ = false;
386  }
387  else
388  {
389  constraint_frame_id_ = pc.header.frame_id;
390  mobile_frame_ = true;
391  }
392 
393  // load primitive shapes, first clearing any we already have
394  for (std::size_t i = 0; i < pc.constraint_region.primitives.size(); ++i)
395  {
396  std::unique_ptr<shapes::Shape> shape(shapes::constructShapeFromMsg(pc.constraint_region.primitives[i]));
397  if (shape)
398  {
399  if (pc.constraint_region.primitive_poses.size() <= i)
400  {
401  RCLCPP_WARN(getLogger(), "Constraint region message does not contain enough primitive poses");
402  continue;
403  }
404  Eigen::Isometry3d t;
405  tf2::fromMsg(pc.constraint_region.primitive_poses[i], t);
406  ASSERT_ISOMETRY(t) // unsanitized input, could contain a non-isometry
407  constraint_region_pose_.push_back(t);
408  if (!mobile_frame_)
409  tf.transformPose(pc.header.frame_id, constraint_region_pose_.back(), constraint_region_pose_.back());
410 
411  const bodies::BodyPtr body(bodies::createEmptyBodyFromShapeType(shape->type));
412  body->setDimensionsDirty(shape.get());
413  body->setPoseDirty(constraint_region_pose_.back());
414  body->updateInternalData();
415  constraint_region_.push_back(body);
416  }
417  else
418  RCLCPP_WARN(getLogger(), "Could not construct primitive shape %zu", i);
419  }
420 
421  // load meshes
422  for (std::size_t i = 0; i < pc.constraint_region.meshes.size(); ++i)
423  {
424  std::unique_ptr<shapes::Shape> shape(shapes::constructShapeFromMsg(pc.constraint_region.meshes[i]));
425  if (shape)
426  {
427  if (pc.constraint_region.mesh_poses.size() <= i)
428  {
429  RCLCPP_WARN(getLogger(), "Constraint region message does not contain enough primitive poses");
430  continue;
431  }
432  Eigen::Isometry3d t;
433  tf2::fromMsg(pc.constraint_region.mesh_poses[i], t);
434  ASSERT_ISOMETRY(t) // unsanitized input, could contain a non-isometry
435  constraint_region_pose_.push_back(t);
436  if (!mobile_frame_)
437  tf.transformPose(pc.header.frame_id, constraint_region_pose_.back(), constraint_region_pose_.back());
438  const bodies::BodyPtr body(bodies::createEmptyBodyFromShapeType(shape->type));
439  body->setDimensionsDirty(shape.get());
440  body->setPoseDirty(constraint_region_pose_.back());
441  body->updateInternalData();
442  constraint_region_.push_back(body);
443  }
444  else
445  {
446  RCLCPP_WARN(getLogger(), "Could not construct mesh shape %zu", i);
447  }
448  }
449 
450  if (pc.weight <= std::numeric_limits<double>::epsilon())
451  {
452  RCLCPP_WARN(getLogger(), "The weight on position constraint for link '%s' is near zero. Setting to 1.0.",
453  pc.link_name.c_str());
454  constraint_weight_ = 1.0;
455  }
456  else
457  constraint_weight_ = pc.weight;
458 
459  return !constraint_region_.empty();
460 }
461 
462 bool PositionConstraint::equal(const KinematicConstraint& other, double margin) const
463 {
464  if (other.getType() != type_)
465  return false;
466  const PositionConstraint& o = static_cast<const PositionConstraint&>(other);
467 
469  {
470  if ((offset_ - o.offset_).norm() > margin)
471  return false;
472  std::vector<bool> other_region_matches_this(constraint_region_.size(), false);
473  for (std::size_t i = 0; i < constraint_region_.size(); ++i)
474  {
475  bool some_match = false;
476  // need to check against all other regions
477  for (std::size_t j = 0; j < o.constraint_region_.size(); ++j)
478  {
479  // constraint_region_pose_ contain only valid isometries, so diff is also a valid isometry
480  Eigen::Isometry3d diff = constraint_region_pose_[i].inverse() * o.constraint_region_pose_[j];
481  if (diff.translation().norm() < margin && diff.linear().isIdentity(margin) &&
482  constraint_region_[i]->getType() == o.constraint_region_[j]->getType() &&
483  fabs(constraint_region_[i]->computeVolume() - o.constraint_region_[j]->computeVolume()) < margin)
484  {
485  some_match = true;
486  // can't break, as need to do matches the other way as well
487  other_region_matches_this[j] = true;
488  }
489  }
490  if (!some_match)
491  return false;
492  }
493  for (std::size_t i = 0; i < o.constraint_region_.size(); ++i)
494  {
495  if (!other_region_matches_this[i])
496  return false;
497  }
498  return true;
499  }
500  return false;
501 }
502 
503 // helper function to avoid code duplication
504 static inline ConstraintEvaluationResult finishPositionConstraintDecision(const Eigen::Vector3d& pt,
505  const Eigen::Vector3d& desired,
506  const std::string& name, double weight,
507  bool result, bool verbose)
508 {
509  double dx = desired.x() - pt.x();
510  double dy = desired.y() - pt.y();
511  double dz = desired.z() - pt.z();
512  if (verbose)
513  {
514  RCLCPP_INFO(getLogger(), "Position constraint %s on link '%s'. Desired: %f, %f, %f, current: %f, %f, %f",
515  result ? "satisfied" : "violated", name.c_str(), desired.x(), desired.y(), desired.z(), pt.x(), pt.y(),
516  pt.z());
517  RCLCPP_INFO(getLogger(), "Differences %g %g %g", dx, dy, dz);
518  }
519  return ConstraintEvaluationResult(result, weight * sqrt(dx * dx + dy * dy + dz * dz));
520 }
521 
523 {
524  if (!link_model_ || constraint_region_.empty())
525  return ConstraintEvaluationResult(true, 0.0);
526 
528  if (mobile_frame_)
529  {
530  for (std::size_t i = 0; i < constraint_region_.size(); ++i)
531  {
532  Eigen::Isometry3d tmp = state.getFrameTransform(constraint_frame_id_) * constraint_region_pose_[i];
533  bool result = constraint_region_[i]->cloneAt(tmp)->containsPoint(pt, verbose);
534  if (result || (i + 1 == constraint_region_pose_.size()))
535  {
536  return finishPositionConstraintDecision(pt, tmp.translation(), link_model_->getName(), constraint_weight_,
537  result, verbose);
538  }
539  else
540  {
541  finishPositionConstraintDecision(pt, tmp.translation(), link_model_->getName(), constraint_weight_, result,
542  verbose);
543  }
544  }
545  }
546  else
547  {
548  for (std::size_t i = 0; i < constraint_region_.size(); ++i)
549  {
550  bool result = constraint_region_[i]->containsPoint(pt, true);
551  if (result || (i + 1 == constraint_region_.size()))
552  {
553  return finishPositionConstraintDecision(pt, constraint_region_[i]->getPose().translation(),
554  link_model_->getName(), constraint_weight_, result, verbose);
555  }
556  else
557  {
558  finishPositionConstraintDecision(pt, constraint_region_[i]->getPose().translation(), link_model_->getName(),
559  constraint_weight_, result, verbose);
560  }
561  }
562  }
563  return ConstraintEvaluationResult(false, 0.0);
564 }
565 
566 void PositionConstraint::print(std::ostream& out) const
567 {
568  if (enabled())
569  {
570  out << "Position constraint on link '" << link_model_->getName() << '\'' << '\n';
571  }
572  else
573  {
574  out << "No constraint" << '\n';
575  }
576 }
577 
579 {
580  offset_ = Eigen::Vector3d(0.0, 0.0, 0.0);
581  has_offset_ = false;
582  constraint_region_.clear();
583  constraint_region_pose_.clear();
584  mobile_frame_ = false;
586  link_model_ = nullptr;
587 }
588 
590 {
591  return link_model_ && !constraint_region_.empty();
592 }
593 
594 bool OrientationConstraint::configure(const moveit_msgs::msg::OrientationConstraint& oc,
595  const moveit::core::Transforms& tf)
596 {
597  // clearing out any old data
598  clear();
599 
600  link_model_ = robot_model_->getLinkModel(oc.link_name);
601  if (!link_model_)
602  {
603  RCLCPP_WARN(getLogger(), "Could not find link model for link name %s", oc.link_name.c_str());
604  return false;
605  }
606  Eigen::Quaterniond q;
607  tf2::fromMsg(oc.orientation, q);
608  if (fabs(q.norm() - 1.0) > 1e-3)
609  {
610  RCLCPP_WARN(getLogger(),
611  "Orientation constraint for link '%s' is probably incorrect: %f, %f, %f, "
612  "%f. Assuming identity instead.",
613  oc.link_name.c_str(), oc.orientation.x, oc.orientation.y, oc.orientation.z, oc.orientation.w);
614  q = Eigen::Quaterniond(1.0, 0.0, 0.0, 0.0);
615  }
616 
617  if (oc.header.frame_id.empty())
618  RCLCPP_WARN(getLogger(), "No frame specified for position constraint on link '%s'!", oc.link_name.c_str());
619 
620  if (tf.isFixedFrame(oc.header.frame_id))
621  {
622  tf.transformQuaternion(oc.header.frame_id, q, q);
624  desired_rotation_matrix_ = Eigen::Matrix3d(q);
626  mobile_frame_ = false;
627  }
628  else
629  {
630  desired_rotation_frame_id_ = oc.header.frame_id;
631  desired_rotation_matrix_ = Eigen::Matrix3d(q);
632  mobile_frame_ = true;
633  }
634  std::stringstream matrix_str;
635  matrix_str << desired_rotation_matrix_;
636  RCLCPP_DEBUG(getLogger(), "The desired rotation matrix for link '%s' in frame %s is:\n%s", oc.link_name.c_str(),
637  desired_rotation_frame_id_.c_str(), matrix_str.str().c_str());
638 
639  if (oc.weight <= std::numeric_limits<double>::epsilon())
640  {
641  RCLCPP_WARN(getLogger(), "The weight on orientation constraint for link '%s' is near zero. Setting to 1.0.",
642  oc.link_name.c_str());
643  constraint_weight_ = 1.0;
644  }
645  else
646  {
647  constraint_weight_ = oc.weight;
648  }
649 
650  parameterization_type_ = oc.parameterization;
651  // validate the parameterization, set to default value if invalid
652  if (parameterization_type_ != moveit_msgs::msg::OrientationConstraint::XYZ_EULER_ANGLES &&
653  parameterization_type_ != moveit_msgs::msg::OrientationConstraint::ROTATION_VECTOR)
654  {
655  RCLCPP_WARN(getLogger(),
656  "Unknown parameterization for orientation constraint tolerance, using default (XYZ_EULER_ANGLES).");
657  parameterization_type_ = moveit_msgs::msg::OrientationConstraint::XYZ_EULER_ANGLES;
658  }
659 
660  absolute_x_axis_tolerance_ = fabs(oc.absolute_x_axis_tolerance);
661  if (absolute_x_axis_tolerance_ < std::numeric_limits<double>::epsilon())
662  RCLCPP_WARN(getLogger(), "Near-zero value for absolute_x_axis_tolerance");
663  absolute_y_axis_tolerance_ = fabs(oc.absolute_y_axis_tolerance);
664  if (absolute_y_axis_tolerance_ < std::numeric_limits<double>::epsilon())
665  RCLCPP_WARN(getLogger(), "Near-zero value for absolute_y_axis_tolerance");
666  absolute_z_axis_tolerance_ = fabs(oc.absolute_z_axis_tolerance);
667  if (absolute_z_axis_tolerance_ < std::numeric_limits<double>::epsilon())
668  RCLCPP_WARN(getLogger(), "Near-zero value for absolute_z_axis_tolerance");
669 
670  return link_model_ != nullptr;
671 }
672 
673 bool OrientationConstraint::equal(const KinematicConstraint& other, double margin) const
674 {
675  if (other.getType() != type_)
676  return false;
677  const OrientationConstraint& o = static_cast<const OrientationConstraint&>(other);
678 
679  if (o.link_model_ == link_model_ &&
681  {
683  return false;
684  return fabs(absolute_x_axis_tolerance_ - o.absolute_x_axis_tolerance_) <= margin &&
687  }
688  return false;
689 }
690 
692 {
693  link_model_ = nullptr;
694  desired_rotation_matrix_ = Eigen::Matrix3d::Identity();
695  desired_rotation_matrix_inv_ = Eigen::Matrix3d::Identity();
697  mobile_frame_ = false;
699 }
700 
702 {
703  return link_model_;
704 }
705 
707 {
708  if (!link_model_)
709  return ConstraintEvaluationResult(true, 0.0);
710 
711  Eigen::Isometry3d diff;
712  if (mobile_frame_)
713  {
714  // getFrameTransform() returns a valid isometry by contract
715  Eigen::Matrix3d tmp = state.getFrameTransform(desired_rotation_frame_id_).linear() * desired_rotation_matrix_;
716  // getGlobalLinkTransform() returns a valid isometry by contract
717  diff = Eigen::Isometry3d(tmp.transpose() * state.getGlobalLinkTransform(link_model_).linear()); // valid isometry
718  }
719  else
720  {
721  // diff is valid isometry by construction
722  diff = Eigen::Isometry3d(desired_rotation_matrix_inv_ * state.getGlobalLinkTransform(link_model_).linear());
723  }
724 
725  // This needs to live outside the if-block scope (as xyz_rotation points to its data).
726  std::tuple<Eigen::Vector3d, bool> euler_angles_error;
727  Eigen::Vector3d xyz_rotation;
728  if (parameterization_type_ == moveit_msgs::msg::OrientationConstraint::XYZ_EULER_ANGLES)
729  {
730  euler_angles_error = calcEulerAngles(diff.linear());
731  // Converting from a rotation matrix to intrinsic XYZ Euler angles has 2 singularities:
732  // pitch ~= pi/2 ==> roll + yaw = theta
733  // pitch ~= -pi/2 ==> roll - yaw = theta
734  // in those cases calcEulerAngles will set roll (xyz_rotation(0)) to theta and yaw (xyz_rotation(2)) to zero, so for
735  // us to be able to capture yaw tolerance violations we do the following: If theta violates the absolute yaw
736  // tolerance we think of it as a pure yaw rotation and set roll to zero.
737  xyz_rotation = std::get<Eigen::Vector3d>(euler_angles_error);
738  if (!std::get<bool>(euler_angles_error))
739  {
740  if (normalizeAbsoluteAngle(xyz_rotation(0)) > absolute_z_axis_tolerance_ + std::numeric_limits<double>::epsilon())
741  {
742  xyz_rotation(2) = xyz_rotation(0);
743  xyz_rotation(0) = 0;
744  }
745  }
746  // Account for angle wrapping
747  xyz_rotation = xyz_rotation.unaryExpr(&normalizeAbsoluteAngle);
748  }
749  else if (parameterization_type_ == moveit_msgs::msg::OrientationConstraint::ROTATION_VECTOR)
750  {
751  Eigen::AngleAxisd aa(diff.linear());
752  xyz_rotation = aa.axis() * aa.angle();
753  xyz_rotation(0) = fabs(xyz_rotation(0));
754  xyz_rotation(1) = fabs(xyz_rotation(1));
755  xyz_rotation(2) = fabs(xyz_rotation(2));
756  }
757  else
758  {
759  /* The parameterization type should be validated in configure, so this should never happen. */
760  RCLCPP_ERROR(getLogger(), "The parameterization type for the orientation constraints is invalid.");
761  }
762 
763  bool result = xyz_rotation(2) < absolute_z_axis_tolerance_ + std::numeric_limits<double>::epsilon() &&
764  xyz_rotation(1) < absolute_y_axis_tolerance_ + std::numeric_limits<double>::epsilon() &&
765  xyz_rotation(0) < absolute_x_axis_tolerance_ + std::numeric_limits<double>::epsilon();
766 
767  if (verbose)
768  {
769  Eigen::Quaterniond q_act(state.getGlobalLinkTransform(link_model_).linear());
770  Eigen::Quaterniond q_des(desired_rotation_matrix_);
771  RCLCPP_INFO(getLogger(),
772  "Orientation constraint %s for link '%s'. Quaternion desired: %f %f %f %f, quaternion "
773  "actual: %f %f %f %f, error: x=%f, y=%f, z=%f, tolerance: x=%f, y=%f, z=%f",
774  result ? "satisfied" : "violated", link_model_->getName().c_str(), q_des.x(), q_des.y(), q_des.z(),
775  q_des.w(), q_act.x(), q_act.y(), q_act.z(), q_act.w(), xyz_rotation(0), xyz_rotation(1),
777  }
778 
779  return ConstraintEvaluationResult(result, constraint_weight_ * (xyz_rotation(0) + xyz_rotation(1) + xyz_rotation(2)));
780 }
781 
782 void OrientationConstraint::print(std::ostream& out) const
783 {
784  if (link_model_)
785  {
786  out << "Orientation constraint on link '" << link_model_->getName() << '\'' << '\n';
787  Eigen::Quaterniond q_des(desired_rotation_matrix_);
788  out << "Desired orientation:" << q_des.x() << ',' << q_des.y() << ',' << q_des.z() << ',' << q_des.w() << '\n';
789  }
790  else
791  out << "No constraint" << '\n';
792 }
793 
794 VisibilityConstraint::VisibilityConstraint(const moveit::core::RobotModelConstPtr& model)
795  : KinematicConstraint(model), robot_model_{ model }
796 {
798 }
799 
801 {
802  target_frame_id_ = "";
803  sensor_frame_id_ = "";
804  sensor_pose_ = Eigen::Isometry3d::Identity();
806  target_pose_ = Eigen::Isometry3d::Identity();
807  cone_sides_ = 0;
808  points_.clear();
809  target_radius_ = -1.0;
810  max_view_angle_ = 0.0;
811  max_range_angle_ = 0.0;
812 }
813 
814 bool VisibilityConstraint::configure(const moveit_msgs::msg::VisibilityConstraint& vc,
815  const moveit::core::Transforms& tf)
816 {
817  clear();
818  target_radius_ = fabs(vc.target_radius);
819 
820  if (vc.target_radius <= std::numeric_limits<double>::epsilon())
821  RCLCPP_WARN(getLogger(), "The radius of the target disc that must be visible should be strictly positive");
822 
823  if (vc.cone_sides < 3)
824  {
825  RCLCPP_WARN(getLogger(),
826  "The number of sides for the visibility region must be 3 or more. "
827  "Assuming 3 sides instead of the specified %d",
828  vc.cone_sides);
829  cone_sides_ = 3;
830  }
831  else
832  cone_sides_ = vc.cone_sides;
833 
834  // compute the points on the base circle of the cone that make up the cone sides
835  points_.clear();
836  double delta = 2.0 * M_PI / static_cast<double>(cone_sides_);
837  double a = 0.0;
838  for (unsigned int i = 0; i < cone_sides_; ++i, a += delta)
839  {
840  double x = sin(a) * target_radius_;
841  double y = cos(a) * target_radius_;
842  points_.push_back(Eigen::Vector3d(x, y, 0.0));
843  }
844 
845  tf2::fromMsg(vc.target_pose.pose, target_pose_);
846  ASSERT_ISOMETRY(target_pose_) // unsanitized input, could contain a non-isometry
847 
848  if (tf.isFixedFrame(vc.target_pose.header.frame_id))
849  {
850  tf.transformPose(vc.target_pose.header.frame_id, target_pose_, target_pose_);
852  }
853  else
854  {
855  target_frame_id_ = vc.target_pose.header.frame_id;
856  }
857 
858  tf2::fromMsg(vc.sensor_pose.pose, sensor_pose_);
859  ASSERT_ISOMETRY(sensor_pose_) // unsanitized input, could contain a non-isometry
860 
861  if (tf.isFixedFrame(vc.sensor_pose.header.frame_id))
862  {
863  tf.transformPose(vc.sensor_pose.header.frame_id, sensor_pose_, sensor_pose_);
865  }
866  else
867  {
868  sensor_frame_id_ = vc.sensor_pose.header.frame_id;
869  }
870 
871  if (vc.weight <= std::numeric_limits<double>::epsilon())
872  {
873  RCLCPP_WARN(getLogger(), "The weight of visibility constraint is near zero. Setting to 1.0.");
874  constraint_weight_ = 1.0;
875  }
876  else
877  constraint_weight_ = vc.weight;
878 
879  max_view_angle_ = vc.max_view_angle;
880  max_range_angle_ = vc.max_range_angle;
881  sensor_view_direction_ = vc.sensor_view_direction;
882 
883  return enabled();
884 }
885 
886 bool VisibilityConstraint::equal(const KinematicConstraint& other, double margin) const
887 {
888  if (other.getType() != type_)
889  return false;
890  const VisibilityConstraint& o = static_cast<const VisibilityConstraint&>(other);
891 
895  {
896  if (fabs(max_view_angle_ - o.max_view_angle_) > margin || fabs(target_radius_ - o.target_radius_) > margin)
897  return false;
898  // sensor_pose_ is valid isometry, checked in configure()
899  Eigen::Isometry3d diff = sensor_pose_.inverse() * o.sensor_pose_; // valid isometry
900  if (diff.translation().norm() > margin)
901  return false;
902  if (!diff.linear().isIdentity(margin))
903  return false;
904  // target_pose_ is valid isometry, checked in configure()
905  diff = target_pose_.inverse() * o.target_pose_; // valid isometry
906  if (diff.translation().norm() > margin)
907  return false;
908  if (!diff.linear().isIdentity(margin))
909  return false;
910  return true;
911  }
912  return false;
913 }
914 
916 {
917  return (target_radius_ > std::numeric_limits<double>::epsilon()) ||
918  (max_view_angle_ > std::numeric_limits<double>::epsilon()) ||
919  (max_range_angle_ > std::numeric_limits<double>::epsilon());
920 }
921 
922 shapes::Mesh* VisibilityConstraint::getVisibilityCone(const Eigen::Isometry3d& tform_world_to_sensor,
923  const Eigen::Isometry3d& tform_world_to_target) const
924 {
925  // the current pose of the sensor
926  const Eigen::Isometry3d& sp = tform_world_to_sensor;
927 
928  // the current pose of the target
929  const Eigen::Isometry3d& tp = tform_world_to_target;
930 
931  // transform the points on the disc to the desired target frame
932  const EigenSTL::vector_Vector3d* points = &points_;
933  std::unique_ptr<EigenSTL::vector_Vector3d> temp_points;
934 
935  temp_points = std::make_unique<EigenSTL::vector_Vector3d>(points_.size());
936  for (std::size_t i = 0; i < points_.size(); ++i)
937  {
938  temp_points->at(i) = tp * points_[i];
939  }
940  points = temp_points.get();
941 
942  // allocate memory for a mesh to represent the visibility cone
943  shapes::Mesh* m = new shapes::Mesh();
944  m->vertex_count = cone_sides_ + 2;
945  m->vertices = new double[m->vertex_count * 3];
946  m->triangle_count = cone_sides_ * 2;
947  m->triangles = new unsigned int[m->triangle_count * 3];
948  // we do NOT allocate normals because we do not compute them
949 
950  // the sensor origin
951  m->vertices[0] = sp.translation().x();
952  m->vertices[1] = sp.translation().y();
953  m->vertices[2] = sp.translation().z();
954 
955  // the center of the base of the cone approximation
956  m->vertices[3] = tp.translation().x();
957  m->vertices[4] = tp.translation().y();
958  m->vertices[5] = tp.translation().z();
959 
960  // the points that approximate the base disc
961  for (std::size_t i = 0; i < points->size(); ++i)
962  {
963  m->vertices[i * 3 + 6] = points->at(i).x();
964  m->vertices[i * 3 + 7] = points->at(i).y();
965  m->vertices[i * 3 + 8] = points->at(i).z();
966  }
967 
968  // add the triangles
969  std::size_t p3 = points->size() * 3;
970  for (std::size_t i = 1; i < points->size(); ++i)
971  {
972  // triangle forming a side of the cone, using the sensor origin
973  std::size_t i3 = (i - 1) * 3;
974  m->triangles[i3] = i + 1;
975  m->triangles[i3 + 1] = 0;
976  m->triangles[i3 + 2] = i + 2;
977  // triangle forming a part of the base of the cone, using the center of the base
978  std::size_t i6 = p3 + i3;
979  m->triangles[i6] = i + 1;
980  m->triangles[i6 + 1] = 1;
981  m->triangles[i6 + 2] = i + 2;
982  }
983 
984  // last triangles
985  m->triangles[p3 - 3] = points->size() + 1;
986  m->triangles[p3 - 2] = 0;
987  m->triangles[p3 - 1] = 2;
988  p3 *= 2;
989  m->triangles[p3 - 3] = points->size() + 1;
990  m->triangles[p3 - 2] = 1;
991  m->triangles[p3 - 1] = 2;
992 
993  return m;
994 }
995 
997  visualization_msgs::msg::MarkerArray& markers) const
998 {
999  // getFrameTransform() returns a valid isometry by contract
1000  // sensor_pose_ is valid isometry (checked in configure())
1001  const Eigen::Isometry3d& sp = state.getFrameTransform(sensor_frame_id_) * sensor_pose_;
1002  // target_pose_ is valid isometry (checked in configure())
1003  const Eigen::Isometry3d& tp = state.getFrameTransform(target_frame_id_) * target_pose_;
1004 
1005  shapes::Mesh* m = getVisibilityCone(sp, tp);
1006  visualization_msgs::msg::Marker mk;
1007  shapes::constructMarkerFromShape(m, mk);
1008  delete m;
1009  mk.header.frame_id = robot_model_->getModelFrame();
1010  mk.header.stamp = rclcpp::Clock().now();
1011  mk.ns = "constraints";
1012  mk.id = 1;
1013  mk.action = visualization_msgs::msg::Marker::ADD;
1014  mk.pose.position.x = 0;
1015  mk.pose.position.y = 0;
1016  mk.pose.position.z = 0;
1017  mk.pose.orientation.x = 0;
1018  mk.pose.orientation.y = 0;
1019  mk.pose.orientation.z = 0;
1020  mk.pose.orientation.w = 1;
1021  mk.lifetime = rclcpp::Duration::from_seconds(60);
1022  // this scale necessary to make results look reasonable
1023  mk.scale.x = .01;
1024  mk.color.a = 1.0;
1025  mk.color.r = 1.0;
1026  mk.color.g = 0.0;
1027  mk.color.b = 0.0;
1028 
1029  markers.markers.push_back(mk);
1030 
1031  visualization_msgs::msg::Marker mka;
1032  mka.type = visualization_msgs::msg::Marker::ARROW;
1033  mka.action = visualization_msgs::msg::Marker::ADD;
1034  mka.color = mk.color;
1035  mka.pose = mk.pose;
1036 
1037  mka.header = mk.header;
1038  mka.ns = mk.ns;
1039  mka.id = 2;
1040  mka.lifetime = mk.lifetime;
1041  mka.scale.x = 0.05;
1042  mka.scale.y = .15;
1043  mka.scale.z = 0.0;
1044  mka.points.resize(2);
1045  Eigen::Vector3d d = tp.translation() + tp.linear().col(2) * 0.5;
1046  mka.points[0].x = tp.translation().x();
1047  mka.points[0].y = tp.translation().y();
1048  mka.points[0].z = tp.translation().z();
1049  mka.points[1].x = d.x();
1050  mka.points[1].y = d.y();
1051  mka.points[1].z = d.z();
1052  markers.markers.push_back(mka);
1053 
1054  mka.id = 3;
1055  mka.color.b = 1.0;
1056  mka.color.r = 0.0;
1057 
1058  d = sp.translation() + sp.linear().col(2 - sensor_view_direction_) * 0.5;
1059  mka.points[0].x = sp.translation().x();
1060  mka.points[0].y = sp.translation().y();
1061  mka.points[0].z = sp.translation().z();
1062  mka.points[1].x = d.x();
1063  mka.points[1].y = d.y();
1064  mka.points[1].z = d.z();
1065 
1066  markers.markers.push_back(mka);
1067 }
1068 
1070 {
1071  // getFrameTransform() returns a valid isometry by contract
1072  // sensor_pose_ is valid isometry (checked in configure())
1073  const Eigen::Isometry3d& tform_world_to_sensor = state.getFrameTransform(sensor_frame_id_) * sensor_pose_;
1074  // target_pose_ is valid isometry (checked in configure())
1075  const Eigen::Isometry3d& tform_world_to_target = state.getFrameTransform(target_frame_id_) * target_pose_;
1076 
1077  // necessary to do subtraction as SENSOR_Z is 0 and SENSOR_X is 2
1078  const Eigen::Vector3d& sensor_view_axis = tform_world_to_sensor.linear().col(2 - sensor_view_direction_);
1079 
1080  // Check view angle constraint
1081  if (max_view_angle_ > std::numeric_limits<double>::epsilon())
1082  {
1083  const Eigen::Vector3d& normal1 = tform_world_to_target.linear().col(2) * -1.0; // along Z axis and inverted
1084  double dp = sensor_view_axis.dot(normal1);
1085  double ang = acos(dp);
1086  if (dp < 0.0)
1087  {
1088  if (verbose)
1089  {
1090  RCLCPP_INFO(getLogger(), "Visibility constraint is violated because the sensor is looking at "
1091  "the wrong side");
1092  }
1093  return ConstraintEvaluationResult(false, 0.0);
1094  }
1095  if (max_view_angle_ < ang)
1096  {
1097  if (verbose)
1098  {
1099  RCLCPP_INFO(getLogger(),
1100  "Visibility constraint is violated because the view angle is %lf "
1101  "(above the maximum allowed of %lf)",
1102  ang, max_view_angle_);
1103  }
1104  return ConstraintEvaluationResult(false, 0.0);
1105  }
1106  }
1107 
1108  // Check range angle constraint
1109  if (max_range_angle_ > std::numeric_limits<double>::epsilon())
1110  {
1111  const Eigen::Vector3d& dir =
1112  (tform_world_to_target.translation() - tform_world_to_sensor.translation()).normalized();
1113  double dp = sensor_view_axis.dot(dir);
1114  if (dp < 0.0)
1115  {
1116  if (verbose)
1117  {
1118  RCLCPP_INFO(getLogger(), "Visibility constraint is violated because the sensor is looking at "
1119  "the wrong side");
1120  }
1121  return ConstraintEvaluationResult(false, 0.0);
1122  }
1123 
1124  double ang = acos(dp);
1125  if (max_range_angle_ < ang)
1126  {
1127  if (verbose)
1128  {
1129  RCLCPP_INFO(getLogger(),
1130  "Visibility constraint is violated because the range angle is %lf "
1131  "(above the maximum allowed of %lf)",
1132  ang, max_range_angle_);
1133  }
1134  return ConstraintEvaluationResult(false, 0.0);
1135  }
1136  }
1137 
1138  // Check visibility cone collision constraint
1139  if (target_radius_ > std::numeric_limits<double>::epsilon())
1140  {
1141  shapes::Mesh* m = getVisibilityCone(tform_world_to_sensor, tform_world_to_target);
1142  if (!m)
1143  {
1144  RCLCPP_ERROR(getLogger(),
1145  "Visibility constraint is violated because we could not create the visibility cone mesh.");
1146  return ConstraintEvaluationResult(false, 0.0);
1147  }
1148 
1149  // add the visibility cone as an object
1150  const auto collision_env_local = std::make_shared<collision_detection::CollisionEnvFCL>(robot_model_);
1151  collision_env_local->getWorld()->addToObject("cone", shapes::ShapeConstPtr(m), Eigen::Isometry3d::Identity());
1152 
1153  // check for collisions between the robot and the cone
1156  return decideContact(contact);
1157  };
1158  acm.setDefaultEntry(std::string("cone"), fn);
1159 
1161  req.contacts = true;
1162  req.verbose = verbose;
1163  req.max_contacts = 1;
1164 
1166  collision_env_local->checkRobotCollision(req, res, state, acm);
1167 
1168  if (verbose)
1169  {
1170  std::stringstream ss;
1171  m->print(ss);
1172  RCLCPP_INFO(getLogger(), "Visibility constraint %ssatisfied. Visibility cone approximation:\n %s",
1173  res.collision ? "not " : "", ss.str().c_str());
1174  }
1175 
1176  collision_env_local->getWorld()->removeObject("cone");
1177 
1178  return ConstraintEvaluationResult(!res.collision, res.collision ? res.contacts.begin()->second.front().depth : 0.0);
1179  }
1180 
1181  // Constraint evaluation succeeded if we made it here
1182  return ConstraintEvaluationResult(true, 0.0);
1183 }
1184 
1186 {
1189  return true;
1194  {
1195  RCLCPP_DEBUG(getLogger(), "Accepted collision with either sensor or target");
1196  return true;
1197  }
1202  {
1203  RCLCPP_DEBUG(getLogger(), "Accepted collision with either sensor or target");
1204  return true;
1205  }
1206  return false;
1207 }
1208 
1209 void VisibilityConstraint::print(std::ostream& out) const
1210 {
1211  if (enabled())
1212  {
1213  out << "Visibility constraint for sensor in frame '" << sensor_frame_id_ << "' using target in frame '"
1214  << target_frame_id_ << '\'' << '\n';
1215  out << "Target radius: " << target_radius_ << ", using " << cone_sides_ << " sides." << '\n';
1216  }
1217  else
1218  out << "No constraint" << '\n';
1219 }
1220 
1222 {
1223  all_constraints_ = moveit_msgs::msg::Constraints();
1224  kinematic_constraints_.clear();
1225  joint_constraints_.clear();
1226  position_constraints_.clear();
1227  orientation_constraints_.clear();
1228  visibility_constraints_.clear();
1229 }
1230 
1231 bool KinematicConstraintSet::add(const std::vector<moveit_msgs::msg::JointConstraint>& jc)
1232 {
1233  bool result = true;
1234  for (const moveit_msgs::msg::JointConstraint& joint_constraint : jc)
1235  {
1237  bool u = ev->configure(joint_constraint);
1238  result = result && u;
1239  kinematic_constraints_.push_back(KinematicConstraintPtr(ev));
1240  joint_constraints_.push_back(joint_constraint);
1241  all_constraints_.joint_constraints.push_back(joint_constraint);
1242  }
1243  return result;
1244 }
1245 
1246 bool KinematicConstraintSet::add(const std::vector<moveit_msgs::msg::PositionConstraint>& pc,
1247  const moveit::core::Transforms& tf)
1248 {
1249  bool result = true;
1250  for (const moveit_msgs::msg::PositionConstraint& position_constraint : pc)
1251  {
1253  bool u = ev->configure(position_constraint, tf);
1254  result = result && u;
1255  kinematic_constraints_.push_back(KinematicConstraintPtr(ev));
1256  position_constraints_.push_back(position_constraint);
1257  all_constraints_.position_constraints.push_back(position_constraint);
1258  }
1259  return result;
1260 }
1261 
1262 bool KinematicConstraintSet::add(const std::vector<moveit_msgs::msg::OrientationConstraint>& oc,
1263  const moveit::core::Transforms& tf)
1264 {
1265  bool result = true;
1266  for (const moveit_msgs::msg::OrientationConstraint& orientation_constraint : oc)
1267  {
1269  bool u = ev->configure(orientation_constraint, tf);
1270  result = result && u;
1271  kinematic_constraints_.push_back(KinematicConstraintPtr(ev));
1272  orientation_constraints_.push_back(orientation_constraint);
1273  all_constraints_.orientation_constraints.push_back(orientation_constraint);
1274  }
1275  return result;
1276 }
1277 
1278 bool KinematicConstraintSet::add(const std::vector<moveit_msgs::msg::VisibilityConstraint>& vc,
1279  const moveit::core::Transforms& tf)
1280 {
1281  bool result = true;
1282  for (const moveit_msgs::msg::VisibilityConstraint& visibility_constraint : vc)
1283  {
1285  bool u = ev->configure(visibility_constraint, tf);
1286  result = result && u;
1287  kinematic_constraints_.push_back(KinematicConstraintPtr(ev));
1288  visibility_constraints_.push_back(visibility_constraint);
1289  all_constraints_.visibility_constraints.push_back(visibility_constraint);
1290  }
1291  return result;
1292 }
1293 
1294 bool KinematicConstraintSet::add(const moveit_msgs::msg::Constraints& c, const moveit::core::Transforms& tf)
1295 {
1296  bool j = add(c.joint_constraints);
1297  bool p = add(c.position_constraints, tf);
1298  bool o = add(c.orientation_constraints, tf);
1299  bool v = add(c.visibility_constraints, tf);
1300  return j && p && o && v;
1301 }
1302 
1304 {
1305  ConstraintEvaluationResult res(true, 0.0);
1306  for (const KinematicConstraintPtr& kinematic_constraint : kinematic_constraints_)
1307  {
1308  ConstraintEvaluationResult r = kinematic_constraint->decide(state, verbose);
1309  if (!r.satisfied)
1310  res.satisfied = false;
1311  res.distance += r.distance;
1312  }
1313  return res;
1314 }
1315 
1317  std::vector<ConstraintEvaluationResult>& results,
1318  bool verbose) const
1319 {
1320  ConstraintEvaluationResult result(true, 0.0);
1321  results.resize(kinematic_constraints_.size());
1322  for (std::size_t i = 0; i < kinematic_constraints_.size(); ++i)
1323  {
1324  results[i] = kinematic_constraints_[i]->decide(state, verbose);
1325  result.satisfied = result.satisfied && results[i].satisfied;
1326  result.distance += results[i].distance;
1327  }
1328 
1329  return result;
1330 }
1331 
1332 void KinematicConstraintSet::print(std::ostream& out) const
1333 {
1334  out << kinematic_constraints_.size() << " kinematic constraints" << '\n';
1335  for (const KinematicConstraintPtr& kinematic_constraint : kinematic_constraints_)
1336  kinematic_constraint->print(out);
1337 }
1338 
1339 bool KinematicConstraintSet::equal(const KinematicConstraintSet& other, double margin) const
1340 {
1341  // each constraint in this matches some in the other
1342  for (const KinematicConstraintPtr& kinematic_constraint : kinematic_constraints_)
1343  {
1344  bool found = false;
1345  for (unsigned int j = 0; !found && j < other.kinematic_constraints_.size(); ++j)
1346  found = kinematic_constraint->equal(*other.kinematic_constraints_[j], margin);
1347  if (!found)
1348  return false;
1349  }
1350  // each constraint in the other matches some constraint in this
1351  for (const KinematicConstraintPtr& kinematic_constraint : other.kinematic_constraints_)
1352  {
1353  bool found = false;
1354  for (unsigned int j = 0; !found && j < kinematic_constraints_.size(); ++j)
1355  found = kinematic_constraint->equal(*kinematic_constraints_[j], margin);
1356  if (!found)
1357  return false;
1358  }
1359  return true;
1360 }
1361 
1362 } // end of namespace kinematic_constraints
Definition of a structure for the allowed collision matrix. All elements in the collision world are r...
void setDefaultEntry(const std::string &name, bool allowed)
Set the default value for entries that include name but are not set explicitly with setEntry().
Class for handling single DOF joint constraints.
double joint_tolerance_below_
Position and tolerance values.
std::string joint_variable_name_
The joint variable name.
bool equal(const KinematicConstraint &other, double margin) const override
Check if two joint constraints are the same.
ConstraintEvaluationResult decide(const moveit::core::RobotState &state, bool verbose=false) const override
Decide whether the constraint is satisfied in the indicated state.
int joint_variable_index_
The index of the joint variable name in the full robot state.
bool joint_is_continuous_
Whether or not the joint is continuous.
void clear() override
Clear the stored constraint.
bool configure(const moveit_msgs::msg::JointConstraint &jc)
Configure the constraint based on a moveit_msgs::msg::JointConstraint.
const moveit::core::JointModel * joint_model_
The joint from the kinematic model for this constraint.
bool enabled() const override
This function returns true if this constraint is configured and able to decide whether states do meet...
void print(std::ostream &out=std::cout) const override
Print the constraint data.
std::string local_variable_name_
The local variable name for a multi DOF joint, if any.
A class that contains many different constraints, and can check RobotState *versus the full set....
void print(std::ostream &out=std::cout) const
Print the constraint data.
bool equal(const KinematicConstraintSet &other, double margin) const
Whether or not another KinematicConstraintSet is equal to this one.
std::vector< moveit_msgs::msg::VisibilityConstraint > visibility_constraints_
Messages corresponding to all internal visibility constraints.
std::vector< moveit_msgs::msg::OrientationConstraint > orientation_constraints_
Messages corresponding to all internal orientation constraints.
moveit::core::RobotModelConstPtr robot_model_
The kinematic model used for by the Set.
bool add(const moveit_msgs::msg::Constraints &c, const moveit::core::Transforms &tf)
Add all known constraints.
moveit_msgs::msg::Constraints all_constraints_
Messages corresponding to all internal constraints.
std::vector< moveit_msgs::msg::PositionConstraint > position_constraints_
Messages corresponding to all internal position constraints.
std::vector< KinematicConstraintPtr > kinematic_constraints_
Shared pointers to all the member constraints.
ConstraintEvaluationResult decide(const moveit::core::RobotState &state, bool verbose=false) const
Determines whether all constraints are satisfied by state, returning a single evaluation result.
std::vector< moveit_msgs::msg::JointConstraint > joint_constraints_
Messages corresponding to all internal joint constraints.
Base class for representing a kinematic constraint.
double constraint_weight_
The weight of a constraint is a multiplicative factor associated to the distance computed by the deci...
ConstraintType type_
The type of the constraint.
ConstraintType getType() const
Get the type of constraint.
KinematicConstraint(const moveit::core::RobotModelConstPtr &model)
Constructor.
moveit::core::RobotModelConstPtr robot_model_
The kinematic model associated with this constraint.
Class for constraints on the orientation of a link.
bool mobile_frame_
Whether or not the header frame is mobile or fixed.
void clear() override
Clear the stored constraint.
bool equal(const KinematicConstraint &other, double margin) const override
Check if two orientation constraints are the same.
void print(std::ostream &out=std::cout) const override
Print the constraint data.
std::string desired_rotation_frame_id_
The target frame of the transform tree.
bool configure(const moveit_msgs::msg::OrientationConstraint &oc, const moveit::core::Transforms &tf)
Configure the constraint based on a moveit_msgs::msg::OrientationConstraint.
ConstraintEvaluationResult decide(const moveit::core::RobotState &state, bool verbose=false) const override
Decide whether the constraint is satisfied in the indicated state.
double absolute_z_axis_tolerance_
Storage for the tolerances.
Eigen::Matrix3d desired_rotation_matrix_inv_
The inverse of the desired rotation matrix, precomputed for efficiency. Guaranteed to be valid rotati...
bool enabled() const override
This function returns true if this constraint is configured and able to decide whether states do meet...
Eigen::Matrix3d desired_rotation_matrix_
The desired rotation matrix in the tf frame. Guaranteed to be valid rotation matrix.
const moveit::core::LinkModel * link_model_
The target link model.
int parameterization_type_
Parameterization type for orientation tolerance.
Class for constraints on the XYZ position of a link.
void print(std::ostream &out=std::cout) const override
Print the constraint data.
bool enabled() const override
This function returns true if this constraint is configured and able to decide whether states do meet...
std::string constraint_frame_id_
The constraint frame id.
void clear() override
Clear the stored constraint.
bool configure(const moveit_msgs::msg::PositionConstraint &pc, const moveit::core::Transforms &tf)
Configure the constraint based on a moveit_msgs::msg::PositionConstraint.
std::vector< bodies::BodyPtr > constraint_region_
The constraint region vector.
ConstraintEvaluationResult decide(const moveit::core::RobotState &state, bool verbose=false) const override
Decide whether the constraint is satisfied in the indicated state.
bool equal(const KinematicConstraint &other, double margin) const override
Check if two constraints are the same. For position constraints this means that:
const moveit::core::LinkModel * link_model_
The link model constraint subject.
bool has_offset_
Whether the offset is substantially different than 0.0.
Eigen::Vector3d offset_
The target offset.
EigenSTL::vector_Isometry3d constraint_region_pose_
The constraint region pose vector. All isometries are guaranteed to be valid.
bool mobile_frame_
Whether or not a mobile frame is employed.
Class for constraints on the visibility relationship between a sensor and a target.
moveit::core::RobotModelConstPtr robot_model_
A copy of the robot model used to create collision environments to check the cone against robot links...
shapes::Mesh * getVisibilityCone(const Eigen::Isometry3d &tform_world_to_sensor, const Eigen::Isometry3d &tform_world_to_target) const
Gets a trimesh shape representing the visibility cone.
Eigen::Isometry3d target_pose_
The target pose transformed into the transform frame.
void clear() override
Clear the stored constraint.
bool equal(const KinematicConstraint &other, double margin) const override
Check if two constraints are the same.
Eigen::Isometry3d sensor_pose_
The sensor pose transformed into the transform frame.
void print(std::ostream &out=std::cout) const override
Print the constraint data.
bool enabled() const override
This function returns true if this constraint is configured and able to decide whether states do meet...
VisibilityConstraint(const moveit::core::RobotModelConstPtr &model)
Constructor.
std::string target_frame_id_
The target frame id.
double max_range_angle_
Storage for the max range angle.
std::string sensor_frame_id_
The sensor frame id.
double max_view_angle_
Storage for the max view angle.
double target_radius_
Storage for the target radius.
ConstraintEvaluationResult decide(const moveit::core::RobotState &state, bool verbose=false) const override
Decide whether the constraint is satisfied in the indicated state.
void getMarkers(const moveit::core::RobotState &state, visualization_msgs::msg::MarkerArray &markers) const
Adds markers associated with the visibility cone, sensor and target to the visualization array.
bool configure(const moveit_msgs::msg::VisibilityConstraint &vc, const moveit::core::Transforms &tf)
Configure the constraint based on a moveit_msgs::msg::VisibilityConstraint.
EigenSTL::vector_Vector3d points_
A set of points along the base of the circle.
int sensor_view_direction_
Storage for the sensor view direction.
unsigned int cone_sides_
Storage for the cone sides
bool decideContact(const collision_detection::Contact &contact) const
Function that gets passed into collision checking to allow some collisions.
const std::vector< std::string > & getLocalVariableNames() const
Get the local names of the variable that make up the joint (suffixes that are attached to joint names...
Definition: joint_model.h:198
std::size_t getVariableCount() const
Get the number of variables that describe this joint.
Definition: joint_model.h:210
const std::string & getName() const
Get the name of the joint.
Definition: joint_model.h:145
const VariableBounds & getVariableBounds(const std::string &variable) const
Get the bounds for a variable. Throw an exception if the variable was not found.
JointType getType() const
Get the type of joint.
Definition: joint_model.h:151
const std::string & getName() const
The name of this link.
Definition: link_model.h:86
bool isContinuous() const
Check if this joint wraps around.
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:1246
const Eigen::Isometry3d & getFrameTransform(const std::string &frame_id, bool *frame_found=nullptr)
Get the transformation matrix from the model frame (root of model) to the frame identified by frame_i...
double getVariablePosition(const std::string &variable) const
Get the position of a particular variable. An exception is thrown if the variable is not known.
Definition: robot_state.h:207
Provides an implementation of a snapshot of a transform tree that can be easily queried for transform...
Definition: transforms.h:59
const std::string & getTargetFrame() const
Get the planning frame corresponding to this set of transforms.
Definition: transforms.cpp:79
static bool sameFrame(const std::string &frame1, const std::string &frame2)
Check if two frames end up being the same once the missing / are added as prefix (if they are missing...
Definition: transforms.cpp:70
void transformPose(const std::string &from_frame, const Eigen::Isometry3d &t_in, Eigen::Isometry3d &t_out) const
Transform a pose in from_frame to the target_frame.
Definition: transforms.h:184
void transformQuaternion(const std::string &from_frame, const Eigen::Quaterniond &q_in, Eigen::Quaterniond &q_out) const
Transform a quaternion in from_frame to the target_frame.
Definition: transforms.h:159
virtual bool isFixedFrame(const std::string &frame) const
Check whether a frame stays constant as the state of the robot model changes. This is true for any tr...
Definition: transforms.cpp:98
@ ROBOT_LINK
A link on the robot.
@ ROBOT_ATTACHED
A body attached to a robot link.
@ WORLD_OBJECT
A body in the environment.
std::function< bool(collision_detection::Contact &)> DecideContactFn
Signature of predicate that decides whether a contact is allowed or not (when AllowedCollision::Type ...
Vec3fX< details::Vec3Data< double > > Vector3d
Definition: fcl_compat.h:89
Representation and evaluation of kinematic constraints.
std::tuple< Eigen::Matrix< typename Eigen::MatrixBase< Derived >::Scalar, 3, 1 >, bool > calcEulerAngles(const Eigen::MatrixBase< Derived > &R)
geometry_msgs::msg::Pose getPose(const moveit::core::RobotState *self, const std::string &link_name)
Definition: robot_state.cpp:80
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition: logger.cpp:79
name
Definition: setup.py:7
Representation of a collision checking request.
bool verbose
Flag indicating whether information about detected collisions should be reported.
bool contacts
If true, compute contacts. Otherwise only a binary collision yes/no is reported.
std::size_t max_contacts
Overall maximum number of contacts to compute.
Representation of a collision checking result.
void print() const
Throttled warning printing the first collision pair, if any. All collisions are logged at DEBUG level...
bool collision
True if collision was found, false otherwise.
Definition of a contact point.
std::string body_name_2
The id of the second body involved in the contact.
std::string body_name_1
The id of the first body involved in the contact.
BodyType body_type_1
The type of the first body involved in the contact.
BodyType body_type_2
The type of the second body involved in the contact.
Struct for containing the results of constraint evaluation.
bool satisfied
Whether or not the constraint or constraints were satisfied.
double distance
The distance evaluation from the constraint or constraints.