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