Apollo 10.0
自动驾驶开放平台
hybrid_a_star.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2018 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 <limits>
24#include <unordered_set>
25
30
31namespace apollo {
32namespace planning {
33
37
39 planner_open_space_config_.CopyFrom(open_space_conf);
40 reed_shepp_generator_ =
41 std::make_unique<ReedShepp>(vehicle_param_, planner_open_space_config_);
42 grid_a_star_heuristic_generator_ =
43 std::make_unique<GridSearch>(planner_open_space_config_);
44 next_node_num_ =
45 planner_open_space_config_.warm_start_config().next_node_num();
46 max_steer_angle_ = vehicle_param_.max_steer_angle() /
47 vehicle_param_.steer_ratio() *
48 planner_open_space_config_.warm_start_config()
50 step_size_ = planner_open_space_config_.warm_start_config().step_size();
51 xy_grid_resolution_ =
52 planner_open_space_config_.warm_start_config().xy_grid_resolution();
53 arc_length_ =
54 planner_open_space_config_.warm_start_config().phi_grid_resolution() *
55 vehicle_param_.wheel_base() /
56 std::tan(max_steer_angle_ * 2 / (next_node_num_ / 2 - 1));
57 if (arc_length_ < std::sqrt(2) * xy_grid_resolution_) {
58 arc_length_ = std::sqrt(2) * xy_grid_resolution_;
59 }
60 AINFO << "arc_length" << arc_length_;
61 delta_t_ = planner_open_space_config_.delta_t();
62 traj_forward_penalty_ =
63 planner_open_space_config_.warm_start_config().traj_forward_penalty();
64 traj_back_penalty_ =
65 planner_open_space_config_.warm_start_config().traj_back_penalty();
66 traj_gear_switch_penalty_ =
67 planner_open_space_config_.warm_start_config().traj_gear_switch_penalty();
68 traj_steer_penalty_ =
69 planner_open_space_config_.warm_start_config().traj_steer_penalty();
70 traj_steer_change_penalty_ = planner_open_space_config_.warm_start_config()
72 acc_weight_ = planner_open_space_config_.iterative_anchoring_smoother_config()
74 .acc_weight();
75 jerk_weight_ =
76 planner_open_space_config_.iterative_anchoring_smoother_config()
78 .jerk_weight();
79 kappa_penalty_weight_ =
80 planner_open_space_config_.iterative_anchoring_smoother_config()
83 ref_s_weight_ =
84 planner_open_space_config_.iterative_anchoring_smoother_config()
86 .ref_s_weight();
87 ref_v_weight_ =
88 planner_open_space_config_.iterative_anchoring_smoother_config()
90 .ref_v_weight();
91 max_forward_v_ =
92 planner_open_space_config_.iterative_anchoring_smoother_config()
94 max_reverse_v_ =
95 planner_open_space_config_.iterative_anchoring_smoother_config()
97 max_forward_acc_ =
98 planner_open_space_config_.iterative_anchoring_smoother_config()
100 max_reverse_acc_ =
101 planner_open_space_config_.iterative_anchoring_smoother_config()
103 max_acc_jerk_ =
104 planner_open_space_config_.iterative_anchoring_smoother_config()
105 .max_acc_jerk();
106}
107
108bool HybridAStar::AnalyticExpansion(
109 std::shared_ptr<Node3d> current_node,
110 std::shared_ptr<Node3d>* candidate_final_node) {
111 std::shared_ptr<ReedSheppPath> reeds_shepp_to_check =
112 std::make_shared<ReedSheppPath>();
113 if (!reed_shepp_generator_->ShortestRSP(current_node, end_node_,
114 reeds_shepp_to_check)) {
115 return false;
116 }
117 if (!RSPCheck(reeds_shepp_to_check)) {
118 return false;
119 }
120 // load the whole RSP as nodes and add to the close set
121 *candidate_final_node = LoadRSPinCS(reeds_shepp_to_check, current_node);
122 return true;
123}
124
125bool HybridAStar::RSPCheck(
126 const std::shared_ptr<ReedSheppPath> reeds_shepp_to_end) {
127 std::shared_ptr<Node3d> node = std::shared_ptr<Node3d>(new Node3d(
128 reeds_shepp_to_end->x, reeds_shepp_to_end->y, reeds_shepp_to_end->phi,
129 XYbounds_, planner_open_space_config_));
130 return ValidityCheck(node);
131}
132
133bool HybridAStar::ValidityCheck(std::shared_ptr<Node3d> node) {
134 CHECK_NOTNULL(node);
135 CHECK_GT(node->GetStepSize(), 0U);
136
137 if (obstacles_linesegments_vec_.empty()) {
138 return true;
139 }
140
141 size_t node_step_size = node->GetStepSize();
142 const auto& traversed_x = node->GetXs();
143 const auto& traversed_y = node->GetYs();
144 const auto& traversed_phi = node->GetPhis();
145
146 // The first {x, y, phi} is collision free unless they are start and end
147 // configuration of search problem
148 size_t check_start_index = 0;
149 if (node_step_size == 1) {
150 check_start_index = 0;
151 } else {
152 check_start_index = 1;
153 }
154
155 for (size_t i = check_start_index; i < node_step_size; ++i) {
156 if (traversed_x[i] > XYbounds_[1] || traversed_x[i] < XYbounds_[0] ||
157 traversed_y[i] > XYbounds_[3] || traversed_y[i] < XYbounds_[2]) {
158 return false;
159 }
160 Box2d bounding_box = Node3d::GetBoundingBox(
161 vehicle_param_, traversed_x[i], traversed_y[i], traversed_phi[i]);
162 for (const auto& obstacle_linesegments : obstacles_linesegments_vec_) {
163 for (const common::math::LineSegment2d& linesegment :
164 obstacle_linesegments) {
165 if (bounding_box.HasOverlap(linesegment)) {
166 ADEBUG << "collision start at x: " << linesegment.start().x();
167 ADEBUG << "collision start at y: " << linesegment.start().y();
168 ADEBUG << "collision end at x: " << linesegment.end().x();
169 ADEBUG << "collision end at y: " << linesegment.end().y();
170 return false;
171 }
172 }
173 }
174 }
175 return true;
176}
177
178std::shared_ptr<Node3d> HybridAStar::LoadRSPinCS(
179 const std::shared_ptr<ReedSheppPath> reeds_shepp_to_end,
180 std::shared_ptr<Node3d> current_node) {
181 std::shared_ptr<Node3d> end_node = std::shared_ptr<Node3d>(new Node3d(
182 reeds_shepp_to_end->x, reeds_shepp_to_end->y, reeds_shepp_to_end->phi,
183 XYbounds_, planner_open_space_config_));
184 end_node->SetPre(current_node);
185 end_node->SetTrajCost(current_node->GetTrajCost() + reeds_shepp_to_end->cost);
186 return end_node;
187}
188
189std::shared_ptr<Node3d> HybridAStar::Next_node_generator(
190 std::shared_ptr<Node3d> current_node, size_t next_node_index) {
191 double steering = 0.0;
192 double traveled_distance = 0.0;
193 if (next_node_index < static_cast<double>(next_node_num_) / 2) {
194 steering = -max_steer_angle_ +
195 (2 * max_steer_angle_ /
196 (static_cast<double>(next_node_num_) / 2 - 1))
197 * static_cast<double>(next_node_index);
198 traveled_distance = step_size_;
199 } else {
200 size_t index = next_node_index - next_node_num_ / 2;
201 steering = -max_steer_angle_
202 + (2 * max_steer_angle_
203 / (static_cast<double>(next_node_num_) / 2 - 1))
204 * static_cast<double>(index);
205 traveled_distance = -step_size_;
206 }
207 // take above motion primitive to generate a curve driving the car to a
208 // different grid
209 std::vector<double> intermediate_x;
210 std::vector<double> intermediate_y;
211 std::vector<double> intermediate_phi;
212 double last_x = current_node->GetX();
213 double last_y = current_node->GetY();
214 double last_phi = current_node->GetPhi();
215 intermediate_x.push_back(last_x);
216 intermediate_y.push_back(last_y);
217 intermediate_phi.push_back(last_phi);
218 for (size_t i = 0; i < arc_length_ / step_size_; ++i) {
219 const double next_phi = last_phi +
220 traveled_distance /
221 vehicle_param_.wheel_base() *
222 std::tan(steering);
223 const double next_x = last_x +
224 traveled_distance *
225 std::cos((last_phi + next_phi) / 2.0);
226 const double next_y = last_y +
227 traveled_distance *
228 std::sin((last_phi + next_phi) / 2.0);
229 intermediate_x.push_back(next_x);
230 intermediate_y.push_back(next_y);
231 intermediate_phi.push_back(common::math::NormalizeAngle(next_phi));
232 last_x = next_x;
233 last_y = next_y;
234 last_phi = next_phi;
235 }
236 // check if the vehicle runs outside of XY boundary
237 if (intermediate_x.back() > XYbounds_[1] ||
238 intermediate_x.back() < XYbounds_[0] ||
239 intermediate_y.back() > XYbounds_[3] ||
240 intermediate_y.back() < XYbounds_[2]) {
241 return nullptr;
242 }
243 std::shared_ptr<Node3d> next_node = std::shared_ptr<Node3d>(
244 new Node3d(intermediate_x, intermediate_y, intermediate_phi, XYbounds_,
245 planner_open_space_config_));
246 next_node->SetPre(current_node);
247 next_node->SetDirec(traveled_distance > 0.0);
248 next_node->SetSteer(steering);
249 return next_node;
250}
251
252void HybridAStar::CalculateNodeCost(
253 std::shared_ptr<Node3d> current_node, std::shared_ptr<Node3d> next_node) {
254 next_node->SetTrajCost(
255 current_node->GetTrajCost() + TrajCost(current_node, next_node));
256 // evaluate heuristic cost
257 double optimal_path_cost = 0.0;
258 optimal_path_cost += HoloObstacleHeuristic(next_node);
259 next_node->SetHeuCost(optimal_path_cost);
260}
261
262double HybridAStar::TrajCost(std::shared_ptr<Node3d> current_node,
263 std::shared_ptr<Node3d> next_node) {
264 // evaluate cost on the trajectory and add current cost
265 double piecewise_cost = 0.0;
266 if (next_node->GetDirec()) {
267 piecewise_cost +=
268 static_cast<double>(next_node->GetStepSize() - 1) *
269 step_size_ *
270 traj_forward_penalty_;
271 } else {
272 piecewise_cost +=
273 static_cast<double>(next_node->GetStepSize() - 1) *
274 step_size_ *
275 traj_back_penalty_;
276 }
277 AINFO << "traj cost: " << piecewise_cost;
278 if (current_node->GetDirec() != next_node->GetDirec()) {
279 piecewise_cost += traj_gear_switch_penalty_;
280 }
281 piecewise_cost += traj_steer_penalty_ * std::abs(next_node->GetSteer());
282 piecewise_cost += traj_steer_change_penalty_ *
283 std::abs(next_node->GetSteer() - current_node->GetSteer());
284 return piecewise_cost;
285}
286
287double HybridAStar::HoloObstacleHeuristic(std::shared_ptr<Node3d> next_node) {
288 return grid_a_star_heuristic_generator_->CheckDpMap(
289 next_node->GetX(), next_node->GetY());
290}
291
292bool HybridAStar::GetResult(HybridAStartResult* result) {
293 std::shared_ptr<Node3d> current_node = final_node_;
294 std::vector<double> hybrid_a_x;
295 std::vector<double> hybrid_a_y;
296 std::vector<double> hybrid_a_phi;
297 AINFO << "switch node:" << current_node->GetXs().back()
298 << ", " << current_node->GetYs().back();
299 AINFO << "switch node:" << current_node->GetXs().front()
300 << ", " << current_node->GetYs().front();
301 AINFO << "cost: " << final_node_->GetCost()
302 << "," << final_node_->GetTrajCost();
303 while (current_node->GetPreNode() != nullptr) {
304 std::vector<double> x = current_node->GetXs();
305 std::vector<double> y = current_node->GetYs();
306 std::vector<double> phi = current_node->GetPhis();
307 if (x.empty() || y.empty() || phi.empty()) {
308 AERROR << "result size check failed";
309 return false;
310 }
311 if (x.size() != y.size() || x.size() != phi.size()) {
312 AERROR << "states sizes are not equal";
313 return false;
314 }
315 std::reverse(x.begin(), x.end());
316 std::reverse(y.begin(), y.end());
317 std::reverse(phi.begin(), phi.end());
318 x.pop_back();
319 y.pop_back();
320 phi.pop_back();
321 hybrid_a_x.insert(hybrid_a_x.end(), x.begin(), x.end());
322 hybrid_a_y.insert(hybrid_a_y.end(), y.begin(), y.end());
323 hybrid_a_phi.insert(hybrid_a_phi.end(), phi.begin(), phi.end());
324 current_node = current_node->GetPreNode();
325 }
326 hybrid_a_x.push_back(current_node->GetX());
327 hybrid_a_y.push_back(current_node->GetY());
328 hybrid_a_phi.push_back(current_node->GetPhi());
329 std::reverse(hybrid_a_x.begin(), hybrid_a_x.end());
330 std::reverse(hybrid_a_y.begin(), hybrid_a_y.end());
331 std::reverse(hybrid_a_phi.begin(), hybrid_a_phi.end());
332 (*result).x = hybrid_a_x;
333 (*result).y = hybrid_a_y;
334 (*result).phi = hybrid_a_phi;
335
336 if (!GetTemporalProfile(result)) {
337 AERROR << "GetSpeedProfile from Hybrid Astar path fails";
338 return false;
339 }
340
341 if (result->x.size() != result->y.size() ||
342 result->x.size() != result->v.size() ||
343 result->x.size() != result->phi.size()) {
344 AERROR << "state sizes not equal, "
345 << "result->x.size(): " << result->x.size()
346 << "result->y.size()" << result->y.size()
347 << "result->phi.size()" << result->phi.size()
348 << "result->v.size()" << result->v.size();
349 return false;
350 }
351 if (result->a.size() != result->steer.size() ||
352 result->x.size() - result->a.size() != 1) {
353 AERROR << "control sizes not equal or not right";
354 AERROR << " acceleration size: " << result->a.size();
355 AERROR << " steer size: " << result->steer.size();
356 AERROR << " x size: " << result->x.size();
357 return false;
358 }
359 return true;
360}
361
362bool HybridAStar::GenerateSpeedAcceleration(
363 HybridAStartResult* result) {
364 AINFO << "GenerateSpeedAcceleration";
365 // Sanity Check
366 if (result->x.size() < 2 || result->y.size() < 2 || result->phi.size() < 2) {
367 AERROR << "result size check when generating speed and acceleration fail";
368 return false;
369 }
370 const size_t x_size = result->x.size();
371
372 // load velocity from position
373 // initial and end speed are set to be zeros
374 result->v.push_back(0.0);
375 for (size_t i = 1; i + 1 < x_size; ++i) {
376 double discrete_v = (((result->x[i + 1] - result->x[i]) / delta_t_) *
377 std::cos(result->phi[i]) +
378 ((result->x[i] - result->x[i - 1]) / delta_t_) *
379 std::cos(result->phi[i])) /
380 2.0 +
381 (((result->y[i + 1] - result->y[i]) / delta_t_) *
382 std::sin(result->phi[i]) +
383 ((result->y[i] - result->y[i - 1]) / delta_t_) *
384 std::sin(result->phi[i])) /
385 2.0;
386 result->v.push_back(discrete_v);
387 }
388 result->v.push_back(0.0);
389
390 // load acceleration from velocity
391 for (size_t i = 0; i + 1 < x_size; ++i) {
392 const double discrete_a = (result->v[i + 1] - result->v[i]) / delta_t_;
393 result->a.push_back(discrete_a);
394 }
395
396 // load steering from phi
397 for (size_t i = 0; i + 1 < x_size; ++i) {
398 double discrete_steer = (result->phi[i + 1] - result->phi[i]) *
399 vehicle_param_.wheel_base() / step_size_;
400 if (result->v[i] > 0.0) {
401 discrete_steer = std::atan(discrete_steer);
402 } else {
403 discrete_steer = std::atan(-discrete_steer);
404 }
405 result->steer.push_back(discrete_steer);
406 }
407 return true;
408}
409
410bool HybridAStar::GenerateSCurveSpeedAcceleration(HybridAStartResult* result) {
411 AINFO << "GenerateSCurveSpeedAcceleration";
412 // sanity check
413 CHECK_NOTNULL(result);
414 if (result->x.size() < 2 || result->y.size() < 2 || result->phi.size() < 2) {
415 AERROR << "result size check when generating speed and acceleration fail";
416 return false;
417 }
418 if (result->x.size() != result->y.size() ||
419 result->x.size() != result->phi.size()) {
420 AERROR << "result sizes not equal";
421 return false;
422 }
423
424 // get gear info
425 double init_heading = result->phi.front();
426 const Vec2d init_tracking_vector(result->x[1] - result->x[0],
427 result->y[1] - result->y[0]);
428 const double gear =
430 init_heading - init_tracking_vector.Angle())) < M_PI_2;
431
432 // get path lengh
433 size_t path_points_size = result->x.size();
434
435 double accumulated_s = 0.0;
436 result->accumulated_s.clear();
437 auto last_x = result->x.front();
438 auto last_y = result->y.front();
439 for (size_t i = 0; i < path_points_size; ++i) {
440 double x_diff = result->x[i] - last_x;
441 double y_diff = result->y[i] - last_y;
442 accumulated_s += std::sqrt(x_diff * x_diff + y_diff * y_diff);
443 result->accumulated_s.push_back(accumulated_s);
444 last_x = result->x[i];
445 last_y = result->y[i];
446 }
447 // assume static initial state
448 const double init_v = 0.0;
449 const double init_a = 0.0;
450
451 // minimum time speed optimization
452 // TODO(Jinyun): move to confs
453
454 SpeedData speed_data;
455
456 // TODO(Jinyun): explore better time horizon heuristic
457 const double path_length = result->accumulated_s.back();
458 const double total_t =
459 std::max(gear ? 1.5 *
460 (max_forward_v_ * max_forward_v_ +
461 path_length * max_forward_acc_) /
462 (max_forward_acc_ * max_forward_v_)
463 : 1.5 *
464 (max_reverse_v_ * max_reverse_v_ +
465 path_length * max_reverse_acc_) /
466 (max_reverse_acc_ * max_reverse_v_),
467 10.0);
468 if (total_t + delta_t_ >= delta_t_ * std::numeric_limits<size_t>::max()) {
469 AERROR << "Number of knots overflow. total_t: " << total_t
470 << ", delta_t: " << delta_t_;
471 return false;
472 }
473 const size_t num_of_knots = static_cast<size_t>(total_t / delta_t_) + 1;
474
475 PiecewiseJerkSpeedProblem piecewise_jerk_problem(
476 num_of_knots, delta_t_, {0.0, std::abs(init_v), std::abs(init_a)});
477
478 // set end constraints
479 std::vector<std::pair<double, double>> x_bounds(num_of_knots,
480 {0.0, path_length});
481
482 const double max_v = gear ? max_forward_v_ : max_reverse_v_;
483 const double max_acc = gear ? max_forward_acc_ : max_reverse_acc_;
484
485 const auto upper_dx = std::fmax(max_v, std::abs(init_v));
486 std::vector<std::pair<double, double>> dx_bounds(num_of_knots,
487 {0.0, upper_dx});
488 std::vector<std::pair<double, double>> ddx_bounds(num_of_knots,
489 {-max_acc, max_acc});
490
491 x_bounds[num_of_knots - 1] = std::make_pair(path_length, path_length);
492 dx_bounds[num_of_knots - 1] = std::make_pair(0.0, 0.0);
493 ddx_bounds[num_of_knots - 1] = std::make_pair(0.0, 0.0);
494
495 // TODO(Jinyun): move to confs
496 std::vector<double> x_ref(num_of_knots, path_length);
497 piecewise_jerk_problem.set_x_ref(ref_s_weight_, std::move(x_ref));
498 piecewise_jerk_problem.set_dx_ref(ref_v_weight_, max_v * 0.8);
499 piecewise_jerk_problem.set_weight_ddx(acc_weight_);
500 piecewise_jerk_problem.set_weight_dddx(jerk_weight_);
501 piecewise_jerk_problem.set_x_bounds(std::move(x_bounds));
502 piecewise_jerk_problem.set_dx_bounds(std::move(dx_bounds));
503 piecewise_jerk_problem.set_ddx_bounds(std::move(ddx_bounds));
504 piecewise_jerk_problem.set_dddx_bound(max_acc_jerk_);
505
506 // solve the problem
507 if (!piecewise_jerk_problem.Optimize()) {
508 AERROR << "Piecewise jerk speed optimizer failed!";
509 return false;
510 }
511
512 // extract output
513 const std::vector<double>& s = piecewise_jerk_problem.opt_x();
514 const std::vector<double>& ds = piecewise_jerk_problem.opt_dx();
515 const std::vector<double>& dds = piecewise_jerk_problem.opt_ddx();
516
517 // assign speed point by gear
518 speed_data.AppendSpeedPoint(s[0], 0.0, ds[0], dds[0], 0.0);
519 const double kEpislon = 1.0e-6;
520 const double sEpislon = 1.0e-6;
521 for (size_t i = 1; i < num_of_knots; ++i) {
522 if (s[i - 1] - s[i] > kEpislon) {
523 ADEBUG << "unexpected decreasing s in speed smoothing at time "
524 << static_cast<double>(i) * delta_t_
525 << "with total time " << total_t;
526 break;
527 }
528 speed_data.AppendSpeedPoint(
529 s[i], delta_t_ * static_cast<double>(i), ds[i],
530 dds[i], (dds[i] - dds[i - 1]) / delta_t_);
531 // cut the speed data when it is about to meet end condition
532 if (path_length - s[i] < sEpislon) {
533 break;
534 }
535 }
536
537 // combine speed and path profile
538 DiscretizedPath path_data;
539 for (size_t i = 0; i < path_points_size; ++i) {
540 common::PathPoint path_point;
541 path_point.set_x(result->x[i]);
542 path_point.set_y(result->y[i]);
543 path_point.set_theta(result->phi[i]);
544 path_point.set_s(result->accumulated_s[i]);
545 path_data.push_back(std::move(path_point));
546 }
547
548 HybridAStartResult combined_result;
549
550 // TODO(Jinyun): move to confs
551 const double kDenseTimeResoltuion = 0.5;
552 const double time_horizon = speed_data.TotalTime() +
553 kDenseTimeResoltuion * 1.0e-6;
554 if (path_data.empty()) {
555 AERROR << "path data is empty";
556 return false;
557 }
558 for (double cur_rel_time = 0.0; cur_rel_time < time_horizon;
559 cur_rel_time += kDenseTimeResoltuion) {
560 common::SpeedPoint speed_point;
561 if (!speed_data.EvaluateByTime(cur_rel_time, &speed_point)) {
562 AERROR << "Fail to get speed point with relative time " << cur_rel_time;
563 return false;
564 }
565
566 if (speed_point.s() > path_data.Length()) {
567 break;
568 }
569
570 common::PathPoint path_point = path_data.Evaluate(speed_point.s());
571
572 combined_result.x.push_back(path_point.x());
573 combined_result.y.push_back(path_point.y());
574 combined_result.phi.push_back(path_point.theta());
575 combined_result.accumulated_s.push_back(path_point.s());
576 if (!gear) {
577 combined_result.v.push_back(-speed_point.v());
578 combined_result.a.push_back(-speed_point.a());
579 } else {
580 combined_result.v.push_back(speed_point.v());
581 combined_result.a.push_back(speed_point.a());
582 }
583 }
584
585 combined_result.a.pop_back();
586
587 // recalc step size
588 path_points_size = combined_result.x.size();
589
590 // load steering from phi
591 for (size_t i = 0; i + 1 < path_points_size; ++i) {
592 double discrete_steer =
593 (combined_result.phi[i + 1] - combined_result.phi[i]) *
594 vehicle_param_.wheel_base() /
595 (combined_result.accumulated_s[i + 1] -
596 combined_result.accumulated_s[i]);
597 discrete_steer =
598 gear ? std::atan(discrete_steer) : std::atan(-discrete_steer);
599 combined_result.steer.push_back(discrete_steer);
600 }
601
602 *result = combined_result;
603 return true;
604}
605
607 const HybridAStartResult& result,
608 std::vector<HybridAStartResult>* partitioned_result) {
609 const auto& x = result.x;
610 const auto& y = result.y;
611 const auto& phi = result.phi;
612 if (x.size() != y.size() || x.size() != phi.size()) {
613 AERROR << "states sizes are not equal when do trajectory partitioning of "
614 "Hybrid A Star result";
615 return false;
616 }
617
618 size_t horizon = x.size();
619 partitioned_result->clear();
620 partitioned_result->emplace_back();
621 auto* current_traj = &(partitioned_result->back());
622 double heading_angle = phi.front();
623 const Vec2d init_tracking_vector(x[1] - x[0], y[1] - y[0]);
624 double tracking_angle = init_tracking_vector.Angle();
625 bool current_gear =
626 std::abs(common::math::NormalizeAngle(tracking_angle - heading_angle))
627 <
628 (M_PI_2);
629 for (size_t i = 0; i < horizon - 1; ++i) {
630 heading_angle = phi[i];
631 const Vec2d tracking_vector(x[i + 1] - x[i], y[i + 1] - y[i]);
632 tracking_angle = tracking_vector.Angle();
633 bool gear =
634 std::abs(common::math::NormalizeAngle(tracking_angle - heading_angle)) <
635 (M_PI_2);
636 if (gear != current_gear) {
637 current_traj->x.push_back(x[i]);
638 current_traj->y.push_back(y[i]);
639 current_traj->phi.push_back(phi[i]);
640 partitioned_result->emplace_back();
641 current_traj = &(partitioned_result->back());
642 current_gear = gear;
643 }
644 current_traj->x.push_back(x[i]);
645 current_traj->y.push_back(y[i]);
646 current_traj->phi.push_back(phi[i]);
647 }
648 current_traj->x.push_back(x.back());
649 current_traj->y.push_back(y.back());
650 current_traj->phi.push_back(phi.back());
651
652 const auto start_timestamp = std::chrono::system_clock::now();
653
654 // Retrieve v, a and steer from path
655 for (auto& result : *partitioned_result) {
656 if (FLAGS_use_s_curve_speed_smooth) {
657 if (!GenerateSCurveSpeedAcceleration(&result)) {
658 AERROR << "GenerateSCurveSpeedAcceleration fail";
659 return false;
660 }
661 } else {
662 if (!GenerateSpeedAcceleration(&result)) {
663 AERROR << "GenerateSpeedAcceleration fail";
664 return false;
665 }
666 }
667 }
668
669 const auto end_timestamp = std::chrono::system_clock::now();
670 std::chrono::duration<double> diff = end_timestamp - start_timestamp;
671 ADEBUG << "speed profile total time: " << diff.count() * 1000.0 << " ms.";
672 return true;
673}
674
675bool HybridAStar::GetTemporalProfile(HybridAStartResult* result) {
676 std::vector<HybridAStartResult> partitioned_results;
677 if (!TrajectoryPartition(*result, &partitioned_results)) {
678 AERROR << "TrajectoryPartition fail";
679 return false;
680 }
681 ADEBUG << "PARTION SIZE " << partitioned_results.size();
682 HybridAStartResult stitched_result;
683 for (const auto& result : partitioned_results) {
684 std::copy(result.x.begin(), result.x.end() - 1,
685 std::back_inserter(stitched_result.x));
686 std::copy(result.y.begin(), result.y.end() - 1,
687 std::back_inserter(stitched_result.y));
688 std::copy(result.phi.begin(), result.phi.end() - 1,
689 std::back_inserter(stitched_result.phi));
690 std::copy(result.v.begin(), result.v.end() - 1,
691 std::back_inserter(stitched_result.v));
692 std::copy(result.a.begin(), result.a.end(),
693 std::back_inserter(stitched_result.a));
694 std::copy(result.steer.begin(), result.steer.end(),
695 std::back_inserter(stitched_result.steer));
696 }
697 stitched_result.x.push_back(partitioned_results.back().x.back());
698 stitched_result.y.push_back(partitioned_results.back().y.back());
699 stitched_result.phi.push_back(partitioned_results.back().phi.back());
700 stitched_result.v.push_back(partitioned_results.back().v.back());
701 *result = stitched_result;
702 return true;
703}
704
706 double sx, double sy, double sphi, double ex, double ey, double ephi,
707 const std::vector<double>& XYbounds,
708 const std::vector<std::vector<common::math::Vec2d>>& obstacles_vertices_vec,
709 HybridAStartResult* result,
710 const std::vector<std::vector<common::math::Vec2d>>&
711 soft_boundary_vertices_vec,
712 bool reeds_sheep_last_straight) {
713 reed_shepp_generator_->reeds_sheep_last_straight_ = reeds_sheep_last_straight;
714 // clear containers
715 open_set_.clear();
716 close_set_.clear();
717 open_pq_ = decltype(open_pq_)();
718 final_node_ = nullptr;
719 PrintCurves print_curves;
720 std::vector<std::vector<common::math::LineSegment2d>>
721 obstacles_linesegments_vec;
722 for (const auto& obstacle_vertices : obstacles_vertices_vec) {
723 size_t vertices_num = obstacle_vertices.size();
724 std::vector<common::math::LineSegment2d> obstacle_linesegments;
725 for (size_t i = 0; i < vertices_num - 1; ++i) {
726 common::math::LineSegment2d line_segment =
728 obstacle_vertices[i], obstacle_vertices[i + 1]);
729 obstacle_linesegments.emplace_back(line_segment);
730 }
731 obstacles_linesegments_vec.emplace_back(obstacle_linesegments);
732 }
733 obstacles_linesegments_vec_ = std::move(obstacles_linesegments_vec);
734 for (size_t i = 0; i < obstacles_linesegments_vec_.size(); i++) {
735 for (auto linesg : obstacles_linesegments_vec_[i]) {
736 std::string name = std::to_string(i) + "roi_boundary";
737 print_curves.AddPoint(name, linesg.start());
738 print_curves.AddPoint(name, linesg.end());
739 }
740 }
741 Vec2d sposition(sx, sy);
742 Vec2d svec_to_center(
743 (vehicle_param_.front_edge_to_center() -
744 vehicle_param_.back_edge_to_center()) / 2.0,
745 (vehicle_param_.left_edge_to_center() -
746 vehicle_param_.right_edge_to_center()) / 2.0);
747 Vec2d scenter(sposition + svec_to_center.rotate(sphi));
748 Box2d sbox(scenter, sphi, vehicle_param_.length(), vehicle_param_.width());
749 print_curves.AddPoint("vehicle_start_box", sbox.GetAllCorners());
750 Vec2d eposition(ex, ey);
751 print_curves.AddPoint("end_position", eposition);
752 Vec2d evec_to_center(
753 (vehicle_param_.front_edge_to_center() -
754 vehicle_param_.back_edge_to_center()) / 2.0,
755 (vehicle_param_.left_edge_to_center() -
756 vehicle_param_.right_edge_to_center()) / 2.0);
757 Vec2d ecenter(eposition + evec_to_center.rotate(ephi));
758 Box2d ebox(ecenter, ephi, vehicle_param_.length(), vehicle_param_.width());
759 print_curves.AddPoint("vehicle_end_box", ebox.GetAllCorners());
760 XYbounds_ = XYbounds;
761// load nodes and obstacles
762 start_node_.reset(
763 new Node3d({sx}, {sy}, {sphi}, XYbounds_, planner_open_space_config_));
764 end_node_.reset(
765 new Node3d({ex}, {ey}, {ephi}, XYbounds_, planner_open_space_config_));
766 AINFO << "start node" << sx << "," << sy << "," << sphi;
767 AINFO << "end node " << ex << "," << ey << "," << ephi;
768 if (!ValidityCheck(start_node_)) {
769 AERROR << "start_node in collision with obstacles";
770 AERROR << start_node_->GetX()
771 << "," << start_node_->GetY()
772 << "," << start_node_->GetPhi();
773 print_curves.PrintToLog();
774 return false;
775 }
776 if (!ValidityCheck(end_node_)) {
777 AERROR << "end_node in collision with obstacles";
778 print_curves.PrintToLog();
779 return false;
780 }
781 double map_time = Clock::NowInSeconds();
782 grid_a_star_heuristic_generator_->GenerateDpMap(
783 ex, ey, XYbounds_, obstacles_linesegments_vec_);
784 ADEBUG << "map time " << Clock::NowInSeconds() - map_time;
785 // load open set, pq
786 open_set_.insert(start_node_->GetIndex());
787 open_pq_.emplace(start_node_, start_node_->GetCost());
788 // Hybrid A* begins
789 size_t explored_node_num = 0;
790 size_t available_result_num = 0;
791 auto best_explored_num = explored_node_num;
792 auto best_available_result_num = available_result_num;
793 double astar_start_time = Clock::NowInSeconds();
794 double heuristic_time = 0.0;
795 double rs_time = 0.0;
796 double node_generator_time = 0.0;
797 double validity_check_time = 0.0;
798 size_t max_explored_num =
799 planner_open_space_config_.warm_start_config().max_explored_num();
800 size_t desired_explored_num = std::min(
801 planner_open_space_config_.warm_start_config().desired_explored_num(),
802 planner_open_space_config_.warm_start_config().max_explored_num());
803 static constexpr int kMaxNodeNum = 200000;
804 std::vector<std::shared_ptr<Node3d>> candidate_final_nodes;
805 while (!open_pq_.empty() &&
806 open_pq_.size() < kMaxNodeNum &&
807 available_result_num < desired_explored_num &&
808 explored_node_num < max_explored_num) {
809 std::shared_ptr<Node3d> current_node = open_pq_.top().first;
810 open_pq_.pop();
811 const double rs_start_time = Clock::NowInSeconds();
812 std::shared_ptr<Node3d> final_node = nullptr;
813 if (AnalyticExpansion(current_node, &final_node)) {
814 if (final_node_ == nullptr ||
815 final_node_->GetTrajCost() > final_node->GetTrajCost()) {
816 final_node_ = final_node;
817 best_explored_num = explored_node_num + 1;
818 best_available_result_num = available_result_num + 1;
819 }
820 available_result_num++;
821 }
822 explored_node_num++;
823 const double rs_end_time = Clock::NowInSeconds();
824 rs_time += rs_end_time - rs_start_time;
825 close_set_.insert(current_node->GetIndex());
826
827 if (Clock::NowInSeconds() - astar_start_time >
828 planner_open_space_config_.warm_start_config()
830 available_result_num > 0) {
831 break;
832 }
833
834 size_t begin_index = 0;
835 size_t end_index = next_node_num_;
836 std::unordered_set<std::string> temp_set;
837 for (size_t i = begin_index; i < end_index; ++i) {
838 const double gen_node_time = Clock::NowInSeconds();
839 std::shared_ptr<Node3d> next_node = Next_node_generator(current_node, i);
840 node_generator_time += Clock::NowInSeconds() - gen_node_time;
841
842 // boundary check failure handle
843 if (next_node == nullptr) {
844 continue;
845 }
846 // check if the node is already in the close set
847 if (close_set_.count(next_node->GetIndex()) > 0) {
848 continue;
849 }
850 // collision check
851 const double validity_check_start_time = Clock::NowInSeconds();
852 if (!ValidityCheck(next_node)) {
853 continue;
854 }
855 validity_check_time += Clock::NowInSeconds() - validity_check_start_time;
856 if (open_set_.count(next_node->GetIndex()) == 0) {
857 const double start_time = Clock::NowInSeconds();
858 CalculateNodeCost(current_node, next_node);
859 const double end_time = Clock::NowInSeconds();
860 heuristic_time += end_time - start_time;
861 temp_set.insert(next_node->GetIndex());
862 open_pq_.emplace(next_node, next_node->GetCost());
863 }
864 }
865 open_set_.insert(temp_set.begin(), temp_set.end());
866 }
867
868 if (final_node_ == nullptr) {
869 AERROR << "Hybird A* cannot find a valid path";
870 print_curves.PrintToLog();
871 return false;
872 }
873
874 AINFO << "open_pq_.empty()" << (open_pq_.empty() ? "true" : "false");
875 AINFO << "open_pq_.size()" << open_pq_.size();
876 AINFO << "desired_explored_num" << desired_explored_num;
877 AINFO << "min cost is : " << final_node_->GetTrajCost();
878 AINFO << "max_explored_num is " << max_explored_num;
879 AINFO << "explored node num is " << explored_node_num
880 << "available_result_num " << available_result_num;
881 AINFO << "best_explored_num is " << best_explored_num
882 << "best_available_result_num is " << best_available_result_num;
883 AINFO << "cal node time is " << heuristic_time
884 << "validity_check_time " << validity_check_time
885 << "node_generator_time " << node_generator_time;
886 AINFO << "reed shepp time is " << rs_time;
887 AINFO << "hybrid astar total time is "
888 << Clock::NowInSeconds() - astar_start_time;
889
890 print_curves.AddPoint(
891 "rs_point", final_node_->GetXs().front(), final_node_->GetYs().front());
892 if (!GetResult(result)) {
893 AERROR << "GetResult failed";
894 print_curves.PrintToLog();
895 return false;
896 }
897 for (size_t i = 0; i < result->x.size(); i++) {
898 print_curves.AddPoint("warm_path", result->x[i], result->y[i]);
899 }
900 // PrintBox print_box("warm_path_box");
901 // for (size_t i = 0; i < result->x.size(); i = i + 5) {
902 // print_box.AddAdcBox(result->x[i], result->y[i], result->phi[i]);
903 // }
904 // print_box.PrintToLog();
905 print_curves.PrintToLog();
906 return true;
907}
908} // namespace planning
909} // namespace apollo
Rectangular (undirected) bounding box in 2-D.
Definition box2d.h:52
void GetAllCorners(std::vector< Vec2d > *const corners) const
Getter of the corners of the box
Definition box2d.cc:140
Implements a class of 2-dimensional vectors.
Definition vec2d.h:42
Vec2d rotate(const double angle) const
rotate the vector by angle.
Definition vec2d.cc:65
double Angle() const
Gets the angle between the vector and the positive x semi-axis
Definition vec2d.cc:37
a singleton clock that can be used to get the current timestamp.
Definition clock.h:39
static double NowInSeconds()
gets the current time in second.
Definition clock.cc:56
HybridAStar(const PlannerOpenSpaceConfig &open_space_conf)
bool TrajectoryPartition(const HybridAStartResult &result, std::vector< HybridAStartResult > *partitioned_result)
bool Plan(double sx, double sy, double sphi, double ex, double ey, double ephi, const std::vector< double > &XYbounds, const std::vector< std::vector< common::math::Vec2d > > &obstacles_vertices_vec, HybridAStartResult *result, const std::vector< std::vector< common::math::Vec2d > > &soft_boundary_vertices_vec={}, bool reeds_sheep_last_straight=false)
static apollo::common::math::Box2d GetBoundingBox(const common::VehicleParam &vehicle_param_, const double x, const double y, const double phi)
Definition node3d.cc:98
void AddPoint(std::string key, double x, double y)
add point to curve key
Planning module main class.
#define ADEBUG
Definition log.h:41
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
double NormalizeAngle(const double angle)
Normalize angle to [-PI, PI).
Definition math_utils.cc:53
class register implement
Definition arena_queue.h:37
Definition future.h:29
optional IterativeAnchoringConfig iterative_anchoring_smoother_config