moveit2
The MoveIt Motion Planning Framework for ROS 2.
cartesian_interpolator.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2013, Ioan A. Sucan
5  * Copyright (c) 2013, Willow Garage, Inc.
6  * Copyright (c) 2019, PickNik Inc.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * * Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  * * Neither the name of the Willow Garage nor the names of its
20  * contributors may be used to endorse or promote products derived
21  * from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *********************************************************************/
36 
37 /* Author: Ioan Sucan, Sachin Chitta, Acorn Pooley, Mario Prats, Dave Coleman */
38 
39 #include <memory>
41 #include <geometric_shapes/check_isometry.h>
42 #include <rclcpp/logger.hpp>
43 #include <rclcpp/logging.hpp>
44 #include <rcpputils/asserts.hpp>
45 #include <moveit/utils/logger.hpp>
46 
47 namespace moveit::core
48 {
49 
50 // Minimum amount of path waypoints recommended to reliably compute a joint-space increment average.
51 // If relative jump detection is selected and the path is shorter than `MIN_STEPS_FOR_JUMP_THRESH`, a warning message
52 // will be printed out.
53 static const std::size_t MIN_STEPS_FOR_JUMP_THRESH = 10;
54 
55 namespace
56 {
57 
58 rclcpp::Logger getLogger()
59 {
60  return moveit::getLogger("cartesian_interpolator");
61 }
62 
63 std::optional<int> hasRelativeJointSpaceJump(const std::vector<moveit::core::RobotStatePtr>& waypoints,
64  const moveit::core::JointModelGroup& group, double jump_threshold_factor)
65 {
66  if (waypoints.size() < MIN_STEPS_FOR_JUMP_THRESH)
67  {
68  RCLCPP_WARN(getLogger(),
69  "The computed path is too short to detect jumps in joint-space. "
70  "Need at least %zu steps, only got %zu. Try a lower max_step.",
71  MIN_STEPS_FOR_JUMP_THRESH, waypoints.size());
72  }
73 
74  std::vector<double> dist_vector;
75  dist_vector.reserve(waypoints.size() - 1);
76  double total_dist = 0.0;
77  for (std::size_t i = 1; i < waypoints.size(); ++i)
78  {
79  const double dist_prev_point = waypoints[i]->distance(*waypoints[i - 1], &group);
80  dist_vector.push_back(dist_prev_point);
81  total_dist += dist_prev_point;
82  }
83 
84  // compute the average distance between the states we looked at.
85  double thres = jump_threshold_factor * (total_dist / static_cast<double>(dist_vector.size()));
86  for (std::size_t i = 0; i < dist_vector.size(); ++i)
87  {
88  if (dist_vector[i] > thres)
89  {
90  return i + 1;
91  }
92  }
93 
94  return std::nullopt;
95 }
96 
97 std::optional<int> hasAbsoluteJointSpaceJump(const std::vector<moveit::core::RobotStatePtr>& waypoints,
98  const moveit::core::JointModelGroup& group, double revolute_threshold,
99  double prismatic_threshold)
100 {
101  const bool check_revolute = revolute_threshold > 0.0;
102  const bool check_prismatic = prismatic_threshold > 0.0;
103 
104  const std::vector<const moveit::core::JointModel*>& joints = group.getActiveJointModels();
105  for (std::size_t i = 1; i < waypoints.size(); ++i)
106  {
107  for (const auto& joint : joints)
108  {
109  const double distance = waypoints[i]->distance(*waypoints[i - 1], joint);
110  switch (joint->getType())
111  {
113  if (check_revolute && distance > revolute_threshold)
114  {
115  return i;
116  }
117  break;
119  if (check_prismatic && distance > prismatic_threshold)
120  {
121  return i;
122  }
123  break;
124  default:
125  RCLCPP_WARN(getLogger(),
126  "Joint %s has not supported type %s. \n"
127  "hasAbsoluteJointSpaceJump only supports prismatic and revolute joints. Skipping joint jump "
128  "check for this joint.",
129  joint->getName().c_str(), joint->getTypeName().c_str());
130  continue;
131  }
132  }
133  }
134 
135  return std::nullopt;
136 }
137 } // namespace
138 
140 {
141  return JumpThreshold();
142 }
143 
144 JumpThreshold JumpThreshold::relative(double relative_factor)
145 {
146  rcpputils::require_true(relative_factor > 1.0);
147 
148  JumpThreshold threshold;
149  threshold.relative_factor = relative_factor;
150  return threshold;
151 }
152 
153 JumpThreshold JumpThreshold::absolute(double revolute, double prismatic)
154 {
155  rcpputils::require_true(revolute > 0.0);
156  rcpputils::require_true(prismatic > 0.0);
157 
158  JumpThreshold threshold;
159  threshold.revolute = revolute;
160  threshold.prismatic = prismatic;
161  return threshold;
162 }
163 
164 JumpThreshold::JumpThreshold(double relative_factor)
165 {
166  this->relative_factor = relative_factor;
167 }
168 JumpThreshold::JumpThreshold(double revolute, double prismatic)
169 {
170  this->revolute = revolute;
171  this->prismatic = prismatic;
172 }
173 
175  RobotState* start_state, const JointModelGroup* group, std::vector<RobotStatePtr>& path, const LinkModel* link,
176  const Eigen::Vector3d& translation, bool global_reference_frame, const MaxEEFStep& max_step,
177  const JumpThreshold& jump_threshold, const GroupStateValidityCallbackFn& validCallback,
179 {
180  const double distance = translation.norm();
181  // The target pose is obtained by adding the translation vector to the link's current pose
182  Eigen::Isometry3d pose = start_state->getGlobalLinkTransform(link);
183 
184  // the translation direction can be specified w.r.t. the local link frame (then rotate into global frame)
185  pose.translation() += global_reference_frame ? translation : pose.linear() * translation;
186 
187  // call computeCartesianPath for the computed target pose in the global reference frame
188  return CartesianInterpolator::Distance(distance) * computeCartesianPath(start_state, group, path, link, pose, true,
189  max_step, jump_threshold, validCallback,
190  options, cost_function);
191 }
192 
194  RobotState* start_state, const JointModelGroup* group, std::vector<RobotStatePtr>& path, const LinkModel* link,
195  const Eigen::Isometry3d& target, bool global_reference_frame, const MaxEEFStep& max_step,
196  const JumpThreshold& jump_threshold, const GroupStateValidityCallbackFn& validCallback,
198  const Eigen::Isometry3d& link_offset)
199 {
200  // check unsanitized inputs for non-isometry
201  ASSERT_ISOMETRY(target)
202  ASSERT_ISOMETRY(link_offset)
203 
204  const std::vector<const JointModel*>& cjnt = group->getContinuousJointModels();
205  // make sure that continuous joints wrap
206  for (const JointModel* joint : cjnt)
207  start_state->enforceBounds(joint);
208 
209  // Cartesian pose we start from
210  Eigen::Isometry3d start_pose = start_state->getGlobalLinkTransform(link) * link_offset;
211  Eigen::Isometry3d offset = link_offset.inverse();
212 
213  // the target can be in the local reference frame (in which case we rotate it)
214  Eigen::Isometry3d rotated_target = global_reference_frame ? target : start_pose * target;
215 
216  Eigen::Quaterniond start_quaternion(start_pose.linear());
217  Eigen::Quaterniond target_quaternion(rotated_target.linear());
218 
219  if (max_step.translation <= 0.0 && max_step.rotation <= 0.0)
220  {
221  RCLCPP_ERROR(getLogger(),
222  "Invalid MaxEEFStep passed into computeCartesianPath. Both the MaxEEFStep.rotation and "
223  "MaxEEFStep.translation components must be non-negative and at least one component must be "
224  "greater than zero");
225  return 0.0;
226  }
227 
228  double rotation_distance = start_quaternion.angularDistance(target_quaternion);
229  double translation_distance = (rotated_target.translation() - start_pose.translation()).norm();
230 
231  // decide how many steps we will need for this path
232  std::size_t translation_steps = 0;
233  if (max_step.translation > 0.0)
234  translation_steps = floor(translation_distance / max_step.translation);
235 
236  std::size_t rotation_steps = 0;
237  if (max_step.rotation > 0.0)
238  rotation_steps = floor(rotation_distance / max_step.rotation);
239 
240  // If we are testing for relative jumps, we always want at least MIN_STEPS_FOR_JUMP_THRESH steps
241  std::size_t steps = std::max(translation_steps, rotation_steps) + 1;
242  if (jump_threshold.relative_factor > 0 && steps < MIN_STEPS_FOR_JUMP_THRESH)
243  steps = MIN_STEPS_FOR_JUMP_THRESH;
244 
245  // To limit absolute joint-space jumps, we pass consistency limits to the IK solver
246  std::vector<double> consistency_limits;
247  if (jump_threshold.prismatic > 0 || jump_threshold.revolute > 0)
248  {
249  for (const JointModel* jm : group->getActiveJointModels())
250  {
251  double limit;
252  switch (jm->getType())
253  {
255  limit = jump_threshold.revolute;
256  break;
258  limit = jump_threshold.prismatic;
259  break;
260  default:
261  limit = 0.0;
262  }
263  if (limit == 0.0)
264  limit = jm->getMaximumExtent();
265  consistency_limits.push_back(limit);
266  }
267  }
268 
269  path.clear();
270  path.push_back(std::make_shared<moveit::core::RobotState>(*start_state));
271 
272  double last_valid_percentage = 0.0;
273  for (std::size_t i = 1; i <= steps; ++i)
274  {
275  double percentage = static_cast<double>(i) / static_cast<double>(steps);
276 
277  Eigen::Isometry3d pose(start_quaternion.slerp(percentage, target_quaternion));
278  pose.translation() = percentage * rotated_target.translation() + (1 - percentage) * start_pose.translation();
279 
280  // Explicitly use a single IK attempt only (by setting a timeout of 0.0), using the current state as the seed.
281  // Random seeding (of additional attempts) would create large joint-space jumps.
282  if (start_state->setFromIK(group, pose * offset, link->getName(), consistency_limits, 0.0, validCallback, options,
283  cost_function))
284  {
285  path.push_back(std::make_shared<moveit::core::RobotState>(*start_state));
286  }
287  else
288  {
289  break;
290  }
291 
292  last_valid_percentage = percentage;
293  }
294 
295  last_valid_percentage *= checkJointSpaceJump(group, path, jump_threshold);
296 
297  return CartesianInterpolator::Percentage(last_valid_percentage);
298 }
299 
300 CartesianInterpolator::Percentage CartesianInterpolator::computeCartesianPath(
301  RobotState* start_state, const JointModelGroup* group, std::vector<RobotStatePtr>& path, const LinkModel* link,
302  const EigenSTL::vector_Isometry3d& waypoints, bool global_reference_frame, const MaxEEFStep& max_step,
303  const JumpThreshold& jump_threshold, const GroupStateValidityCallbackFn& validCallback,
305  const Eigen::Isometry3d& link_offset)
306 {
307  double percentage_solved = 0.0;
308  for (std::size_t i = 0; i < waypoints.size(); ++i)
309  {
310  // Don't test joint space jumps for every waypoint, test them later on the whole path.
311  static const JumpThreshold NO_JOINT_SPACE_JUMP_TEST = JumpThreshold::disabled();
312  std::vector<RobotStatePtr> waypoint_path;
313  double wp_percentage_solved =
314  computeCartesianPath(start_state, group, waypoint_path, link, waypoints[i], global_reference_frame, max_step,
315  NO_JOINT_SPACE_JUMP_TEST, validCallback, options, cost_function, link_offset);
316  if (fabs(wp_percentage_solved - 1.0) < std::numeric_limits<double>::epsilon())
317  {
318  percentage_solved = static_cast<double>((i + 1)) / static_cast<double>(waypoints.size());
319  std::vector<RobotStatePtr>::iterator start = waypoint_path.begin();
320  if (i > 0 && !waypoint_path.empty())
321  std::advance(start, 1);
322  path.insert(path.end(), start, waypoint_path.end());
323  }
324  else
325  {
326  percentage_solved += wp_percentage_solved / static_cast<double>(waypoints.size());
327  std::vector<RobotStatePtr>::iterator start = waypoint_path.begin();
328  if (i > 0 && !waypoint_path.empty())
329  std::advance(start, 1);
330  path.insert(path.end(), start, waypoint_path.end());
331  break;
332  }
333  }
334 
335  percentage_solved *= checkJointSpaceJump(group, path, jump_threshold);
336 
337  return CartesianInterpolator::Percentage(percentage_solved);
338 }
339 
341  std::vector<RobotStatePtr>& path,
342  const JumpThreshold& jump_threshold)
343 {
344  std::optional<int> jump_index = hasJointSpaceJump(path, *group, jump_threshold);
345 
346  double percentage_solved = 1.0;
347  if (jump_index.has_value())
348  {
349  percentage_solved = static_cast<double>(*jump_index) / static_cast<double>(path.size());
350  // Truncate the path at the index right before the jump.
351  path.resize(jump_index.value());
352  }
353 
354  return CartesianInterpolator::Percentage(percentage_solved);
355 }
356 
357 std::optional<int> hasJointSpaceJump(const std::vector<moveit::core::RobotStatePtr>& waypoints,
358  const moveit::core::JointModelGroup& group,
359  const moveit::core::JumpThreshold& jump_threshold)
360 {
361  if (waypoints.size() <= 1)
362  {
363  return std::nullopt;
364  }
365 
366  if (jump_threshold.relative_factor > 0.0)
367  {
368  return hasRelativeJointSpaceJump(waypoints, group, jump_threshold.relative_factor);
369  }
370 
371  if (jump_threshold.revolute > 0.0 || jump_threshold.prismatic > 0.0)
372  {
373  return hasAbsoluteJointSpaceJump(waypoints, group, jump_threshold.revolute, jump_threshold.prismatic);
374  }
375 
376  return std::nullopt;
377 }
378 
379 } // end of namespace moveit::core
std::function< double(const geometry_msgs::msg::Pose &, const moveit::core::RobotState &, const moveit::core::JointModelGroup *, const std::vector< double > &)> IKCostFn
Signature for a cost function used to evaluate IK solutions.
static Distance computeCartesianPath(RobotState *start_state, const JointModelGroup *group, std::vector< std::shared_ptr< RobotState >> &path, const LinkModel *link, const Eigen::Vector3d &translation, bool global_reference_frame, const MaxEEFStep &max_step, const JumpThreshold &jump_threshold, const GroupStateValidityCallbackFn &validCallback=GroupStateValidityCallbackFn(), const kinematics::KinematicsQueryOptions &options=kinematics::KinematicsQueryOptions(), const kinematics::KinematicsBase::IKCostFn &cost_function=kinematics::KinematicsBase::IKCostFn())
Compute the sequence of joint values that correspond to a straight Cartesian path for a particular li...
static Percentage checkJointSpaceJump(const JointModelGroup *group, std::vector< std::shared_ptr< RobotState >> &path, const JumpThreshold &jump_threshold)
Checks if a path has a joint-space jump, and truncates the path at the jump.
const std::vector< const JointModel * > & getActiveJointModels() const
Get the active joints in this group (that have controllable DOF). This does not include mimic joints.
const std::vector< const JointModel * > & getContinuousJointModels() const
Get the array of continuous joints used in this group (may include mimic joints).
A joint from the robot. Models the transform that this joint applies in the kinematic chain....
Definition: joint_model.h:117
A link from the robot. Contains the constant transform applied to the link and its geometry.
Definition: link_model.h:72
const std::string & getName() const
The name of this link.
Definition: link_model.h:86
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
bool setFromIK(const JointModelGroup *group, const geometry_msgs::msg::Pose &pose, double timeout=0.0, const GroupStateValidityCallbackFn &constraint=GroupStateValidityCallbackFn(), const kinematics::KinematicsQueryOptions &options=kinematics::KinematicsQueryOptions(), const kinematics::KinematicsBase::IKCostFn &cost_function=kinematics::KinematicsBase::IKCostFn())
If the group this state corresponds to is a chain and a solver is available, then the joint values ca...
Vec3fX< details::Vec3Data< double > > Vector3d
Definition: fcl_compat.h:89
Core components of MoveIt.
std::function< bool(RobotState *robot_state, const JointModelGroup *joint_group, const double *joint_group_variable_values)> GroupStateValidityCallbackFn
Signature for functions that can verify that if the group joint_group in robot_state is set to joint_...
Definition: robot_state.h:70
std::optional< int > hasJointSpaceJump(const std::vector< moveit::core::RobotStatePtr > &waypoints, const moveit::core::JointModelGroup &group, const moveit::core::JumpThreshold &jump_threshold)
Checks if a joint-space path has a jump larger than the given threshold.
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition: logger.cpp:79
double distance(const urdf::Pose &transform)
Definition: pr2_arm_ik.h:55
A set of options for the kinematics solver.
Struct with options for defining joint-space jump thresholds.
static JumpThreshold relative(double relative_factor)
Detect joint-space jumps relative to the average joint-space displacement along the path.
static JumpThreshold absolute(double revolute, double prismatic)
Detect joint-space jumps greater than the given absolute thresholds.
static JumpThreshold disabled()
Do not define any jump threshold, i.e., disable joint-space jump detection.
Struct for containing max_step for computeCartesianPath.