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