moveit2
The MoveIt Motion Planning Framework for ROS 2.
ompl_constraints.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2020, KU Leuven
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 KU Leuven 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: Jeroen De Maeyer, Boston Cleek */
36 
37 #include <algorithm>
38 #include <iterator>
39 
41 
42 #include <tf2_eigen/tf2_eigen.hpp>
43 
44 namespace ompl_interface
45 {
46 
47 static const rclcpp::Logger LOGGER = rclcpp::get_logger("moveit_planners_ompl.ompl_constraints");
48 
49 Bounds::Bounds() : size_(0)
50 {
51 }
52 
53 Bounds::Bounds(const std::vector<double>& lower, const std::vector<double>& upper)
54  : lower_(lower), upper_(upper), size_(lower.size())
55 {
56  // how to report this in release mode??
57  assert(lower_.size() == upper_.size());
58 }
59 
60 Eigen::VectorXd Bounds::penalty(const Eigen::Ref<const Eigen::VectorXd>& x) const
61 {
62  assert((long)lower_.size() == x.size());
63  Eigen::VectorXd penalty(x.size());
64 
65  for (unsigned int i = 0; i < x.size(); ++i)
66  {
67  if (x[i] < lower_.at(i))
68  {
69  penalty[i] = lower_.at(i) - x[i];
70  }
71  else if (x[i] > upper_.at(i))
72  {
73  penalty[i] = x[i] - upper_.at(i);
74  }
75  else
76  {
77  penalty[i] = 0.0;
78  }
79  }
80  return penalty;
81 }
82 
83 Eigen::VectorXd Bounds::derivative(const Eigen::Ref<const Eigen::VectorXd>& x) const
84 {
85  assert((long)lower_.size() == x.size());
86  Eigen::VectorXd derivative(x.size());
87 
88  for (unsigned int i = 0; i < x.size(); ++i)
89  {
90  if (x[i] < lower_.at(i))
91  {
92  derivative[i] = -1.0;
93  }
94  else if (x[i] > upper_.at(i))
95  {
96  derivative[i] = 1.0;
97  }
98  else
99  {
100  derivative[i] = 0.0;
101  }
102  }
103  return derivative;
104 }
105 
106 std::size_t Bounds::size() const
107 {
108  return size_;
109 }
110 
111 std::ostream& operator<<(std::ostream& os, const ompl_interface::Bounds& bounds)
112 {
113  os << "Bounds:\n";
114  for (std::size_t i{ 0 }; i < bounds.size(); ++i)
115  {
116  os << "( " << bounds.lower_[i] << ", " << bounds.upper_[i] << " )\n";
117  }
118  return os;
119 }
120 
121 /****************************
122  * Base class for constraints
123  * **************************/
124 BaseConstraint::BaseConstraint(const moveit::core::RobotModelConstPtr& robot_model, const std::string& group,
125  const unsigned int num_dofs, const unsigned int num_cons_)
126  : ompl::base::Constraint(num_dofs, num_cons_)
127  , state_storage_(robot_model)
128  , joint_model_group_(robot_model->getJointModelGroup(group))
129 
130 {
131 }
132 
133 void BaseConstraint::init(const moveit_msgs::msg::Constraints& constraints)
134 {
135  parseConstraintMsg(constraints);
136 }
137 
138 void BaseConstraint::function(const Eigen::Ref<const Eigen::VectorXd>& joint_values,
139  Eigen::Ref<Eigen::VectorXd> out) const
140 {
141  const Eigen::VectorXd current_values = calcError(joint_values);
142  out = bounds_.penalty(current_values);
143 }
144 
145 void BaseConstraint::jacobian(const Eigen::Ref<const Eigen::VectorXd>& joint_values,
146  Eigen::Ref<Eigen::MatrixXd> out) const
147 {
148  const Eigen::VectorXd constraint_error = calcError(joint_values);
149  const Eigen::VectorXd constraint_derivative = bounds_.derivative(constraint_error);
150  const Eigen::MatrixXd robot_jacobian = calcErrorJacobian(joint_values);
151  for (std::size_t i = 0; i < bounds_.size(); ++i)
152  {
153  out.row(i) = constraint_derivative[i] * robot_jacobian.row(i);
154  }
155 }
156 
157 Eigen::Isometry3d BaseConstraint::forwardKinematics(const Eigen::Ref<const Eigen::VectorXd>& joint_values) const
158 {
160  robot_state->setJointGroupPositions(joint_model_group_, joint_values);
161  return robot_state->getGlobalLinkTransform(link_name_);
162 }
163 
164 Eigen::MatrixXd BaseConstraint::robotGeometricJacobian(const Eigen::Ref<const Eigen::VectorXd>& joint_values) const
165 {
167  robot_state->setJointGroupPositions(joint_model_group_, joint_values);
168  Eigen::MatrixXd jacobian;
169  // return value (success) not used, could return a garbage jacobian.
171  Eigen::Vector3d(0.0, 0.0, 0.0), jacobian);
172  return jacobian;
173 }
174 
175 Eigen::VectorXd BaseConstraint::calcError(const Eigen::Ref<const Eigen::VectorXd>& /*x*/) const
176 {
177  RCLCPP_WARN_STREAM(LOGGER,
178  "BaseConstraint: Constraint method calcError was not overridden, so it should not be used.");
179  return Eigen::VectorXd::Zero(getCoDimension());
180 }
181 
182 Eigen::MatrixXd BaseConstraint::calcErrorJacobian(const Eigen::Ref<const Eigen::VectorXd>& /*x*/) const
183 {
184  RCLCPP_WARN_STREAM(
185  LOGGER, "BaseConstraint: Constraint method calcErrorJacobian was not overridden, so it should not be used.");
186  return Eigen::MatrixXd::Zero(getCoDimension(), n_);
187 }
188 
189 /******************************************
190  * Position constraints
191  * ****************************************/
192 BoxConstraint::BoxConstraint(const moveit::core::RobotModelConstPtr& robot_model, const std::string& group,
193  const unsigned int num_dofs)
194  : BaseConstraint(robot_model, group, num_dofs)
195 {
196 }
197 
198 void BoxConstraint::parseConstraintMsg(const moveit_msgs::msg::Constraints& constraints)
199 {
200  assert(bounds_.size() == 0);
201  bounds_ = positionConstraintMsgToBoundVector(constraints.position_constraints.at(0));
202 
203  // extract target position and orientation
204  geometry_msgs::msg::Point position =
205  constraints.position_constraints.at(0).constraint_region.primitive_poses.at(0).position;
206 
207  target_position_ << position.x, position.y, position.z;
208 
209  tf2::fromMsg(constraints.position_constraints.at(0).constraint_region.primitive_poses.at(0).orientation,
211 
212  link_name_ = constraints.position_constraints.at(0).link_name;
213 }
214 
215 Eigen::VectorXd BoxConstraint::calcError(const Eigen::Ref<const Eigen::VectorXd>& x) const
216 {
217  return target_orientation_.matrix().transpose() * (forwardKinematics(x).translation() - target_position_);
218 }
219 
220 Eigen::MatrixXd BoxConstraint::calcErrorJacobian(const Eigen::Ref<const Eigen::VectorXd>& x) const
221 {
222  return target_orientation_.matrix().transpose() * robotGeometricJacobian(x).topRows(3);
223 }
224 
225 /******************************************
226  * Equality constraints
227  * ****************************************/
228 EqualityPositionConstraint::EqualityPositionConstraint(const moveit::core::RobotModelConstPtr& robot_model,
229  const std::string& group, const unsigned int num_dofs)
230  : BaseConstraint(robot_model, group, num_dofs)
231 {
232 }
233 
234 void EqualityPositionConstraint::parseConstraintMsg(const moveit_msgs::msg::Constraints& constraints)
235 {
236  const auto dims = constraints.position_constraints.at(0).constraint_region.primitives.at(0).dimensions;
237 
238  is_dim_constrained_ = { false, false, false };
239  for (std::size_t i = 0; i < dims.size(); ++i)
240  {
241  if (dims.at(i) < EQUALITY_CONSTRAINT_THRESHOLD)
242  {
243  if (dims.at(i) < getTolerance())
244  {
245  RCLCPP_ERROR_STREAM(
246  LOGGER,
247  "Dimension: " << i
248  << " of position constraint is smaller than the tolerance used to evaluate the constraints. "
249  "This will make all states invalid and planning will fail. Please use a value between: "
250  << getTolerance() << " and " << EQUALITY_CONSTRAINT_THRESHOLD);
251  }
252 
253  is_dim_constrained_.at(i) = true;
254  }
255  }
256 
257  // extract target position and orientation
258  geometry_msgs::msg::Point position =
259  constraints.position_constraints.at(0).constraint_region.primitive_poses.at(0).position;
260 
261  target_position_ << position.x, position.y, position.z;
262 
263  tf2::fromMsg(constraints.position_constraints.at(0).constraint_region.primitive_poses.at(0).orientation,
265 
266  link_name_ = constraints.position_constraints.at(0).link_name;
267 }
268 
269 void EqualityPositionConstraint::function(const Eigen::Ref<const Eigen::VectorXd>& joint_values,
270  Eigen::Ref<Eigen::VectorXd> out) const
271 {
272  Eigen::Vector3d error =
273  target_orientation_.matrix().transpose() * (forwardKinematics(joint_values).translation() - target_position_);
274  for (std::size_t dim = 0; dim < 3; ++dim)
275  {
276  if (is_dim_constrained_.at(dim))
277  {
278  out[dim] = error[dim]; // equality constraint dimension
279  }
280  else
281  {
282  out[dim] = 0.0; // unbounded dimension
283  }
284  }
285 }
286 
287 void EqualityPositionConstraint::jacobian(const Eigen::Ref<const Eigen::VectorXd>& joint_values,
288  Eigen::Ref<Eigen::MatrixXd> out) const
289 {
290  out.setZero();
291  Eigen::MatrixXd jac = target_orientation_.matrix().transpose() * robotGeometricJacobian(joint_values).topRows(3);
292  for (std::size_t dim = 0; dim < 3; ++dim)
293  {
294  if (is_dim_constrained_.at(dim))
295  {
296  out.row(dim) = jac.row(dim); // equality constraint dimension
297  }
298  }
299 }
300 
301 /******************************************
302  * Orientation constraints
303  * ****************************************/
304 void OrientationConstraint::parseConstraintMsg(const moveit_msgs::msg::Constraints& constraints)
305 {
306  bounds_ = orientationConstraintMsgToBoundVector(constraints.orientation_constraints.at(0));
307 
308  tf2::fromMsg(constraints.orientation_constraints.at(0).orientation, target_orientation_);
309 
310  link_name_ = constraints.orientation_constraints.at(0).link_name;
311 }
312 
313 Eigen::VectorXd OrientationConstraint::calcError(const Eigen::Ref<const Eigen::VectorXd>& x) const
314 {
315  Eigen::Matrix3d orientation_difference = forwardKinematics(x).linear().transpose() * target_orientation_;
316  Eigen::AngleAxisd aa(orientation_difference);
317  return aa.axis() * aa.angle();
318 }
319 
320 Eigen::MatrixXd OrientationConstraint::calcErrorJacobian(const Eigen::Ref<const Eigen::VectorXd>& x) const
321 {
322  Eigen::Matrix3d orientation_difference = forwardKinematics(x).linear().transpose() * target_orientation_;
323  Eigen::AngleAxisd aa{ orientation_difference };
324  return -angularVelocityToAngleAxis(aa.angle(), aa.axis()) * robotGeometricJacobian(x).bottomRows(3);
325 }
326 
327 /************************************
328  * MoveIt constraint message parsing
329  * **********************************/
330 Bounds positionConstraintMsgToBoundVector(const moveit_msgs::msg::PositionConstraint& pos_con)
331 {
332  auto dims = pos_con.constraint_region.primitives.at(0).dimensions;
333 
334  // dimension of -1 signifies unconstrained parameter, so set to infinity
335  for (auto& dim : dims)
336  {
337  if (dim == -1)
338  {
339  dim = std::numeric_limits<double>::infinity();
340  }
341  }
342 
343  return { { -dims.at(0) / 2.0, -dims.at(1) / 2.0, -dims.at(2) / 2.0 },
344  { dims.at(0) / 2.0, dims.at(1) / 2.0, dims.at(2) / 2.0 } };
345 }
346 
347 Bounds orientationConstraintMsgToBoundVector(const moveit_msgs::msg::OrientationConstraint& ori_con)
348 {
349  std::vector<double> dims = { ori_con.absolute_x_axis_tolerance, ori_con.absolute_y_axis_tolerance,
350  ori_con.absolute_z_axis_tolerance };
351 
352  // dimension of -1 signifies unconstrained parameter, so set to infinity
353  for (auto& dim : dims)
354  {
355  if (dim == -1)
356  dim = std::numeric_limits<double>::infinity();
357  }
358  return { { -dims[0], -dims[1], -dims[2] }, { dims[0], dims[1], dims[2] } };
359 }
360 
361 /******************************************
362  * OMPL Constraints Factory
363  * ****************************************/
364 ompl::base::ConstraintPtr createOMPLConstraints(const moveit::core::RobotModelConstPtr& robot_model,
365  const std::string& group,
366  const moveit_msgs::msg::Constraints& constraints)
367 {
368  // This factory method contains template code to support position and/or orientation constraints.
369  // If the specified constraints are invalid, a nullptr is returned.
370  std::vector<ompl::base::ConstraintPtr> ompl_constraints;
371  const std::size_t num_dofs = robot_model->getJointModelGroup(group)->getVariableCount();
372 
373  // Parse Position Constraints
374  if (!constraints.position_constraints.empty())
375  {
376  if (constraints.position_constraints.size() > 1)
377  {
378  RCLCPP_WARN(LOGGER, "Only a single position constraint is supported. Using the first one.");
379  }
380 
381  const auto& primitives = constraints.position_constraints.at(0).constraint_region.primitives;
382  if (primitives.size() > 1)
383  {
384  RCLCPP_WARN(LOGGER, "Only a single position primitive is supported. Using the first one.");
385  }
386  if (primitives.empty() || primitives.at(0).type != shape_msgs::msg::SolidPrimitive::BOX)
387  {
388  RCLCPP_ERROR(LOGGER, "Unable to plan with the requested position constraint. "
389  "Only BOX primitive shapes are supported as constraint region.");
390  }
391  else
392  {
393  BaseConstraintPtr pos_con;
394  if (constraints.name == "use_equality_constraints")
395  {
396  pos_con = std::make_shared<EqualityPositionConstraint>(robot_model, group, num_dofs);
397  }
398  else
399  {
400  pos_con = std::make_shared<BoxConstraint>(robot_model, group, num_dofs);
401  }
402  pos_con->init(constraints);
403  ompl_constraints.emplace_back(pos_con);
404  }
405  }
406 
407  // Parse Orientation Constraints
408  if (!constraints.orientation_constraints.empty())
409  {
410  if (constraints.orientation_constraints.size() > 1)
411  {
412  RCLCPP_WARN(LOGGER, "Only a single orientation constraint is supported. Using the first one.");
413  }
414 
415  auto ori_con = std::make_shared<OrientationConstraint>(robot_model, group, num_dofs);
416  ori_con->init(constraints);
417  ompl_constraints.emplace_back(ori_con);
418  }
419 
420  // Check if we have any constraints to plan with
421  if (ompl_constraints.empty())
422  {
423  RCLCPP_ERROR(LOGGER, "Failed to parse any supported path constraints from planning request.");
424  return nullptr;
425  }
426 
427  return std::make_shared<ompl::base::ConstraintIntersection>(num_dofs, ompl_constraints);
428 }
429 } // namespace ompl_interface
const LinkModel * getLinkModel(const std::string &link) const
Get a link by its name. Throw an exception if the link is not part of this group.
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:1340
bool getJacobian(const JointModelGroup *group, const LinkModel *link, const Eigen::Vector3d &reference_point_position, Eigen::MatrixXd &jacobian, bool use_quaternion_representation=false) const
Compute the Jacobian with reference to a particular point on a given link, for a specified group.
void setJointGroupPositions(const std::string &joint_group_name, const double *gstate)
Given positions for the variables that make up a group, in the order found in the group (including va...
Definition: robot_state.h:605
Abstract base class for different types of constraints, implementations of ompl::base::Constraint.
Eigen::Quaterniond target_orientation_
target for equality constraints, nominal value for inequality constraints.
virtual void parseConstraintMsg(const moveit_msgs::msg::Constraints &constraints)=0
Parse bounds on position and orientation parameters from MoveIt's constraint message.
TSStateStorage state_storage_
Thread-safe storage of the robot state.
const moveit::core::JointModelGroup * joint_model_group_
virtual Eigen::MatrixXd calcErrorJacobian(const Eigen::Ref< const Eigen::VectorXd > &) const
For inequality constraints: calculate the Jacobian for the current parameters that are being constrai...
Bounds bounds_
Upper and lower bounds on constrained variables.
virtual Eigen::VectorXd calcError(const Eigen::Ref< const Eigen::VectorXd > &) const
For inequality constraints: calculate the value of the parameter that is being constrained by the bou...
Eigen::Isometry3d forwardKinematics(const Eigen::Ref< const Eigen::VectorXd > &joint_values) const
Wrapper for forward kinematics calculated by MoveIt's Robot State.
std::string link_name_
Robot link the constraints are applied to.
Eigen::MatrixXd robotGeometricJacobian(const Eigen::Ref< const Eigen::VectorXd > &joint_values) const
Calculate the robot's geometric Jacobian using MoveIt's Robot State.
BaseConstraint(const moveit::core::RobotModelConstPtr &robot_model, const std::string &group, const unsigned int num_dofs, const unsigned int num_cons=3)
Construct a BaseConstraint using 3 num_cons by default because all constraints currently implemented ...
Eigen::Vector3d target_position_
target for equality constraints, nominal value for inequality constraints.
void init(const moveit_msgs::msg::Constraints &constraints)
Initialize constraint based on message content.
void jacobian(const Eigen::Ref< const Eigen::VectorXd > &joint_values, Eigen::Ref< Eigen::MatrixXd > out) const override
Jacobian of the constraint function.
void function(const Eigen::Ref< const Eigen::VectorXd > &joint_values, Eigen::Ref< Eigen::VectorXd > out) const override
Represents upper and lower bound on the elements of a vector.
Eigen::VectorXd derivative(const Eigen::Ref< const Eigen::VectorXd > &x) const
Derivative of the penalty function ^ | | -1-1-1 0 0 0 +1+1+1 |---------------------—>
std::size_t size() const
Eigen::VectorXd penalty(const Eigen::Ref< const Eigen::VectorXd > &x) const
Distance to region inside bounds.
Eigen::MatrixXd calcErrorJacobian(const Eigen::Ref< const Eigen::VectorXd > &x) const override
For inequality constraints: calculate the Jacobian for the current parameters that are being constrai...
BoxConstraint(const moveit::core::RobotModelConstPtr &robot_model, const std::string &group, const unsigned int num_dofs)
Eigen::VectorXd calcError(const Eigen::Ref< const Eigen::VectorXd > &x) const override
For inequality constraints: calculate the value of the parameter that is being constrained by the bou...
void parseConstraintMsg(const moveit_msgs::msg::Constraints &constraints) override
Parse bounds on position parameters from MoveIt's constraint message.
void function(const Eigen::Ref< const Eigen::VectorXd > &joint_values, Eigen::Ref< Eigen::VectorXd > out) const override
void parseConstraintMsg(const moveit_msgs::msg::Constraints &constraints) override
Parse bounds on position parameters from MoveIt's constraint message.
void jacobian(const Eigen::Ref< const Eigen::VectorXd > &joint_values, Eigen::Ref< Eigen::MatrixXd > out) const override
EqualityPositionConstraint(const moveit::core::RobotModelConstPtr &robot_model, const std::string &group, const unsigned int num_dofs)
Eigen::MatrixXd calcErrorJacobian(const Eigen::Ref< const Eigen::VectorXd > &x) const override
For inequality constraints: calculate the Jacobian for the current parameters that are being constrai...
void parseConstraintMsg(const moveit_msgs::msg::Constraints &constraints) override
Parse bounds on orientation parameters from MoveIt's constraint message.
Eigen::VectorXd calcError(const Eigen::Ref< const Eigen::VectorXd > &x) const override
For inequality constraints: calculate the value of the parameter that is being constrained by the bou...
moveit::core::RobotState * getStateStorage() const
Vec3fX< details::Vec3Data< double > > Vector3d
Definition: fcl_compat.h:89
The MoveIt interface to OMPL.
Bounds orientationConstraintMsgToBoundVector(const moveit_msgs::msg::OrientationConstraint &ori_con)
Extract orientation constraints from the MoveIt message.
std::ostream & operator<<(std::ostream &os, const ompl_interface::Bounds &bounds)
Pretty printing of bounds.
Eigen::Matrix3d angularVelocityToAngleAxis(const double &angle, const Eigen::Ref< const Eigen::Vector3d > &axis)
Return a matrix to convert angular velocity to angle-axis velocity Based on: https://ethz....
ompl::base::ConstraintPtr createOMPLConstraints(const moveit::core::RobotModelConstPtr &robot_model, const std::string &group, const moveit_msgs::msg::Constraints &constraints)
Factory to create constraints based on what is in the MoveIt constraint message.
Bounds positionConstraintMsgToBoundVector(const moveit_msgs::msg::PositionConstraint &pos_con)
Extract position constraints from the MoveIt message.