Apollo 11.0
自动驾驶开放平台
iterative_anchoring_smoother.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2019 The Apollo Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
17/*
18 * @file
19 */
20
22
23#include <algorithm>
24#include <limits>
25#include <random>
26
27#include "modules/planning/planning_open_space/proto/planner_open_space_config.pb.h"
28
29#include "cyber/common/log.h"
36
37namespace apollo {
38namespace planning {
39
46
48 // TODO(Jinyun, Yu): refactor after stabilized.
49 const auto& vehicle_param = common::VehicleConfigHelper::Instance()->GetConfig().vehicle_param();
50 ego_length_ = vehicle_param.length();
51 ego_width_ = vehicle_param.width();
52 center_shift_distance_ = ego_length_ / 2.0 - vehicle_param.back_edge_to_center();
53 planner_open_space_config_ = planner_open_space_config;
54 iterative_anchoring_config_ = planner_open_space_config_.iterative_anchoring_smoother_config();
55 AINFO << "config:" << planner_open_space_config_.DebugString();
56}
57
59 // TODO(Jinyun, Yu): refactor after stabilized.
60 const auto& vehicle_param = common::VehicleConfigHelper::Instance()->GetConfig().vehicle_param();
61 ego_length_ = vehicle_param.length();
62 ego_width_ = vehicle_param.width();
63 center_shift_distance_ = ego_length_ / 2.0 - vehicle_param.back_edge_to_center();
64 iterative_anchoring_config_ = iterative_anchoring_config;
65 AINFO << "config:" << iterative_anchoring_config_.DebugString();
66}
67
69 const Eigen::MatrixXd& xWS,
70 const double init_a,
71 const double init_v,
72 const std::vector<std::vector<Vec2d>>& obstacles_vertices_vec,
73 DiscretizedTrajectory* discretized_trajectory) {
74 if (xWS.cols() < 2) {
75 AERROR << "reference points size smaller than two, smoother early "
76 "returned";
77 return false;
78 }
79 const auto start_timestamp = std::chrono::system_clock::now();
80
81 // Set gear of the trajectory
82 gear_ = CheckGear(xWS);
83
84 // Set obstacle in form of linesegments
85 std::vector<std::vector<LineSegment2d>> obstacles_linesegments_vec;
86 for (const auto& obstacle_vertices : obstacles_vertices_vec) {
87 size_t vertices_num = obstacle_vertices.size();
88 std::vector<LineSegment2d> obstacle_linesegments;
89 for (size_t i = 0; i + 1 < vertices_num; ++i) {
90 LineSegment2d line_segment = LineSegment2d(obstacle_vertices[i], obstacle_vertices[i + 1]);
91 obstacle_linesegments.emplace_back(line_segment);
92 }
93 obstacles_linesegments_vec.emplace_back(obstacle_linesegments);
94 }
95 obstacles_linesegments_vec_ = std::move(obstacles_linesegments_vec);
96
97 // Interpolate the traj
98 DiscretizedPath warm_start_path;
99 size_t xWS_size = xWS.cols();
100 double accumulated_s = 0.0;
101 Vec2d last_path_point(xWS(0, 0), xWS(1, 0));
102 for (size_t i = 0; i < xWS_size; ++i) {
103 Vec2d cur_path_point(xWS(0, i), xWS(1, i));
104 accumulated_s += cur_path_point.DistanceTo(last_path_point);
105 PathPoint path_point;
106 path_point.set_x(xWS(0, i));
107 path_point.set_y(xWS(1, i));
108 path_point.set_theta(xWS(2, i));
109 path_point.set_s(accumulated_s);
110 warm_start_path.push_back(std::move(path_point));
111 last_path_point = cur_path_point;
112 }
113 if (std::fabs(accumulated_s) < 0.1) {
114 AERROR << "warm_start_path smaller than 0.1 m, can't enforce "
115 "heading continuity";
116 return false;
117 }
118
119 const double interpolated_delta_s = iterative_anchoring_config_.interpolated_delta_s();
120 std::vector<std::pair<double, double>> interpolated_warm_start_point2ds;
121 double path_length = warm_start_path.Length();
122 int count = std::ceil(path_length / interpolated_delta_s);
123 double delta_s = path_length / (count < 4 ? 4 : count);
124 path_length += delta_s * 1.0e-6;
125 for (double s = 0; s < path_length; s += delta_s) {
126 const auto point2d = warm_start_path.Evaluate(s);
127 interpolated_warm_start_point2ds.emplace_back(point2d.x(), point2d.y());
128 }
129
130 const size_t interpolated_size = interpolated_warm_start_point2ds.size();
131 if (interpolated_size < 6) {
132 ADEBUG << "interpolated_warm_start_path smaller than 4, can't enforce "
133 "initial zero kappa";
134 enforce_initial_kappa_ = false;
135 } else {
136 enforce_initial_kappa_ = true;
137 }
138
139 // Adjust heading to ensure heading continuity
140 AdjustStartEndHeading(xWS, &interpolated_warm_start_point2ds);
141
142 // Reset path profile by discrete point heading and curvature estimation
143 DiscretizedPath interpolated_warm_start_path;
144 if (!SetPathProfile(interpolated_warm_start_point2ds, &interpolated_warm_start_path)) {
145 AERROR << "Set path profile fails";
146 return false;
147 }
148
149 // Generate feasible bounds for each path point
150 std::vector<double> bounds;
151 if (!GenerateInitialBounds(interpolated_warm_start_path, &bounds)) {
152 AERROR << "Generate initial bounds failed, path point to close to obstacle";
153 return false;
154 }
155
156 // Check initial path collision avoidance, if it fails, smoother assumption
157 // fails. Try reanchoring
158 input_colliding_point_index_.clear();
159 if (!CheckCollisionAvoidance(interpolated_warm_start_path, &input_colliding_point_index_)) {
160 AERROR << "Interpolated input path points colliding with obstacle";
161 // if (!ReAnchoring(colliding_point_index, &interpolated_warm_start_path)) {
162 // AERROR << "Fail to reanchor colliding input path points";
163 // return false;
164 // }
165 }
166
167 const auto path_smooth_start_timestamp = std::chrono::system_clock::now();
168
169 // Smooth path to have smoothed x, y, phi, kappa and s
170 DiscretizedPath smoothed_path_points;
171 if (!SmoothPath(interpolated_warm_start_path, bounds, &smoothed_path_points)) {
172 return false;
173 }
174 // PrintCurves print_curve;
175 // for (size_t i = 0; i < smoothed_path_points.size(); i++) {
176 // print_curve.AddPoint("iter_smoothed_xy", smoothed_path_points[i].x(),
177 // smoothed_path_points[i].y());
178 // print_curve.AddPoint("iter_smoothed_s-kappa",
179 // smoothed_path_points[i].s(),
180 // smoothed_path_points[i].kappa());
181 // print_curve.AddPoint("iter_smoothed_s-theta",
182 // smoothed_path_points[i].s(),
183 // smoothed_path_points[i].theta());
184 // }
185 // for (size_t i = 0; i < interpolated_warm_start_path.size(); i++) {
186 // print_curve.AddPoint("iter_warm_xy", interpolated_warm_start_path[i].x(),
187 // interpolated_warm_start_path[i].y());
188 // print_curve.AddPoint("iter_warm_s-kappa",
189 // interpolated_warm_start_path[i].s(),
190 // interpolated_warm_start_path[i].kappa());
191 // print_curve.AddPoint("iter_warm_s-theta",
192 // interpolated_warm_start_path[i].s(),
193 // interpolated_warm_start_path[i].theta());
194 // }
195 // print_curve.PrintToLog();
196 const auto path_smooth_end_timestamp = std::chrono::system_clock::now();
197 std::chrono::duration<double> path_smooth_diff = path_smooth_end_timestamp - path_smooth_start_timestamp;
198 ADEBUG << "iterative anchoring path smoother time: " << path_smooth_diff.count() * 1000.0 << " ms.";
199
200 const auto speed_smooth_start_timestamp = std::chrono::system_clock::now();
201
202 // Smooth speed to have smoothed v and a
203 SpeedData smoothed_speeds;
204 if (!SmoothSpeed(init_a, init_v, smoothed_path_points.Length(), &smoothed_speeds)) {
205 return false;
206 }
207
208 // TODO(Jinyun): Evaluate performance
209 // SpeedData smoothed_speeds;
210 // if (!GenerateStopProfileFromPolynomial(
211 // init_a, init_v, smoothed_path_points.Length(), &smoothed_speeds)) {
212 // return false;
213 // }
214
215 const auto speed_smooth_end_timestamp = std::chrono::system_clock::now();
216 std::chrono::duration<double> speed_smooth_diff = speed_smooth_end_timestamp - speed_smooth_start_timestamp;
217 ADEBUG << "iterative anchoring speed smoother time: " << speed_smooth_diff.count() * 1000.0 << " ms.";
218
219 // Combine path and speed
220 if (!CombinePathAndSpeed(smoothed_path_points, smoothed_speeds, discretized_trajectory)) {
221 return false;
222 }
223
224 AdjustPathAndSpeedByGear(discretized_trajectory);
225
226 const auto end_timestamp = std::chrono::system_clock::now();
227 std::chrono::duration<double> diff = end_timestamp - start_timestamp;
228 ADEBUG << "iterative anchoring smoother total time: " << diff.count() * 1000.0 << " ms.";
229
230 ADEBUG << "discretized_trajectory size " << discretized_trajectory->size();
231 return true;
232}
233
234void IterativeAnchoringSmoother::AdjustStartEndHeading(
235 const Eigen::MatrixXd& xWS,
236 std::vector<std::pair<double, double>>* const point2d) {
237 // Sanity check
238 CHECK_NOTNULL(point2d);
239 CHECK_GT(xWS.cols(), 1);
240 CHECK_GT(point2d->size(), 3U);
241
242 // Set initial heading and bounds
243 const double initial_heading = xWS(2, 0);
244 const double end_heading = xWS(2, xWS.cols() - 1);
245
246 // Adjust the point position to have heading by finite element difference of
247 // the point and the other point equal to the given warm start initial or end
248 // heading
249 const double first_to_second_dx = point2d->at(1).first - point2d->at(0).first;
250 const double first_to_second_dy = point2d->at(1).second - point2d->at(0).second;
251 const double first_to_second_s
252 = std::sqrt(first_to_second_dx * first_to_second_dx + first_to_second_dy * first_to_second_dy);
253 Vec2d first_point(point2d->at(0).first, point2d->at(0).second);
254 Vec2d initial_vec(first_to_second_s, 0);
255 initial_vec.SelfRotate(gear_ ? initial_heading : NormalizeAngle(initial_heading + M_PI));
256 initial_vec += first_point;
257 point2d->at(1) = std::make_pair(initial_vec.x(), initial_vec.y());
258
259 const size_t path_size = point2d->size();
260 const double second_last_to_last_dx = point2d->at(path_size - 1).first - point2d->at(path_size - 2).first;
261 const double second_last_to_last_dy = point2d->at(path_size - 1).second - point2d->at(path_size - 2).second;
262 const double second_last_to_last_s = std::sqrt(
263 second_last_to_last_dx * second_last_to_last_dx + second_last_to_last_dy * second_last_to_last_dy);
264 Vec2d last_point(point2d->at(path_size - 1).first, point2d->at(path_size - 1).second);
265 Vec2d end_vec(second_last_to_last_s, 0);
266 end_vec.SelfRotate(gear_ ? NormalizeAngle(end_heading + M_PI) : end_heading);
267 end_vec += last_point;
268 point2d->at(path_size - 2) = std::make_pair(end_vec.x(), end_vec.y());
269}
270
271bool IterativeAnchoringSmoother::ReAnchoring(
272 const std::vector<size_t>& colliding_point_index,
273 DiscretizedPath* path_points) {
274 CHECK_NOTNULL(path_points);
275 if (colliding_point_index.empty()) {
276 ADEBUG << "no point needs to be re-anchored";
277 return true;
278 }
279
280 CHECK_GT(path_points->size(), *(std::max_element(colliding_point_index.begin(), colliding_point_index.end())));
281
282 for (const auto index : colliding_point_index) {
283 if (index == 0 || index == path_points->size() - 1) {
284 AERROR << "Initial and end points collision avoid condition failed.";
285 return false;
286 }
287 if (index == 1 || index == path_points->size() - 2) {
288 AERROR << "second to last point or second point pos reanchored. Heading "
289 "discontinuity might "
290 "happen";
291 }
292 }
293
294 // TODO(Jinyun): move to confs
295 const size_t reanchoring_trails_num = static_cast<size_t>(iterative_anchoring_config_.reanchoring_trails_num());
296 const double reanchoring_pos_stddev = iterative_anchoring_config_.reanchoring_pos_stddev();
297 const double reanchoring_length_stddev = iterative_anchoring_config_.reanchoring_length_stddev();
298 std::random_device rd;
299 std::default_random_engine gen = std::default_random_engine(rd());
300 std::normal_distribution<> pos_dis{0, reanchoring_pos_stddev};
301 std::normal_distribution<> length_dis{0, reanchoring_length_stddev};
302
303 for (const auto index : colliding_point_index) {
304 bool reanchoring_success = false;
305 for (size_t i = 0; i < reanchoring_trails_num; ++i) {
306 // Get ego box for collision check on collision point index
307 bool is_colliding = false;
308 for (size_t j = index - 1; j < index + 2; ++j) {
309 const double heading
310 = gear_ ? path_points->at(j).theta() : NormalizeAngle(path_points->at(j).theta() + M_PI);
311 Box2d ego_box(
312 {path_points->at(j).x() + center_shift_distance_ * std::cos(heading),
313 path_points->at(j).y() + center_shift_distance_ * std::sin(heading)},
314 heading,
315 ego_length_,
316 ego_width_);
317 for (const auto& obstacle_linesegments : obstacles_linesegments_vec_) {
318 for (const LineSegment2d& linesegment : obstacle_linesegments) {
319 if (ego_box.HasOverlap(linesegment)) {
320 is_colliding = true;
321 break;
322 }
323 }
324 if (is_colliding) {
325 break;
326 }
327 }
328 if (is_colliding) {
329 break;
330 }
331 }
332
333 if (is_colliding) {
334 // Adjust the point by randomly move around the original points
335 if (index == 1) {
336 const double adjust_theta = path_points->at(index - 1).theta();
337 const double delta_s = std::abs(path_points->at(index).s() - path_points->at(index - 1).s());
338 double rand_dev = common::math::Clamp(length_dis(gen), 0.8, -0.8);
339 double adjusted_delta_s = delta_s * (1.0 + rand_dev);
340 path_points->at(index).set_x(
341 path_points->at(index - 1).x() + adjusted_delta_s * std::cos(adjust_theta));
342 path_points->at(index).set_y(
343 path_points->at(index - 1).y() + adjusted_delta_s * std::sin(adjust_theta));
344 } else if (index == path_points->size() - 2) {
345 const double adjust_theta = NormalizeAngle(path_points->at(index + 1).theta() + M_PI);
346 const double delta_s = std::abs(path_points->at(index + 1).s() - path_points->at(index).s());
347 double rand_dev = common::math::Clamp(length_dis(gen), 0.8, -0.8);
348 double adjusted_delta_s = delta_s * (1.0 + rand_dev);
349 path_points->at(index).set_x(
350 path_points->at(index + 1).x() + adjusted_delta_s * std::cos(adjust_theta));
351 path_points->at(index).set_y(
352 path_points->at(index + 1).y() + adjusted_delta_s * std::sin(adjust_theta));
353 } else {
354 double rand_dev_x = common::math::Clamp(
355 pos_dis(gen), 2.0 * reanchoring_pos_stddev, -2.0 * reanchoring_pos_stddev);
356 double rand_dev_y = common::math::Clamp(
357 pos_dis(gen), 2.0 * reanchoring_pos_stddev, -2.0 * reanchoring_pos_stddev);
358 path_points->at(index).set_x(path_points->at(index).x() + rand_dev_x);
359 path_points->at(index).set_y(path_points->at(index).y() + rand_dev_y);
360 }
361
362 // Adjust heading accordingly
363 // TODO(Jinyun): refactor into math module
364 // current point heading adjustment
365 for (size_t i = index - 1; i < index + 2; ++i) {
366 path_points->at(i).set_theta(CalcHeadings(*path_points, i));
367 }
368 } else {
369 reanchoring_success = true;
370 break;
371 }
372 }
373
374 if (!reanchoring_success) {
375 AERROR << "interpolated points at index " << index << "can't be successfully reanchored";
376 return false;
377 }
378 }
379 return true;
380}
381
382bool IterativeAnchoringSmoother::GenerateInitialBounds(
383 const DiscretizedPath& path_points,
384 std::vector<double>* initial_bounds) {
385 CHECK_NOTNULL(initial_bounds);
386 initial_bounds->clear();
387
388 const bool estimate_bound = iterative_anchoring_config_.estimate_bound();
389 const double default_bound = iterative_anchoring_config_.default_bound();
390 const double vehicle_shortest_dimension = iterative_anchoring_config_.vehicle_shortest_dimension();
391 const double kEpislon = 1e-8;
392
393 if (!estimate_bound) {
394 std::vector<double> default_bounds(path_points.size(), default_bound);
395 *initial_bounds = std::move(default_bounds);
396 return true;
397 }
398
399 // TODO(Jinyun): refine obstacle formulation and speed it up
400 for (const auto& path_point : path_points) {
401 double min_bound = std::numeric_limits<double>::infinity();
402 for (const auto& obstacle_linesegments : obstacles_linesegments_vec_) {
403 for (const LineSegment2d& linesegment : obstacle_linesegments) {
404 min_bound = std::min(min_bound, linesegment.DistanceTo({path_point.x(), path_point.y()}));
405 }
406 }
407 min_bound -= vehicle_shortest_dimension;
408 min_bound = min_bound < kEpislon ? 0.0 : min_bound;
409 initial_bounds->push_back(min_bound);
410 }
411 return true;
412}
413
414bool IterativeAnchoringSmoother::SmoothPath(
415 const DiscretizedPath& raw_path_points,
416 const std::vector<double>& bounds,
417 DiscretizedPath* smoothed_path_points,
418 std::vector<std::vector<common::math::Vec2d>> point_box) {
419 std::vector<std::pair<double, double>> raw_point2d;
420 std::vector<double> flexible_bounds;
421 for (const auto& path_point : raw_path_points) {
422 raw_point2d.emplace_back(path_point.x(), path_point.y());
423 }
424 flexible_bounds = bounds;
425
426 FemPosDeviationSmoother fem_pos_smoother(iterative_anchoring_config_.fem_pos_deviation_smoother_config());
427
428 // TODO(Jinyun): move to confs
429 const size_t max_iteration_num = 50;
430
431 bool is_collision_free = false;
432 std::vector<size_t> colliding_point_index;
433 std::vector<std::pair<double, double>> smoothed_point2d;
434 size_t counter = 0;
435
436 while (!is_collision_free) {
437 if (counter > max_iteration_num) {
438 AERROR << "Maximum iteration reached, path smoother early stops";
439 return true;
440 }
441
442 AdjustPathBounds(colliding_point_index, &flexible_bounds);
443
444 std::vector<double> opt_x;
445 std::vector<double> opt_y;
446 if (!fem_pos_smoother.Solve(raw_point2d, flexible_bounds, &opt_x, &opt_y)) {
447 AERROR << "Smoothing path fails";
448 return false;
449 }
450
451 if (opt_x.size() < 2 || opt_y.size() < 2) {
452 AERROR << "Return by fem_pos_smoother is wrong. Size smaller than 2 ";
453 return false;
454 }
455
456 CHECK_EQ(opt_x.size(), opt_y.size());
457
458 size_t point_size = opt_x.size();
459 smoothed_point2d.clear();
460 for (size_t i = 0; i < point_size; ++i) {
461 smoothed_point2d.emplace_back(opt_x[i], opt_y[i]);
462 }
463
464 if (!SetPathProfile(smoothed_point2d, smoothed_path_points)) {
465 AERROR << "Set path profile fails";
466 return false;
467 }
468
469 is_collision_free = CheckCollisionAvoidance(*smoothed_path_points, &colliding_point_index);
470
471 ADEBUG << "loop iteration number is " << counter;
472 ++counter;
473 }
474 return true;
475}
476
477bool IterativeAnchoringSmoother::CheckCollisionAvoidance(
478 const DiscretizedPath& path_points,
479 std::vector<size_t>* colliding_point_index) {
480 CHECK_NOTNULL(colliding_point_index);
481
482 colliding_point_index->clear();
483 size_t path_points_size = path_points.size();
484 for (size_t i = 0; i < path_points_size; ++i) {
485 // Skip checking collision for thoese points colliding originally
486 bool skip_checking = false;
487 for (const auto index : input_colliding_point_index_) {
488 if (i == index) {
489 skip_checking = true;
490 break;
491 }
492 }
493 if (skip_checking) {
494 continue;
495 }
496
497 const double heading = gear_ ? path_points[i].theta() : NormalizeAngle(path_points[i].theta() + M_PI);
498 Box2d ego_box(
499 {path_points[i].x() + center_shift_distance_ * std::cos(heading),
500 path_points[i].y() + center_shift_distance_ * std::sin(heading)},
501 heading,
502 ego_length_,
503 ego_width_);
504
505 bool is_colliding = false;
506 for (const auto& obstacle_linesegments : obstacles_linesegments_vec_) {
507 for (const LineSegment2d& linesegment : obstacle_linesegments) {
508 if (ego_box.HasOverlap(linesegment)) {
509 colliding_point_index->push_back(i);
510 ADEBUG << "point at " << i << "collied with LineSegment " << linesegment.DebugString();
511 is_colliding = true;
512 break;
513 }
514 }
515 if (is_colliding) {
516 break;
517 }
518 }
519 }
520
521 if (!colliding_point_index->empty()) {
522 return false;
523 }
524 return true;
525}
526
527void IterativeAnchoringSmoother::AdjustPathBounds(
528 const std::vector<size_t>& colliding_point_index,
529 std::vector<double>* bounds) {
530 CHECK_NOTNULL(bounds);
531
532 const double collision_decrease_ratio = iterative_anchoring_config_.collision_decrease_ratio();
533
534 for (const auto index : colliding_point_index) {
535 bounds->at(index) *= collision_decrease_ratio;
536 }
537
538 // Anchor the end points to enforce the initial end end heading continuity and
539 // zero kappa
540 bounds->at(0) = 0.0;
541 bounds->at(1) = 0.0;
542 bounds->at(bounds->size() - 1) = 0.0;
543 bounds->at(bounds->size() - 2) = 0.0;
544 if (enforce_initial_kappa_) {
545 bounds->at(2) = 0.0;
546 }
547}
548
549bool IterativeAnchoringSmoother::SetPathProfile(
550 const std::vector<std::pair<double, double>>& point2d,
551 DiscretizedPath* raw_path_points) {
552 CHECK_NOTNULL(raw_path_points);
553 raw_path_points->clear();
554 // Compute path profile
555 std::vector<double> headings;
556 std::vector<double> kappas;
557 std::vector<double> dkappas;
558 std::vector<double> accumulated_s;
559 if (!DiscretePointsMath::ComputePathProfile(point2d, &headings, &accumulated_s, &kappas, &dkappas)) {
560 return false;
561 }
562 CHECK_EQ(point2d.size(), headings.size());
563 CHECK_EQ(point2d.size(), kappas.size());
564 CHECK_EQ(point2d.size(), dkappas.size());
565 CHECK_EQ(point2d.size(), accumulated_s.size());
566
567 // Load into path point
568 size_t points_size = point2d.size();
569 for (size_t i = 0; i < points_size; ++i) {
570 PathPoint path_point;
571 path_point.set_x(point2d[i].first);
572 path_point.set_y(point2d[i].second);
573 path_point.set_theta(headings[i]);
574 path_point.set_s(accumulated_s[i]);
575 path_point.set_kappa(kappas[i]);
576 path_point.set_dkappa(dkappas[i]);
577 raw_path_points->push_back(std::move(path_point));
578 }
579 return true;
580}
581
582bool IterativeAnchoringSmoother::CheckGear(const Eigen::MatrixXd& xWS) {
583 CHECK_GT(xWS.size(), 1);
584 double init_heading_angle = xWS(2, 0);
585 const Vec2d init_tracking_vector(xWS(0, 1) - xWS(0, 0), xWS(1, 1) - xWS(1, 0));
586 double init_tracking_angle = init_tracking_vector.Angle();
587 return std::abs(NormalizeAngle(init_tracking_angle - init_heading_angle)) < M_PI_2;
588}
589
590bool IterativeAnchoringSmoother::SmoothSpeed(
591 const double init_a,
592 const double init_v,
593 const double path_length,
594 SpeedData* smoothed_speeds) {
595 const double max_forward_v = FLAGS_open_space_max_forward_v;
596 const double max_reverse_v = FLAGS_open_space_max_reverse_v;
597 const double max_forward_acc = FLAGS_open_space_max_forward_acc;
598 const double max_reverse_acc = FLAGS_open_space_max_reverse_acc;
599 const double max_acc_jerk = FLAGS_open_space_max_jerk;
600 const double delta_t = FLAGS_open_space_delta_t;
601
602 const double total_t = 2 * path_length / max_reverse_acc * 10;
603 ADEBUG << "total_t is : " << total_t << "path_length" << path_length;
604 const size_t num_of_knots = static_cast<size_t>(total_t / delta_t) + 1;
605
606 PiecewiseJerkSpeedProblem piecewise_jerk_problem(num_of_knots, delta_t, {0.0, std::abs(init_v), 0.0});
607
608 // set end constraints
609 std::vector<std::pair<double, double>> x_bounds(num_of_knots, {0.0, path_length});
610
611 const double max_v = gear_ ? max_forward_v : max_reverse_v;
612 const double max_acc = gear_ ? max_forward_acc : max_reverse_acc;
613
614 const auto upper_dx = std::fmax(max_v, std::abs(init_v));
615 std::vector<std::pair<double, double>> dx_bounds(num_of_knots, {0.0, upper_dx});
616 std::vector<std::pair<double, double>> ddx_bounds(num_of_knots, {-max_acc, max_acc});
617
618 x_bounds[num_of_knots - 1] = std::make_pair(path_length, path_length);
619 dx_bounds[num_of_knots - 1] = std::make_pair(0.0, 0.0);
620 ddx_bounds[num_of_knots - 1] = std::make_pair(0.0, 0.0);
621
622 std::vector<double> x_ref(num_of_knots, path_length);
623 piecewise_jerk_problem.set_x_ref(FLAGS_open_space_reference_s_weight, std::move(x_ref));
624 piecewise_jerk_problem.set_weight_ddx(FLAGS_open_space_acc_weight);
625 piecewise_jerk_problem.set_weight_dddx(FLAGS_open_space_jerk_weight);
626 piecewise_jerk_problem.set_x_bounds(x_bounds);
627 piecewise_jerk_problem.set_dx_bounds(dx_bounds);
628 piecewise_jerk_problem.set_ddx_bounds(ddx_bounds);
629 piecewise_jerk_problem.set_dddx_bound(max_acc_jerk);
630
631 // Solve the problem
632 if (!piecewise_jerk_problem.Optimize()) {
633 AERROR << "Piecewise jerk speed optimizer failed!";
634 AERROR << "init v" << init_v << ",a" << init_a << "jerk bound" << max_acc_jerk;
635 AERROR << "xbounds; dxbounds;ddxbounds";
636 for (size_t i = 0; i < num_of_knots; i++) {
637 AERROR << x_bounds[i].first << ", " << x_bounds[i].second << ";" << dx_bounds[i].first << ","
638 << dx_bounds[i].second << ";" << ddx_bounds[i].first << "," << ddx_bounds[i].second;
639 }
640 return false;
641 }
642
643 // Extract output
644 const std::vector<double>& s = piecewise_jerk_problem.opt_x();
645 const std::vector<double>& ds = piecewise_jerk_problem.opt_dx();
646 const std::vector<double>& dds = piecewise_jerk_problem.opt_ddx();
647
648 // Assign speed point by gear
649 smoothed_speeds->AppendSpeedPoint(s[0], 0.0, ds[0], dds[0], 0.0);
650 const double kEpislon = 1.0e-1;
651 const double sEpislon = 1.0e-1;
652 for (size_t i = 1; i < num_of_knots; ++i) {
653 if (s[i - 1] - s[i] > kEpislon) {
654 AERROR << "unexpected decreasing s in speed smoothing at time " << static_cast<double>(i) * delta_t
655 << "with total time " << total_t;
656 return false;
657 }
658 smoothed_speeds->AppendSpeedPoint(
659 s[i], delta_t * static_cast<double>(i), ds[i], dds[i], (dds[i] - dds[i - 1]) / delta_t);
660 // Cut the speed data when it is about to meet end condition
661 if (path_length - s[i] < sEpislon) {
662 break;
663 }
664 }
665 return true;
666}
667
668bool IterativeAnchoringSmoother::CombinePathAndSpeed(
669 const DiscretizedPath& path_points,
670 const SpeedData& speed_points,
671 DiscretizedTrajectory* discretized_trajectory) {
672 CHECK_NOTNULL(discretized_trajectory);
673 discretized_trajectory->clear();
674 // TODO(Jinyun): move to confs
675 const double kDenseTimeResolution = 0.1;
676 const double time_horizon = speed_points.TotalTime() + kDenseTimeResolution * 1.0e-6;
677 if (path_points.empty()) {
678 AERROR << "path data is empty";
679 return false;
680 }
681 ADEBUG << "speed_points.TotalTime() " << speed_points.TotalTime();
682 for (double cur_rel_time = 0.0; cur_rel_time < time_horizon; cur_rel_time += kDenseTimeResolution) {
683 common::SpeedPoint speed_point;
684 if (!speed_points.EvaluateByTime(cur_rel_time, &speed_point)) {
685 AERROR << "Fail to get speed point with relative time " << cur_rel_time;
686 return false;
687 }
688
689 if (speed_point.s() > path_points.Length()) {
690 break;
691 }
692
693 common::PathPoint path_point = path_points.Evaluate(speed_point.s());
694
695 common::TrajectoryPoint trajectory_point;
696 trajectory_point.mutable_path_point()->CopyFrom(path_point);
697 trajectory_point.set_v(speed_point.v());
698 trajectory_point.set_a(speed_point.a());
699 trajectory_point.set_relative_time(speed_point.t());
700 discretized_trajectory->AppendTrajectoryPoint(trajectory_point);
701 }
702
703 common::PathPoint path_point = path_points.Evaluate(path_points.Length());
704 common::TrajectoryPoint trajectory_point;
705 trajectory_point.mutable_path_point()->CopyFrom(path_point);
706 trajectory_point.set_v(0.0);
707 trajectory_point.set_a(0.0);
708 trajectory_point.set_relative_time(speed_points.TotalTime() + kDenseTimeResolution);
709 discretized_trajectory->AppendTrajectoryPoint(trajectory_point);
710
711 ADEBUG << "path length before combine " << path_points.Length();
712 ADEBUG << "trajectory length after combine " << discretized_trajectory->GetSpatialLength();
713 return true;
714}
715
716void IterativeAnchoringSmoother::AdjustPathAndSpeedByGear(DiscretizedTrajectory* discretized_trajectory) {
717 if (gear_) {
718 return;
719 }
720 std::for_each(
721 discretized_trajectory->begin(), discretized_trajectory->end(), [](TrajectoryPoint& trajectory_point) {
722 trajectory_point.mutable_path_point()->set_theta(
723 NormalizeAngle(trajectory_point.path_point().theta() + M_PI));
724 trajectory_point.mutable_path_point()->set_s(-1.0 * trajectory_point.path_point().s());
725 trajectory_point.mutable_path_point()->set_kappa(-1.0 * trajectory_point.path_point().kappa());
726 // dkappa stays the same as direction of both kappa and s are reversed
727 trajectory_point.set_v(-1.0 * trajectory_point.v());
728 trajectory_point.set_a(-1.0 * trajectory_point.a());
729 });
730}
731
732bool IterativeAnchoringSmoother::GenerateStopProfileFromPolynomial(
733 const double init_acc,
734 const double init_speed,
735 const double stop_distance,
736 SpeedData* smoothed_speeds) {
737 static constexpr double kMaxT = 8.0;
738 static constexpr double kUnitT = 0.2;
739 for (double t = 2.0; t <= kMaxT; t += kUnitT) {
740 QuinticPolynomialCurve1d curve(0.0, init_speed, init_acc, stop_distance, 0.0, 0.0, t);
741 if (!IsValidPolynomialProfile(curve)) {
742 continue;
743 }
744 for (double curve_t = 0.0; curve_t <= t; curve_t += kUnitT) {
745 const double curve_s = curve.Evaluate(0, curve_t);
746 const double curve_v = curve.Evaluate(1, curve_t);
747 const double curve_a = curve.Evaluate(2, curve_t);
748 const double curve_da = curve.Evaluate(3, curve_t);
749 smoothed_speeds->AppendSpeedPoint(curve_s, curve_t, curve_v, curve_a, curve_da);
750 }
751 return true;
752 }
753 AERROR << "GenerateStopProfileFromPolynomial fails.";
754 return false;
755}
756
757bool IterativeAnchoringSmoother::IsValidPolynomialProfile(const QuinticPolynomialCurve1d& curve) {
758 for (double evaluate_t = 0.1; evaluate_t <= curve.ParamLength(); evaluate_t += 0.2) {
759 const double v = curve.Evaluate(1, evaluate_t);
760 const double a = curve.Evaluate(2, evaluate_t);
761 static constexpr double kEpsilon = 1e-3;
762 if (v < -kEpsilon || a > 1.0) {
763 return false;
764 }
765 }
766 return true;
767}
768
769double IterativeAnchoringSmoother::CalcHeadings(const DiscretizedPath& path_points, const size_t index) {
770 CHECK_GT(path_points.size(), 2U);
771 double dx = 0.0;
772 double dy = 0.0;
773 if (index == 0) {
774 dx = path_points[index + 1].x() - path_points[index].x();
775 dy = path_points[index + 1].y() - path_points[index].y();
776 } else if (index == path_points.size() - 1) {
777 dx = path_points[index].x() - path_points[index - 1].x();
778 dy = path_points[index].y() - path_points[index - 1].y();
779 } else {
780 dx = 0.5 * (path_points[index + 1].x() - path_points[index - 1].x());
781 dy = 0.5 * (path_points[index + 1].y() - path_points[index - 1].y());
782 }
783 return std::atan2(dy, dx);
784}
785} // namespace planning
786} // namespace apollo
Rectangular (undirected) bounding box in 2-D.
Definition box2d.h:52
Implements a class of 2-dimensional vectors.
Definition vec2d.h:42
double DistanceTo(const Vec2d &other) const
Returns the distance to the given vector
Definition vec2d.cc:47
static bool ComputePathProfile(const std::vector< std::pair< double, double > > &xy_points, std::vector< double > *headings, std::vector< double > *accumulated_s, std::vector< double > *kappas, std::vector< double > *dkappas)
common::PathPoint Evaluate(const double path_s) const
bool Smooth(const Eigen::MatrixXd &xWS, const double init_a, const double init_v, const std::vector< std::vector< common::math::Vec2d > > &obstacles_vertices_vec, DiscretizedTrajectory *discretized_trajectory)
IterativeAnchoringSmoother(const PlannerOpenSpaceConfig &planner_open_space_config)
Planning module main class.
Math-related util functions.
#define ADEBUG
Definition log.h:41
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
T Clamp(const T value, T bound1, T bound2)
Clamp a value between two bounds.
Definition math_utils.h:155
double NormalizeAngle(const double angle)
Normalize angle to [-PI, PI).
Definition math_utils.cc:53
class register implement
Definition arena_queue.h:37
optional FemPosDeviationSmootherConfig fem_pos_deviation_smoother_config
optional IterativeAnchoringConfig iterative_anchoring_smoother_config