moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
trajectory_generator_polyline.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 * Copyright (c) 2025 Aiman Haidar
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 * * Neither the name of Pilz GmbH & Co. KG nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 *********************************************************************/
35
38
40
41#include <cassert>
42#include <sstream>
43#include <time.h>
45#include <kdl/path_roundedcomposite.hpp>
46#include <kdl/trajectory_segment.hpp>
47#include <kdl/utilities/error.h>
48// TODO: Remove conditional include when released to all active distros.
49#if __has_include(<tf2/convert.hpp>)
50#include <tf2/convert.hpp>
51#else
52#include <tf2/convert.h>
53#endif
54#include <rclcpp/logger.hpp>
55#include <rclcpp/logging.hpp>
56#include <tf2_eigen/tf2_eigen.hpp>
57#include <tf2_eigen_kdl/tf2_eigen_kdl.hpp>
58#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
60
62{
63namespace
64{
65rclcpp::Logger getLogger()
66{
67 return moveit::getLogger("moveit.planners.pilz.trajectory_generator.polyline");
68}
69} // namespace
70TrajectoryGeneratorPolyline::TrajectoryGeneratorPolyline(const moveit::core::RobotModelConstPtr& robot_model,
71 const LimitsContainer& planner_limits,
72 const std::string& /*group_name*/)
73 : TrajectoryGenerator::TrajectoryGenerator(robot_model, planner_limits)
74{
75 planner_limits_.printCartesianLimits();
76}
77
78void TrajectoryGeneratorPolyline::extractMotionPlanInfo(const planning_scene::PlanningSceneConstPtr& scene,
81{
82 RCLCPP_DEBUG(getLogger(), "Extract necessary information from motion plan request.");
83
84 info.group_name = req.group_name;
85 moveit::core::RobotState robot_state = scene->getCurrentState();
86
87 std::string frame_id;
88
89 info.link_name = req.goal_constraints.front().position_constraints.front().link_name;
90 if (req.goal_constraints.front().position_constraints.front().header.frame_id.empty() ||
91 req.goal_constraints.front().orientation_constraints.front().header.frame_id.empty())
92 {
93 RCLCPP_WARN(getLogger(), "Frame id is not set in position/orientation constraints of "
94 "goal. Use model frame as default");
95 frame_id = robot_model_->getModelFrame();
96 }
97 else
98 {
99 frame_id = req.goal_constraints.front().position_constraints.front().header.frame_id;
100 }
101
102 // Add the path waypoints
103 for (const auto& pc : req.path_constraints.position_constraints)
104 {
105 Eigen::Isometry3d waypoint;
106 waypoint = getConstraintPose(pc.constraint_region.primitive_poses.front().position,
107 pc.constraint_region.primitive_poses.front().orientation, pc.target_point_offset);
108 waypoint = scene->getFrameTransform(frame_id) * waypoint;
109 info.waypoints.push_back(waypoint);
110 }
111 // goal constraint is just the final pose
112 info.goal_pose = scene->getFrameTransform(frame_id) * getConstraintPose(req.goal_constraints.front());
113 frame_id = robot_model_->getModelFrame();
114
115 // check goal pose ik before Cartesian motion plan starts
116 std::map<std::string, double> ik_solution;
117 if (!computePoseIK(scene, info.group_name, info.link_name, info.goal_pose, frame_id, info.start_joint_position,
118 ik_solution))
119 {
120 std::ostringstream os;
121 os << "Failed to compute inverse kinematics for link: " << info.link_name << " of goal pose";
122 throw LinInverseForGoalIncalculable(os.str());
123 }
124
125 // Ignored return value because at this point the function should always
126 // return 'true'.
127 computeLinkFK(robot_state, info.link_name, info.start_joint_position, info.start_pose);
128}
129
130void TrajectoryGeneratorPolyline::plan(const planning_scene::PlanningSceneConstPtr& scene,
132 const MotionPlanInfo& plan_info, double sampling_time,
133 trajectory_msgs::msg::JointTrajectory& joint_trajectory)
134{
135 // set pilz cartesian limits for each item
137 // create Cartesian POLYLINE path
138 std::unique_ptr<KDL::Path> path;
139 try
140 {
141 path = setPathPolyline(plan_info.start_pose, plan_info.waypoints, req.smoothness_level);
142 }
143 catch (const KDL::Error_MotionPlanning& e)
144 {
145 RCLCPP_ERROR(getLogger(), "Motion planning error: %s", e.Description());
146 int code = e.GetType();
147 std::ostringstream os;
148 if (code == 3102 || code == 3103)
149 {
150 os << "zero distance between two points";
151 }
152 else if (code == 3104)
153 {
154 os << "waypoints specified in path constraints have three consicutive colinear points";
155 }
156 else if (code == 3105 || code == 3106)
157 {
158 os << "rounding circle of a point is bigger than the distance with one of the neighbor points";
159 }
160 else if (code == 3001 || code == 3002)
161 {
162 os << "the rounding radius is lower than KDL::epsilon. use bigger smoothness or resample your waypoints";
163 }
164 throw ConsicutiveColinearWaypoints(os.str());
165 }
166 // create velocity profile
167 std::unique_ptr<KDL::VelocityProfile> vp(
168 cartesianTrapVelocityProfile(req.max_velocity_scaling_factor, req.max_acceleration_scaling_factor, path));
169
170 // combine path and velocity profile into Cartesian trajectory
171 // with the third parameter set to false, KDL::Trajectory_Segment does not
172 // take
173 // the ownship of Path and Velocity Profile
174 KDL::Trajectory_Segment cart_trajectory(path.get(), vp.get(), false);
175
176 moveit_msgs::msg::MoveItErrorCodes error_code;
177 // sample the Cartesian trajectory and compute joint trajectory using inverse
178 // kinematics
179 if (!generateJointTrajectory(scene, planner_limits_.getJointLimitContainer(), cart_trajectory, plan_info.group_name,
180 plan_info.link_name, plan_info.start_joint_position, sampling_time, joint_trajectory,
181 error_code))
182 {
183 std::ostringstream os;
184 os << "Failed to generate valid joint trajectory from the Cartesian path";
185 throw LinTrajectoryConversionFailure(os.str(), error_code.val);
186 }
187}
188
189std::unique_ptr<KDL::Path> TrajectoryGeneratorPolyline::setPathPolyline(const Eigen::Affine3d& start_pose,
190 const std::vector<Eigen::Isometry3d>& waypoints,
191 double smoothness_level) const
192{
193 RCLCPP_DEBUG(getLogger(), "Set Cartesian path for POLYLINE command.");
194
195 KDL::Frame kdl_start_pose;
196 tf2::transformEigenToKDL(start_pose, kdl_start_pose);
197 // transform waypoints to KDL frames
198 std::vector<KDL::Frame> kdl_waypoints;
199 for (const auto& waypoint : waypoints)
200 {
201 KDL::Frame kdl_waypoint;
202 tf2::transformEigenToKDL(waypoint, kdl_waypoint);
203 kdl_waypoints.push_back(kdl_waypoint);
204 }
205
206 RCLCPP_INFO_STREAM(getLogger(), "Transformed waypoints number: " << kdl_waypoints.size());
207
208 double eqradius = max_cartesian_speed_ / planner_limits_.getCartesianLimits().max_rot_vel;
209 KDL::RotationalInterpolation* rot_interpo = new KDL::RotationalInterpolation_SingleAxis();
210
211 return PathPolylineGenerator::polylineFromWaypoints(kdl_start_pose, kdl_waypoints, rot_interpo, smoothness_level,
212 eqradius);
213}
214
215void TrajectoryGeneratorPolyline::cmdSpecificRequestValidation(const planning_interface::MotionPlanRequest& req) const
216{
217 if (req.path_constraints.position_constraints.size() < 2)
218 {
219 std::ostringstream os;
220 os << "waypoints specified in path constraints is less than 2 for POLYLINE motion.";
221 throw NoWaypointsSpecified(os.str());
222 }
223}
224} // namespace pilz_industrial_motion_planner
Representation of a robot's state. This includes position, velocity, acceleration and effort.
This class combines CartesianLimit and JointLimits into on single class.
static std::unique_ptr< KDL::Path > polylineFromWaypoints(const KDL::Frame &start_pose, const std::vector< KDL::Frame > &waypoints, KDL::RotationalInterpolation *rot_interpo, double smoothness, double eqradius)
set the path polyline from waypoints
TrajectoryGeneratorPolyline(const moveit::core::RobotModelConstPtr &robot_model, const pilz_industrial_motion_planner::LimitsContainer &planner_limits, const std::string &group_name)
Constructor of Polyline Trajectory Generator.
This class is used to extract needed information from motion plan request.
const moveit::core::RobotModelConstPtr robot_model_
TrajectoryGenerator(const moveit::core::RobotModelConstPtr &robot_model, const pilz_industrial_motion_planner::LimitsContainer &planner_limits)
const pilz_industrial_motion_planner::LimitsContainer planner_limits_
std::unique_ptr< KDL::VelocityProfile > cartesianTrapVelocityProfile(double max_velocity_scaling_factor, double max_acceleration_scaling_factor, const std::unique_ptr< KDL::Path > &path) const
build cartesian velocity profile for the path
void setMaxCartesianSpeed(const moveit_msgs::msg::MotionPlanRequest &req)
Set the max cartesian speed from motion request.
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition logger.cpp:79
bool computeLinkFK(moveit::core::RobotState &robot_state, const std::string &link_name, const std::map< std::string, double > &joint_state, Eigen::Isometry3d &pose)
compute the pose of a link at a given robot state
bool generateJointTrajectory(const planning_scene::PlanningSceneConstPtr &scene, const JointLimitsContainer &joint_limits, const KDL::Trajectory &trajectory, const std::string &group_name, const std::string &link_name, const std::map< std::string, double > &initial_joint_position, double sampling_time, trajectory_msgs::msg::JointTrajectory &joint_trajectory, moveit_msgs::msg::MoveItErrorCodes &error_code, bool check_self_collision=false)
Generate joint trajectory from a KDL Cartesian trajectory.
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.