@@ -47,4 +47,115 @@ bool hasCollisionsAlongPath(const Scene& scene, const Eigen::VectorXd& q_start,
4747 return false ;
4848}
4949
50+ JointPath shortcutPath (const Scene& scene, const JointPath& path, double max_step_size,
51+ unsigned int max_iters) {
52+ // Make a copy of the provided path.
53+ JointPath shortened_path = path;
54+
55+ // Cannot shorten paths if they don't have enough points.
56+ if (path.positions .size () < 3 ) {
57+ return shortened_path;
58+ }
59+
60+ std::random_device rd;
61+ std::mt19937 gen (rd ());
62+ std::uniform_real_distribution<double > dis (0.0 , 1.0 );
63+
64+ for (unsigned int i = 0 ; i < max_iters; ++i) {
65+
66+ // The the path is at maximum shortcutted-ness
67+ if (shortened_path.positions .size () < 3 ) {
68+ return shortened_path;
69+ }
70+
71+ // Randomly sample two points along the scaled path
72+ const auto path_scalings = getNormalizedPathScaling (scene, shortened_path);
73+ double low_point = dis (gen);
74+ double high_point = dis (gen);
75+ if (low_point > high_point) {
76+ std::swap (low_point, high_point);
77+ }
78+
79+ const auto [q_low, idx_low] =
80+ getConfigurationFromNormalizedPathScaling (scene, shortened_path, path_scalings, low_point);
81+ const auto [q_high, idx_high] =
82+ getConfigurationFromNormalizedPathScaling (scene, shortened_path, path_scalings, high_point);
83+
84+ if (idx_low == idx_high) {
85+ continue ;
86+ }
87+
88+ // Check if the sampled segment is collision free. If it is, shortcut the path.
89+ if (!hasCollisionsAlongPath (scene, q_low, q_high, max_step_size)) {
90+ std::vector<Eigen::VectorXd> new_positions;
91+ new_positions.reserve (idx_low + 2 + (shortened_path.positions .size () - idx_high));
92+
93+ for (size_t i = 0 ; i < idx_low; ++i) {
94+ new_positions.push_back (shortened_path.positions [i]);
95+ }
96+
97+ new_positions.push_back (q_low);
98+ new_positions.push_back (q_high);
99+
100+ for (size_t i = idx_high; i < shortened_path.positions .size (); ++i) {
101+ new_positions.push_back (shortened_path.positions [i]);
102+ }
103+
104+ shortened_path.positions = std::move (new_positions);
105+ }
106+ }
107+
108+ return shortened_path;
109+ }
110+
111+ std::vector<double > getNormalizedPathScaling (const Scene& scene, const JointPath& path) {
112+ if (path.positions .empty ()) {
113+ return {};
114+ }
115+ if (path.positions .size () == 1 ) {
116+ return {1.0 };
117+ }
118+
119+ std::vector<double > path_length_list;
120+ path_length_list.reserve (path.positions .size ());
121+
122+ // Iteratively compute path lengths from start to finish
123+ double path_length = 0.0 ;
124+ path_length_list.push_back (path_length);
125+ for (size_t idx = 0 ; idx < path.positions .size () - 1 ; ++idx) {
126+ path_length += scene.configurationDistance (path.positions [idx], path.positions [idx + 1 ]);
127+ path_length_list.push_back (path_length);
128+ }
129+
130+ // Normalize
131+ for (auto & length : path_length_list) {
132+ length /= path_length;
133+ }
134+
135+ return path_length_list;
136+ }
137+
138+ std::pair<Eigen::VectorXd, size_t >
139+ getConfigurationFromNormalizedPathScaling (const Scene& scene, const JointPath& path,
140+ const std::vector<double >& path_scalings, double value) {
141+
142+ for (size_t idx = 0 ; idx < path_scalings.size (); ++idx) {
143+ // Find the smallest index that is less than value
144+ if (value > path_scalings[idx]) {
145+ continue ;
146+ }
147+
148+ // Interpolate to the joint configuration
149+ const double delta_scale =
150+ (value - path_scalings[idx - 1 ]) / (path_scalings[idx] - path_scalings[idx - 1 ]);
151+ const Eigen::VectorXd q_interp =
152+ scene.interpolate (path.positions [idx - 1 ], path.positions [idx], delta_scale);
153+
154+ return {q_interp, idx};
155+ }
156+
157+ // This shouldn't be possible
158+ return {path.positions .back (), path.positions .size () - 1 };
159+ }
160+
50161} // namespace roboplan
0 commit comments