moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
path_polyline_generator.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
37
39{
40std::unique_ptr<KDL::Path> PathPolylineGenerator::polylineFromWaypoints(const KDL::Frame& start_pose,
41 const std::vector<KDL::Frame>& waypoints,
42 KDL::RotationalInterpolation* rot_interpo,
43 double smoothness, double eqradius)
44{
45 std::vector<KDL::Frame> filtered_waypoints = filterWaypoints(start_pose, waypoints);
46 double blend_radius = computeBlendRadius(filtered_waypoints, smoothness);
47
48 KDL::Path_RoundedComposite* composite_path = new KDL::Path_RoundedComposite(blend_radius, eqradius, rot_interpo);
49
50 for (const auto& waypoint : filtered_waypoints)
51 {
52 composite_path->Add(waypoint);
53 }
54
55 composite_path->Finish();
56 return std::unique_ptr<KDL::Path>(composite_path);
57}
58
59std::vector<KDL::Frame> PathPolylineGenerator::filterWaypoints(const KDL::Frame& start_pose,
60 const std::vector<KDL::Frame>& waypoints)
61{
62 std::vector<KDL::Frame> filtered_waypoints = {};
63
64 // index for the last add point
65 filtered_waypoints.push_back(start_pose);
66 int last_added_point_indx = -1; // -1 for the start_pose
67
68 // the following is to remove very close waypoints
69 // to avoid issues in KDL::Path_RoundedComposite like throwing Not_Fesible exceptions
70 // to get the last added point in the rounded composite path
71 auto last_point = [&]() { return last_added_point_indx != -1 ? waypoints[last_added_point_indx].p : start_pose.p; };
72 // distance between start pose and first waypoint
73 double dist;
74
75 // add points and skip the points which are too close to each other
76 for (const auto& waypoint : waypoints)
77 {
78 dist = (last_point() - waypoint.p).Norm();
79 if (dist > MIN_SEGMENT_LENGTH)
80 {
81 filtered_waypoints.push_back(waypoint);
82 ++last_added_point_indx;
83 }
84 }
85 return filtered_waypoints;
86}
87double PathPolylineGenerator::computeBlendRadius(const std::vector<KDL::Frame>& waypoints_, double smoothness)
88{
89 double max_allowed_radius = std::numeric_limits<double>::infinity();
90
91 auto pose_distance = [](const KDL::Frame& p1, const KDL::Frame& p2) { return (p1.p - p2.p).Norm(); };
92
93 // to calculate the angle between the two segments of p2
94 auto segment_angle = [](const KDL::Frame& p1, const KDL::Frame& p2, const KDL::Frame& p3) {
95 KDL::Vector v1 = p2.p - p1.p;
96 KDL::Vector v2 = p2.p - p3.p;
97
98 double norm_product = v1.Norm() * v2.Norm();
99 if (norm_product < MIN_SEGMENT_LENGTH * MIN_SEGMENT_LENGTH)
100 return 0.0; // avoid division by zero
101
102 double cos_theta = KDL::dot(v1, v2) / norm_product;
103 cos_theta = std::clamp(cos_theta, -1.0, 1.0);
104
105 return std::acos(cos_theta);
106 };
107
108 for (size_t i = 1; i + 1 < waypoints_.size(); ++i)
109 {
110 double dist1 = pose_distance(waypoints_[i], waypoints_[i - 1]);
111 double dist2 = pose_distance(waypoints_[i + 1], waypoints_[i]);
112 checkConsecutiveColinearWaypoints(waypoints_[i - 1], waypoints_[i], waypoints_[i + 1]);
113 if (dist1 < MIN_SEGMENT_LENGTH || dist2 < MIN_SEGMENT_LENGTH)
114 {
115 continue;
116 }
117
118 // The maximum feasible radius for this junction
119 double theta = segment_angle(waypoints_[i - 1], waypoints_[i], waypoints_[i + 1]);
120 double local_max_radius = std::abs(std::tan(theta / 2.0)) * std::min(dist1 / 2.0, dist2 / 2.0);
121
122 // track the tightest constraint
123 // due to KDL::Path_RoundedComposite don't support changing radius
124 if (local_max_radius < max_allowed_radius)
125 max_allowed_radius = local_max_radius;
126 }
127
128 max_allowed_radius *= std::clamp(smoothness, MIN_SMOOTHNESS, MAX_SMOOTHNESS);
129
130 return max_allowed_radius;
131}
132void PathPolylineGenerator::checkConsecutiveColinearWaypoints(const KDL::Frame& p1, const KDL::Frame& p2,
133 const KDL::Frame& p3)
134{
135 KDL::Vector v1 = p2.p - p1.p;
136 KDL::Vector v2 = p3.p - p2.p;
137
138 KDL::Vector cross_product = v1 * v2;
139 if (cross_product.Norm() < MIN_COLINEAR_NORM)
140 {
142 }
143}
144
145} // namespace pilz_industrial_motion_planner
static std::vector< KDL::Frame > filterWaypoints(const KDL::Frame &start_pose, const std::vector< KDL::Frame > &waypoints)
compute the maximum rounding radius from KDL::Path_RoundedComosite
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
static double computeBlendRadius(const std::vector< KDL::Frame > &waypoints_, double smoothness)
static void checkConsecutiveColinearWaypoints(const KDL::Frame &p1, const KDL::Frame &p2, const KDL::Frame &p3)