moveit2
The MoveIt Motion Planning Framework for ROS 2.
trajectory_generator_ptp.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2018 Pilz GmbH & Co. KG
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 Pilz GmbH & Co. KG 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 
37 #include <moveit/utils/logger.hpp>
38 
39 #include <rclcpp/duration.hpp>
40 #include <rclcpp/logger.hpp>
41 #include <rclcpp/logging.hpp>
42 #include <iostream>
43 #include <sstream>
44 
45 #include <tf2_eigen/tf2_eigen.hpp>
46 #include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
47 
49 {
50 namespace
51 {
52 rclcpp::Logger getLogger()
53 {
54  return moveit::getLogger("moveit.planners.pilz.trajectory_generator.ptp");
55 }
56 } // namespace
57 TrajectoryGeneratorPTP::TrajectoryGeneratorPTP(const moveit::core::RobotModelConstPtr& robot_model,
58  const LimitsContainer& planner_limits, const std::string& group_name)
59  : TrajectoryGenerator::TrajectoryGenerator(robot_model, planner_limits)
60 {
62  {
63  throw TrajectoryGeneratorInvalidLimitsException("joint limit not set");
64  }
65 
66  joint_limits_ = planner_limits_.getJointLimitContainer();
67 
68  // collect most strict joint limits for each group in robot model
69  const auto* jmg = robot_model->getJointModelGroup(group_name);
70  if (!jmg)
71  throw TrajectoryGeneratorInvalidLimitsException("invalid group: " + group_name);
72 
73  const auto& active_joints = jmg->getActiveJointModelNames();
74 
75  // no active joints
76  if (!active_joints.empty())
77  {
78  most_strict_limit_ = joint_limits_.getCommonLimit(active_joints);
79 
80  if (!most_strict_limit_.has_velocity_limits)
81  {
82  throw TrajectoryGeneratorInvalidLimitsException("velocity limit not set for group " + group_name);
83  }
84  if (!most_strict_limit_.has_acceleration_limits)
85  {
86  throw TrajectoryGeneratorInvalidLimitsException("acceleration limit not set for group " + group_name);
87  }
88  if (!most_strict_limit_.has_deceleration_limits)
89  {
90  throw TrajectoryGeneratorInvalidLimitsException("deceleration limit not set for group " + group_name);
91  }
92  }
93 
94  RCLCPP_INFO(getLogger(), "Initialized Point-to-Point Trajectory Generator.");
95 }
96 
97 void TrajectoryGeneratorPTP::planPTP(const std::map<std::string, double>& start_pos,
98  const std::map<std::string, double>& goal_pos,
99  trajectory_msgs::msg::JointTrajectory& joint_trajectory,
100  double velocity_scaling_factor, double acceleration_scaling_factor,
101  double sampling_time)
102 {
103  // initialize joint names
104  for (const auto& item : goal_pos)
105  {
106  joint_trajectory.joint_names.push_back(item.first);
107  }
108 
109  // check if goal already reached
110  bool goal_reached = true;
111  for (const auto& goal : goal_pos)
112  {
113  if (fabs(start_pos.at(goal.first) - goal.second) >= MIN_MOVEMENT)
114  {
115  goal_reached = false;
116  break;
117  }
118  }
119  if (goal_reached)
120  {
121  RCLCPP_INFO_STREAM(getLogger(), "Goal already reached, set one goal point explicitly.");
122  if (joint_trajectory.points.empty())
123  {
124  trajectory_msgs::msg::JointTrajectoryPoint point;
125  point.time_from_start = rclcpp::Duration::from_seconds(sampling_time);
126  for (const std::string& joint_name : joint_trajectory.joint_names)
127  {
128  point.positions.push_back(start_pos.at(joint_name));
129  point.velocities.push_back(0);
130  point.accelerations.push_back(0);
131  }
132  joint_trajectory.points.push_back(point);
133  }
134  return;
135  }
136 
137  // compute the fastest trajectory and choose the slowest joint as leading axis
138  std::string leading_axis = joint_trajectory.joint_names.front();
139  double max_duration = -1.0;
140 
141  std::map<std::string, VelocityProfileATrap> velocity_profile;
142  for (const auto& joint_name : joint_trajectory.joint_names)
143  {
144  // create vecocity profile if necessary
145  velocity_profile.insert(std::make_pair(
146  joint_name, VelocityProfileATrap(velocity_scaling_factor * most_strict_limit_.max_velocity,
147  acceleration_scaling_factor * most_strict_limit_.max_acceleration,
148  acceleration_scaling_factor * most_strict_limit_.max_deceleration)));
149 
150  velocity_profile.at(joint_name).SetProfile(start_pos.at(joint_name), goal_pos.at(joint_name));
151  if (velocity_profile.at(joint_name).Duration() > max_duration)
152  {
153  max_duration = velocity_profile.at(joint_name).Duration();
154  leading_axis = joint_name;
155  }
156  }
157 
158  // Full Synchronization
159  // This should only work if all axes have same max_vel, max_acc, max_dec
160  // values
161  // reset the velocity profile for other joints
162  double acc_time = velocity_profile.at(leading_axis).firstPhaseDuration();
163  double const_time = velocity_profile.at(leading_axis).secondPhaseDuration();
164  double dec_time = velocity_profile.at(leading_axis).thirdPhaseDuration();
165 
166  for (const auto& joint_name : joint_trajectory.joint_names)
167  {
168  if (joint_name != leading_axis)
169  {
170  // make full synchronization
171  // causes the program to terminate if acc_time<=0 or dec_time<=0 (should
172  // be prevented by goal_reached block above)
173  // by using the most strict limit, the following should always return true
174  if (!velocity_profile.at(joint_name)
175  .setProfileAllDurations(start_pos.at(joint_name), goal_pos.at(joint_name), acc_time, const_time,
176  dec_time))
177  // LCOV_EXCL_START
178  {
179  std::stringstream error_str;
180  error_str << "TrajectoryGeneratorPTP::planPTP(): Can not synchronize "
181  "velocity profile of axis "
182  << joint_name << " with leading axis " << leading_axis;
183  throw PtpVelocityProfileSyncFailed(error_str.str());
184  }
185  // LCOV_EXCL_STOP
186  }
187  }
188 
189  // first generate the time samples
190  std::vector<double> time_samples;
191  for (double t_sample = 0.0; t_sample < max_duration; t_sample += sampling_time)
192  {
193  time_samples.push_back(t_sample);
194  }
195  // add last time
196  time_samples.push_back(max_duration);
197 
198  // construct joint trajectory point
199  for (double time_stamp : time_samples)
200  {
201  trajectory_msgs::msg::JointTrajectoryPoint point;
202  point.time_from_start = rclcpp::Duration::from_seconds(time_stamp);
203  for (std::string& joint_name : joint_trajectory.joint_names)
204  {
205  point.positions.push_back(velocity_profile.at(joint_name).Pos(time_stamp));
206  point.velocities.push_back(velocity_profile.at(joint_name).Vel(time_stamp));
207  point.accelerations.push_back(velocity_profile.at(joint_name).Acc(time_stamp));
208  }
209  joint_trajectory.points.push_back(point);
210  }
211 
212  // Set last point velocity and acceleration to zero
213  std::fill(joint_trajectory.points.back().velocities.begin(), joint_trajectory.points.back().velocities.end(), 0.0);
214  std::fill(joint_trajectory.points.back().accelerations.begin(), joint_trajectory.points.back().accelerations.end(),
215  0.0);
216 }
217 
218 void TrajectoryGeneratorPTP::extractMotionPlanInfo(const planning_scene::PlanningSceneConstPtr& scene,
220  MotionPlanInfo& info) const
221 {
222  info.group_name = req.group_name;
223 
224  // extract goal
225  info.goal_joint_position.clear();
226  if (!req.goal_constraints.at(0).joint_constraints.empty())
227  {
228  for (const auto& joint_constraint : req.goal_constraints.at(0).joint_constraints)
229  {
230  info.goal_joint_position[joint_constraint.joint_name] = joint_constraint.position;
231  }
232  }
233  // solve the ik
234  else
235  {
236  std::string frame_id;
237 
238  info.link_name = req.goal_constraints.front().position_constraints.front().link_name;
239  if (req.goal_constraints.front().position_constraints.front().header.frame_id.empty() ||
240  req.goal_constraints.front().orientation_constraints.front().header.frame_id.empty())
241  {
242  RCLCPP_WARN(getLogger(), "Frame id is not set in position/orientation constraints of "
243  "goal. Use model frame as default");
244  frame_id = robot_model_->getModelFrame();
245  }
246  else
247  {
248  frame_id = req.goal_constraints.front().position_constraints.front().header.frame_id;
249  }
250 
251  // goal pose with optional offset wrt. the planning frame
252  info.goal_pose = scene->getFrameTransform(frame_id) * getConstraintPose(req.goal_constraints.front());
253  frame_id = robot_model_->getModelFrame();
254 
255  // check goal pose ik before Cartesian motion plan start
256  if (!computePoseIK(scene, info.group_name, info.link_name, info.goal_pose, frame_id, info.start_joint_position,
257  info.goal_joint_position))
258  {
259  std::ostringstream os;
260  os << "Failed to compute inverse kinematics for link: " << info.link_name << " of goal pose";
261  throw PtpNoIkSolutionForGoalPose(os.str());
262  }
263  }
264 }
265 
266 void TrajectoryGeneratorPTP::plan(const planning_scene::PlanningSceneConstPtr& /*scene*/,
267  const planning_interface::MotionPlanRequest& req, const MotionPlanInfo& plan_info,
268  double sampling_time, trajectory_msgs::msg::JointTrajectory& joint_trajectory)
269 {
270  // plan the ptp trajectory
271  planPTP(plan_info.start_joint_position, plan_info.goal_joint_position, joint_trajectory,
272  req.max_velocity_scaling_factor, req.max_acceleration_scaling_factor, sampling_time);
273 }
274 
275 } // namespace pilz_industrial_motion_planner
JointLimit getCommonLimit() const
Returns joint limit fusion of all(position, velocity, acceleration, deceleration) limits for all join...
This class combines CartesianLimit and JointLimits into on single class.
bool hasJointLimits() const
Return if this LimitsContainer has defined joint limits.
const JointLimitsContainer & getJointLimitContainer() const
Obtain the Joint Limits from the container.
TrajectoryGeneratorPTP(const moveit::core::RobotModelConstPtr &robot_model, const pilz_industrial_motion_planner::LimitsContainer &planner_limits, const std::string &group_name)
Constructor of PTP Trajectory Generator.
const moveit::core::RobotModelConstPtr robot_model_
const pilz_industrial_motion_planner::LimitsContainer planner_limits_
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition: logger.cpp:79
bool computePoseIK(const planning_scene::PlanningSceneConstPtr &scene, const std::string &group_name, const std::string &link_name, const Eigen::Isometry3d &pose, const std::string &frame_id, const std::map< std::string, double > &seed, std::map< std::string, double > &solution, bool check_self_collision=true, const double timeout=0.0)
compute the inverse kinematics of a given pose, also check robot self collision
moveit_msgs::msg::MotionPlanRequest MotionPlanRequest
double max_deceleration
maximum deceleration MUST(!) be negative
Eigen::Isometry3d getConstraintPose(const geometry_msgs::msg::Point &position, const geometry_msgs::msg::Quaternion &orientation, const geometry_msgs::msg::Vector3 &offset)
Adapt goal pose, defined by position+orientation, to consider offset.