moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
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
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{
50namespace
51{
52rclcpp::Logger getLogger()
53{
54 return moveit::getLogger("moveit.planners.pilz.trajectory_generator.ptp");
55}
56} // namespace
57TrajectoryGeneratorPTP::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
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
97void 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
218void 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
266void 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
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.