hybrid_a_star.cc 25.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

/*
J
JasonZhou404 已提交
18
 * @file
19 20
 */

21
#include "modules/planning/open_space/coarse_trajectory_generator/hybrid_a_star.h"
22

23
#include "modules/planning/math/piecewise_jerk/piecewise_jerk_speed_problem.h"
24

25 26 27
namespace apollo {
namespace planning {

28 29
using apollo::common::time::Clock;

30 31
HybridAStar::HybridAStar(const PlannerOpenSpaceConfig& open_space_conf) {
  planner_open_space_config_.CopyFrom(open_space_conf);
32 33 34
  reed_shepp_generator_ =
      std::make_unique<ReedShepp>(vehicle_param_, planner_open_space_config_);
  grid_a_star_heuristic_generator_ =
35
      std::make_unique<GridSearch>(planner_open_space_config_);
36 37
  next_node_num_ =
      planner_open_space_config_.warm_start_config().next_node_num();
J
JasonZhou404 已提交
38 39
  max_steer_angle_ =
      vehicle_param_.max_steer_angle() / vehicle_param_.steer_ratio();
40
  step_size_ = planner_open_space_config_.warm_start_config().step_size();
41
  xy_grid_resolution_ =
42 43
      planner_open_space_config_.warm_start_config().xy_grid_resolution();
  delta_t_ = planner_open_space_config_.delta_t();
44 45 46 47 48 49 50 51 52 53
  traj_forward_penalty_ =
      planner_open_space_config_.warm_start_config().traj_forward_penalty();
  traj_back_penalty_ =
      planner_open_space_config_.warm_start_config().traj_back_penalty();
  traj_gear_switch_penalty_ =
      planner_open_space_config_.warm_start_config().traj_gear_switch_penalty();
  traj_steer_penalty_ =
      planner_open_space_config_.warm_start_config().traj_steer_penalty();
  traj_steer_change_penalty_ = planner_open_space_config_.warm_start_config()
                                   .traj_steer_change_penalty();
54
}
55

56
bool HybridAStar::AnalyticExpansion(std::shared_ptr<Node3d> current_node) {
57
  std::shared_ptr<ReedSheppPath> reeds_shepp_to_check =
58 59 60
      std::make_shared<ReedSheppPath>();
  if (!reed_shepp_generator_->ShortestRSP(current_node, end_node_,
                                          reeds_shepp_to_check)) {
61
    ADEBUG << "ShortestRSP failed";
62 63 64
    return false;
  }

65
  if (!RSPCheck(reeds_shepp_to_check)) {
66 67
    return false;
  }
68

Q
Qi Luo 已提交
69
  ADEBUG << "Reach the end configuration with Reed Sharp";
70 71
  // load the whole RSP as nodes and add to the close set
  final_node_ = LoadRSPinCS(reeds_shepp_to_check, current_node);
72 73 74
  return true;
}

75
bool HybridAStar::RSPCheck(
76
    const std::shared_ptr<ReedSheppPath> reeds_shepp_to_end) {
77 78 79
  std::shared_ptr<Node3d> node = std::shared_ptr<Node3d>(new Node3d(
      reeds_shepp_to_end->x, reeds_shepp_to_end->y, reeds_shepp_to_end->phi,
      XYbounds_, planner_open_space_config_));
A
Aaron Xiao 已提交
80
  return ValidityCheck(node);
81 82
}

83
bool HybridAStar::ValidityCheck(std::shared_ptr<Node3d> node) {
X
 
Xiangquan Xiao 已提交
84
  if (obstacles_linesegments_vec_.empty()) {
85 86
    return true;
  }
87
  size_t node_step_size = node->GetStepSize();
J
JasonZhou404 已提交
88
  size_t last_check_index = 0;
89 90 91 92 93 94
  std::vector<double> traversed_x;
  std::vector<double> traversed_y;
  std::vector<double> traversed_phi;
  traversed_x = node->GetXs();
  traversed_y = node->GetYs();
  traversed_phi = node->GetPhis();
J
JasonZhou404 已提交
95 96 97
  std::reverse(traversed_x.begin(), traversed_x.end());
  std::reverse(traversed_y.begin(), traversed_y.end());
  std::reverse(traversed_phi.begin(), traversed_phi.end());
98 99 100
  // The first {x, y, phi} is collision free unless they are start and end
  // configuration of search problem
  if (node_step_size == 1) {
J
JasonZhou404 已提交
101
    last_check_index = 1;
102
  } else {
J
JasonZhou404 已提交
103
    last_check_index = node_step_size - 1;
104
  }
J
JasonZhou404 已提交
105
  for (size_t i = 0; i < last_check_index; ++i) {
106 107 108 109 110 111 112 113 114 115 116 117
    if (traversed_x[i] > XYbounds_[1] || traversed_x[i] < XYbounds_[0] ||
        traversed_y[i] > XYbounds_[3] || traversed_y[i] < XYbounds_[2]) {
      return false;
    }
    Box2d bounding_box = Node3d::GetBoundingBox(
        vehicle_param_, traversed_x[i], traversed_y[i], traversed_phi[i]);
    for (const auto& obstacle_linesegments : obstacles_linesegments_vec_) {
      for (const common::math::LineSegment2d& linesegment :
           obstacle_linesegments) {
        if (bounding_box.HasOverlap(linesegment)) {
          return false;
        }
118
      }
119 120 121
    }
  }
  return true;
122
}
123

124
std::shared_ptr<Node3d> HybridAStar::LoadRSPinCS(
125
    const std::shared_ptr<ReedSheppPath> reeds_shepp_to_end,
126
    std::shared_ptr<Node3d> current_node) {
127 128 129
  std::shared_ptr<Node3d> end_node = std::shared_ptr<Node3d>(new Node3d(
      reeds_shepp_to_end->x, reeds_shepp_to_end->y, reeds_shepp_to_end->phi,
      XYbounds_, planner_open_space_config_));
130 131
  end_node->SetPre(current_node);
  close_set_.insert(std::make_pair(end_node->GetIndex(), end_node));
132
  return end_node;
133 134 135
}

std::shared_ptr<Node3d> HybridAStar::Next_node_generator(
136
    std::shared_ptr<Node3d> current_node, size_t next_node_index) {
137
  double steering = 0.0;
138
  size_t index = 0;
139
  double traveled_distance = 0.0;
140
  if (next_node_index < static_cast<double>(next_node_num_) / 2) {
J
JasonZhou404 已提交
141 142 143 144
    steering =
        -max_steer_angle_ +
        (2 * max_steer_angle_ / (static_cast<double>(next_node_num_) / 2 - 1)) *
            static_cast<double>(next_node_index);
J
JasonZhou404 已提交
145
    traveled_distance = step_size_;
146 147
  } else {
    index = next_node_index - next_node_num_ / 2;
J
JasonZhou404 已提交
148 149 150 151
    steering =
        -max_steer_angle_ +
        (2 * max_steer_angle_ / (static_cast<double>(next_node_num_) / 2 - 1)) *
            static_cast<double>(index);
J
JasonZhou404 已提交
152
    traveled_distance = -step_size_;
153 154 155 156 157 158 159 160 161 162
  }
  // take above motion primitive to generate a curve driving the car to a
  // different grid
  double arc = std::sqrt(2) * xy_grid_resolution_;
  std::vector<double> intermediate_x;
  std::vector<double> intermediate_y;
  std::vector<double> intermediate_phi;
  double last_x = current_node->GetX();
  double last_y = current_node->GetY();
  double last_phi = current_node->GetPhi();
Q
Qi Luo 已提交
163 164 165
  intermediate_x.push_back(last_x);
  intermediate_y.push_back(last_y);
  intermediate_phi.push_back(last_phi);
166
  for (size_t i = 0; i < arc / step_size_; ++i) {
Q
Qi Luo 已提交
167 168 169
    const double next_x = last_x + traveled_distance * std::cos(last_phi);
    const double next_y = last_y + traveled_distance * std::sin(last_phi);
    const double next_phi = common::math::NormalizeAngle(
170 171
        last_phi +
        traveled_distance / vehicle_param_.wheel_base() * std::tan(steering));
Q
Qi Luo 已提交
172 173 174
    intermediate_x.push_back(next_x);
    intermediate_y.push_back(next_y);
    intermediate_phi.push_back(next_phi);
175 176 177
    last_x = next_x;
    last_y = next_y;
    last_phi = next_phi;
178
  }
179 180 181 182 183 184 185
  // check if the vehicle runs outside of XY boundary
  if (intermediate_x.back() > XYbounds_[1] ||
      intermediate_x.back() < XYbounds_[0] ||
      intermediate_y.back() > XYbounds_[3] ||
      intermediate_y.back() < XYbounds_[2]) {
    return nullptr;
  }
186
  std::shared_ptr<Node3d> next_node = std::shared_ptr<Node3d>(
187 188
      new Node3d(intermediate_x, intermediate_y, intermediate_phi, XYbounds_,
                 planner_open_space_config_));
189
  next_node->SetPre(current_node);
190
  next_node->SetDirec(traveled_distance > 0.0);
191
  next_node->SetSteer(steering);
192 193 194
  return next_node;
}

J
JasonZhou404 已提交
195
void HybridAStar::CalculateNodeCost(std::shared_ptr<Node3d> current_node,
196
                                    std::shared_ptr<Node3d> next_node) {
197 198 199
  next_node->SetTrajCost(current_node->GetTrajCost() +
                         TrajCost(current_node, next_node));
  // evaluate heuristic cost
200
  double optimal_path_cost = 0.0;
J
JasonZhou404 已提交
201
  optimal_path_cost += HoloObstacleHeuristic(next_node);
202
  next_node->SetHeuCost(optimal_path_cost);
203 204 205 206
}

double HybridAStar::TrajCost(std::shared_ptr<Node3d> current_node,
                             std::shared_ptr<Node3d> next_node) {
207 208 209
  // evaluate cost on the trajectory and add current cost
  double piecewise_cost = 0.0;
  if (next_node->GetDirec()) {
210
    piecewise_cost += static_cast<double>(next_node->GetStepSize() - 1) *
211
                      step_size_ * traj_forward_penalty_;
212
  } else {
213
    piecewise_cost += static_cast<double>(next_node->GetStepSize() - 1) *
214
                      step_size_ * traj_back_penalty_;
215 216
  }
  if (current_node->GetDirec() != next_node->GetDirec()) {
217
    piecewise_cost += traj_gear_switch_penalty_;
218
  }
219 220
  piecewise_cost += traj_steer_penalty_ * std::abs(next_node->GetSteer());
  piecewise_cost += traj_steer_change_penalty_ *
221
                    std::abs(next_node->GetSteer() - current_node->GetSteer());
222
  return piecewise_cost;
223 224
}

J
JasonZhou404 已提交
225 226 227
double HybridAStar::HoloObstacleHeuristic(std::shared_ptr<Node3d> next_node) {
  return grid_a_star_heuristic_generator_->CheckDpMap(next_node->GetX(),
                                                      next_node->GetY());
228
}
229

230
bool HybridAStar::GetResult(HybridAStartResult* result) {
231
  std::shared_ptr<Node3d> current_node = final_node_;
232 233 234 235 236 237 238
  std::vector<double> hybrid_a_x;
  std::vector<double> hybrid_a_y;
  std::vector<double> hybrid_a_phi;
  while (current_node->GetPreNode() != nullptr) {
    std::vector<double> x = current_node->GetXs();
    std::vector<double> y = current_node->GetYs();
    std::vector<double> phi = current_node->GetPhis();
X
 
Xiangquan Xiao 已提交
239
    if (x.empty() || y.empty() || phi.empty()) {
J
JasonZhou404 已提交
240
      AERROR << "result size check failed";
241 242
      return false;
    }
243 244 245 246
    if (x.size() != y.size() || x.size() != phi.size()) {
      AERROR << "states sizes are not equal";
      return false;
    }
247 248 249 250 251 252 253 254 255 256 257
    std::reverse(x.begin(), x.end());
    std::reverse(y.begin(), y.end());
    std::reverse(phi.begin(), phi.end());
    x.pop_back();
    y.pop_back();
    phi.pop_back();
    hybrid_a_x.insert(hybrid_a_x.end(), x.begin(), x.end());
    hybrid_a_y.insert(hybrid_a_y.end(), y.begin(), y.end());
    hybrid_a_phi.insert(hybrid_a_phi.end(), phi.begin(), phi.end());
    current_node = current_node->GetPreNode();
  }
258 259 260
  hybrid_a_x.push_back(current_node->GetX());
  hybrid_a_y.push_back(current_node->GetY());
  hybrid_a_phi.push_back(current_node->GetPhi());
J
JasonZhou404 已提交
261 262 263
  std::reverse(hybrid_a_x.begin(), hybrid_a_x.end());
  std::reverse(hybrid_a_y.begin(), hybrid_a_y.end());
  std::reverse(hybrid_a_phi.begin(), hybrid_a_phi.end());
264 265 266
  (*result).x = hybrid_a_x;
  (*result).y = hybrid_a_y;
  (*result).phi = hybrid_a_phi;
267

268 269 270
  if (!GetTemporalProfile(result)) {
    AERROR << "GetSpeedProfile from Hybrid Astar path fails";
    return false;
271
  }
272

273 274 275
  if (result->x.size() != result->y.size() ||
      result->x.size() != result->v.size() ||
      result->x.size() != result->phi.size()) {
276 277 278 279
    AERROR << "state sizes not equal, "
           << "result->x.size(): " << result->x.size() << "result->y.size()"
           << result->y.size() << "result->phi.size()" << result->phi.size()
           << "result->v.size()" << result->v.size();
280 281 282 283
    return false;
  }
  if (result->a.size() != result->steer.size() ||
      result->x.size() - result->a.size() != 1) {
J
JasonZhou404 已提交
284
    AERROR << "control sizes not equal or not right";
Q
Qi Luo 已提交
285 286 287
    AERROR << " acceleration size: " << result->a.size();
    AERROR << " steer size: " << result->steer.size();
    AERROR << " x size: " << result->x.size();
288 289
    return false;
  }
290 291
  return true;
}
292

293
bool HybridAStar::GenerateSpeedAcceleration(HybridAStartResult* result) {
294
  // Sanity Check
295
  if (result->x.size() < 2 || result->y.size() < 2 || result->phi.size() < 2) {
J
JasonZhou404 已提交
296
    AERROR << "result size check when generating speed and acceleration fail";
297 298
    return false;
  }
299
  const size_t x_size = result->x.size();
300

301
  // load velocity from position
302
  // initial and end speed are set to be zeros
303 304
  result->v.push_back(0.0);
  for (size_t i = 1; i + 1 < x_size; ++i) {
305 306 307 308 309 310 311 312 313 314
    double discrete_v = (((result->x[i + 1] - result->x[i]) / delta_t_) *
                             std::cos(result->phi[i]) +
                         ((result->x[i] - result->x[i - 1]) / delta_t_) *
                             std::cos(result->phi[i])) /
                            2.0 +
                        (((result->y[i + 1] - result->y[i]) / delta_t_) *
                             std::sin(result->phi[i]) +
                         ((result->y[i] - result->y[i - 1]) / delta_t_) *
                             std::sin(result->phi[i])) /
                            2.0;
315
    result->v.push_back(discrete_v);
316
  }
Q
Qi Luo 已提交
317
  result->v.push_back(0.0);
318

319
  // load acceleration from velocity
Q
Qi Luo 已提交
320 321 322
  for (size_t i = 0; i + 1 < x_size; ++i) {
    const double discrete_a = (result->v[i + 1] - result->v[i]) / delta_t_;
    result->a.push_back(discrete_a);
323
  }
324

325
  // load steering from phi
Q
Qi Luo 已提交
326
  for (size_t i = 0; i + 1 < x_size; ++i) {
327 328
    double discrete_steer = (result->phi[i + 1] - result->phi[i]) *
                            vehicle_param_.wheel_base() / step_size_;
329
    if (result->v[i] > 0.0) {
330 331 332 333
      discrete_steer = std::atan(discrete_steer);
    } else {
      discrete_steer = std::atan(-discrete_steer);
    }
Q
Qi Luo 已提交
334
    result->steer.push_back(discrete_steer);
335 336 337 338
  }
  return true;
}

339 340 341 342 343 344
bool HybridAStar::GenerateSCurveSpeedAcceleration(HybridAStartResult* result) {
  if (result->x.size() < 2 || result->y.size() < 2 || result->phi.size() < 2) {
    AERROR << "result size check when generating speed and acceleration fail";
    return false;
  }

345
  const size_t x_size = result->x.size();
346

Q
Qi Luo 已提交
347 348
  ADEBUG << "x_size is: " << x_size;

349
  double accumulated_s = 0.0;
350 351
  std::vector<std::pair<double, double>> x_bounds;
  std::vector<std::pair<double, double>> dx_bounds;
352

Q
Qi Luo 已提交
353 354 355
  // Setup for initial point.
  result->accumulated_s.push_back(0.0);
  result->v.push_back(0.0);
356 357
  x_bounds.emplace_back(-10.0, 10.0);
  dx_bounds.emplace_back(-10.0, 10.0);
Q
Qi Luo 已提交
358 359 360

  ADEBUG << "Initial accumulated_s: " << 0.0 << " initial discrete_v: " << 0.0;

Q
Qi Luo 已提交
361
  for (size_t i = 0; i + 1 < x_size; ++i) {
362 363 364 365
    const double discrete_v = ((result->x[i + 1] - result->x[i]) / delta_t_) *
                                  std::cos(result->phi[i]) +
                              ((result->y[i + 1] - result->y[i]) / delta_t_) *
                                  std::sin(result->phi[i]);
366 367 368

    accumulated_s += discrete_v * delta_t_;

Q
Qi Luo 已提交
369 370
    result->v.push_back(discrete_v);
    result->accumulated_s.push_back(accumulated_s);
371 372
    x_bounds.emplace_back(accumulated_s - 10, accumulated_s + 10);
    dx_bounds.emplace_back(discrete_v - 10, discrete_v + 10);
Q
Qi Luo 已提交
373

374 375 376 377
    ADEBUG << "index: " << i << " accumulated_s: " << accumulated_s
           << " x_bounds: " << accumulated_s - 10 << " and "
           << accumulated_s + 10 << ", discrete_v: " << discrete_v
           << " dx_bounds: " << discrete_v - 10 << " and " << discrete_v + 10;
378 379
  }

Q
Qi Luo 已提交
380 381
  result->v[x_size - 1] = 0.0;

382 383
  std::array<double, 3> init_s = {result->accumulated_s[0], result->v[0],
                                  (result->v[1] - result->v[0]) / delta_t_};
384

Q
Qi Luo 已提交
385 386 387
  ADEBUG << "init_s: " << result->accumulated_s[0] << " " << result->v[0] << " "
         << (result->v[1] - result->v[0]) / delta_t_;

388
  // Start a PathTimeQpProblem
Q
Qi Luo 已提交
389 390 391 392
  std::array<double, 3> end_s = {result->accumulated_s[x_size - 1], 0.0, 0.0};

  ADEBUG << "end_s: " << result->accumulated_s[x_size - 1] << " " << 0.0 << " "
         << 0.0;
393

394
  PiecewiseJerkSpeedProblem path_time_qp(x_size, delta_t_, init_s);
395 396
  auto s_curve_config =
      planner_open_space_config_.warm_start_config().s_curve_config();
397 398
  path_time_qp.set_weight_ddx(s_curve_config.acc_weight());
  path_time_qp.set_weight_dddx(s_curve_config.jerk_weight());
399

400 401 402 403
  path_time_qp.set_x_bounds(x_bounds);

  path_time_qp.set_dx_bounds(dx_bounds);

404
  // TODO(QiL): load this from configs
405 406
  path_time_qp.set_ddx_bounds(-4.4, 10.0);
  path_time_qp.set_dddx_bound(FLAGS_longitudinal_jerk_bound);
407

408 409
  path_time_qp.set_x_ref(s_curve_config.ref_s_weight(), result->accumulated_s);
  path_time_qp.set_end_state_ref({1.0, 1.0, 0.0}, end_s);
410

411 412
  // Solve the problem
  if (!path_time_qp.Optimize()) {
413
    std::string msg("Piecewise jerk speed optimizer failed!");
414
    AERROR << msg;
415 416 417 418 419 420
    return false;
  }

  // Extract output
  result->v.clear();
  result->accumulated_s.clear();
421 422 423
  result->accumulated_s = path_time_qp.opt_x();
  result->v = path_time_qp.opt_dx();
  result->a = path_time_qp.opt_ddx();
Q
Qi Luo 已提交
424
  result->a.pop_back();
425 426

  // load steering from phi
Q
Qi Luo 已提交
427
  for (size_t i = 0; i + 1 < x_size; ++i) {
428 429 430 431 432 433 434
    double discrete_steer = (result->phi[i + 1] - result->phi[i]) *
                            vehicle_param_.wheel_base() / step_size_;
    if (result->v[i] > 0.0) {
      discrete_steer = std::atan(discrete_steer);
    } else {
      discrete_steer = std::atan(-discrete_steer);
    }
Q
Qi Luo 已提交
435
    result->steer.push_back(discrete_steer);
436 437 438 439 440
  }

  return true;
}

441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
bool HybridAStar::CombinePathAndSpeedProfile(const PathData& path_data,
                                             const SpeedData& speed_data,
                                             HybridAStartResult* result) {
  CHECK(result != nullptr);

  // Clear the result
  result->x.clear();
  result->y.clear();
  result->phi.clear();
  result->v.clear();
  result->a.clear();
  result->steer.clear();
  result->accumulated_s.clear();

  if (path_data.discretized_path().empty()) {
    AWARN << "path data is empty";
    return false;
  }

  // TODO(QiL): load 0.05 from configs
  for (double cur_rel_time = 0.0; cur_rel_time < speed_data.TotalTime();
       cur_rel_time += 0.05) {
    common::SpeedPoint speed_point;
    if (!speed_data.EvaluateByTime(cur_rel_time, &speed_point)) {
      AERROR << "Fail to get speed point with relative time " << cur_rel_time;
      return false;
    }

    if (speed_point.s() > path_data.discretized_path().Length()) {
      break;
    }
    common::PathPoint path_point;
    if (!path_data.GetPathPointWithPathS(speed_point.s(), &path_point)) {
      AERROR << "Fail to get path data with s " << speed_point.s()
             << "path total length " << path_data.discretized_path().Length();
      return false;
    }
    path_point.set_s(path_point.s());

    result->x.push_back(path_point.x());
    result->y.push_back(path_point.y());
    result->phi.push_back(path_point.theta());
    result->v.push_back(speed_point.v());
    result->a.push_back(speed_point.a());
    // TODO(QiL): update steer.
    result->accumulated_s.push_back(speed_point.s());
  }
  return true;
}

491 492 493 494 495 496 497 498 499 500 501 502
bool HybridAStar::TrajectoryPartition(
    const HybridAStartResult& result,
    std::vector<HybridAStartResult>* partitioned_result) {
  const auto& x = result.x;
  const auto& y = result.y;
  const auto& phi = result.phi;
  if (x.size() != y.size() || x.size() != phi.size()) {
    AERROR << "states sizes are not equal when do trajectory partitioning of "
              "Hybrid A Star result";
    return false;
  }

503
  size_t horizon = x.size();
504 505 506
  partitioned_result->clear();
  partitioned_result->emplace_back();
  auto* current_traj = &(partitioned_result->back());
507 508 509 510 511
  double heading_angle = phi.front();
  const Vec2d init_tracking_vector(x[1] - x[0], y[1] - y[0]);
  double tracking_angle = init_tracking_vector.Angle();
  bool current_gear =
      std::abs(common::math::NormalizeAngle(tracking_angle - heading_angle)) <
512
      (M_PI_2);
513 514 515 516 517 518
  for (size_t i = 0; i < horizon - 1; ++i) {
    heading_angle = phi[i];
    const Vec2d tracking_vector(x[i + 1] - x[i], y[i + 1] - y[i]);
    tracking_angle = tracking_vector.Angle();
    bool gear =
        std::abs(common::math::NormalizeAngle(tracking_angle - heading_angle)) <
519
        (M_PI_2);
520 521 522 523
    if (gear != current_gear) {
      current_traj->x.push_back(x[i]);
      current_traj->y.push_back(y[i]);
      current_traj->phi.push_back(phi[i]);
524 525
      partitioned_result->emplace_back();
      current_traj = &(partitioned_result->back());
526
      current_gear = gear;
527 528 529 530 531
    }
    current_traj->x.push_back(x[i]);
    current_traj->y.push_back(y[i]);
    current_traj->phi.push_back(phi[i]);
  }
532 533 534
  current_traj->x.push_back(x.back());
  current_traj->y.push_back(y.back());
  current_traj->phi.push_back(phi.back());
535 536

  // Retrieve v, a and steer from path
J
JasonZhou404 已提交
537
  for (auto& result : *partitioned_result) {
538
    if (FLAGS_use_s_curve_speed_smooth) {
539
      if (!GenerateSCurveSpeedAcceleration(&result)) {
540 541 542
        AERROR << "GenerateSCurveSpeedAcceleration fail";
        return false;
      }
543 544 545 546 547 548 549 550 551
      // TODO: generate PathData and SpeedData from results and combine
      // profiles.

      PathData path_data;
      SpeedData speed_data;
      DiscretizedTrajectory discretized_trajectory;
      if (!CombinePathAndSpeedProfile(path_data, speed_data, &result)) {
        return false;
      }
552
    } else {
553
      if (!GenerateSpeedAcceleration(&result)) {
554 555 556 557 558 559 560 561
        AERROR << "GenerateSpeedAcceleration fail";
        return false;
      }
    }
  }
  return true;
}

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
bool HybridAStar::GetTemporalProfile(HybridAStartResult* result) {
  std::vector<HybridAStartResult> partitioned_results;
  if (!TrajectoryPartition(*result, &partitioned_results)) {
    AERROR << "TrajectoryPartition fail";
    return false;
  }
  HybridAStartResult stitched_result;
  for (const auto& result : partitioned_results) {
    std::copy(result.x.begin(), result.x.end() - 1,
              std::back_inserter(stitched_result.x));
    std::copy(result.y.begin(), result.y.end() - 1,
              std::back_inserter(stitched_result.y));
    std::copy(result.phi.begin(), result.phi.end() - 1,
              std::back_inserter(stitched_result.phi));
    std::copy(result.v.begin(), result.v.end() - 1,
              std::back_inserter(stitched_result.v));
    std::copy(result.a.begin(), result.a.end(),
              std::back_inserter(stitched_result.a));
    std::copy(result.steer.begin(), result.steer.end(),
              std::back_inserter(stitched_result.steer));
  }
  stitched_result.x.push_back(partitioned_results.back().x.back());
  stitched_result.y.push_back(partitioned_results.back().y.back());
  stitched_result.phi.push_back(partitioned_results.back().phi.back());
  stitched_result.v.push_back(partitioned_results.back().v.back());
  *result = stitched_result;
  return true;
}

591 592 593 594 595
bool HybridAStar::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) {
596 597 598
  // clear containers
  open_set_.clear();
  close_set_.clear();
J
JasonZhou404 已提交
599
  open_pq_ = decltype(open_pq_)();
600 601
  final_node_ = nullptr;

602 603 604 605 606 607 608 609 610 611 612 613 614 615
  std::vector<std::vector<common::math::LineSegment2d>>
      obstacles_linesegments_vec;
  for (const auto& obstacle_vertices : obstacles_vertices_vec) {
    size_t vertices_num = obstacle_vertices.size();
    std::vector<common::math::LineSegment2d> obstacle_linesegments;
    for (size_t i = 0; i < vertices_num - 1; ++i) {
      common::math::LineSegment2d line_segment = common::math::LineSegment2d(
          obstacle_vertices[i], obstacle_vertices[i + 1]);
      obstacle_linesegments.emplace_back(line_segment);
    }
    obstacles_linesegments_vec.emplace_back(obstacle_linesegments);
  }
  obstacles_linesegments_vec_ = std::move(obstacles_linesegments_vec);

616 617
  // load XYbounds
  XYbounds_ = XYbounds;
618
  // load nodes and obstacles
619 620 621 622
  start_node_.reset(
      new Node3d({sx}, {sy}, {sphi}, XYbounds_, planner_open_space_config_));
  end_node_.reset(
      new Node3d({ex}, {ey}, {ephi}, XYbounds_, planner_open_space_config_));
623
  if (!ValidityCheck(start_node_)) {
624
    ADEBUG << "start_node in collision with obstacles";
625 626
    return false;
  }
627
  if (!ValidityCheck(end_node_)) {
628
    ADEBUG << "end_node in collision with obstacles";
629 630
    return false;
  }
631 632 633
  double map_time = Clock::NowInSeconds();
  grid_a_star_heuristic_generator_->GenerateDpMap(ex, ey, XYbounds_,
                                                  obstacles_linesegments_vec_);
Q
Qi Luo 已提交
634
  ADEBUG << "map time " << Clock::NowInSeconds() - map_time;
635
  // load open set, pq
636 637 638
  open_set_.insert(std::make_pair(start_node_->GetIndex(), start_node_));
  open_pq_.push(
      std::make_pair(start_node_->GetIndex(), start_node_->GetCost()));
639

640
  // Hybrid A* begins
641
  size_t explored_node_num = 0;
642
  double astar_start_time = Clock::NowInSeconds();
643
  double heuristic_time = 0.0;
644
  double rs_time = 0.0;
645 646
  double start_time = 0.0;
  double end_time = 0.0;
647
  while (!open_pq_.empty()) {
A
Aaron Xiao 已提交
648
    // take out the lowest cost neighboring node
649
    const std::string current_id = open_pq_.top().first;
650 651
    open_pq_.pop();
    std::shared_ptr<Node3d> current_node = open_set_[current_id];
652 653 654
    // check if a analystic curve could be connected from current
    // configuration to the end configuration without collision. if so, search
    // ends.
655
    start_time = Clock::NowInSeconds();
656
    if (AnalyticExpansion(current_node)) {
657
      break;
J
JasonZhou404 已提交
658
    }
659 660
    end_time = Clock::NowInSeconds();
    rs_time += end_time - start_time;
661
    close_set_.insert(std::make_pair(current_node->GetIndex(), current_node));
662
    for (size_t i = 0; i < next_node_num_; ++i) {
663
      std::shared_ptr<Node3d> next_node = Next_node_generator(current_node, i);
664 665 666 667
      // boundary check failure handle
      if (next_node == nullptr) {
        continue;
      }
668 669 670 671
      // check if the node is already in the close set
      if (close_set_.find(next_node->GetIndex()) != close_set_.end()) {
        continue;
      }
J
JasonZhou404 已提交
672 673 674 675
      // collision check
      if (!ValidityCheck(next_node)) {
        continue;
      }
676
      if (open_set_.find(next_node->GetIndex()) == open_set_.end()) {
677
        explored_node_num++;
678
        start_time = Clock::NowInSeconds();
J
JasonZhou404 已提交
679
        CalculateNodeCost(current_node, next_node);
680 681
        end_time = Clock::NowInSeconds();
        heuristic_time += end_time - start_time;
682 683
        open_set_.emplace(next_node->GetIndex(), next_node);
        open_pq_.emplace(next_node->GetIndex(), next_node->GetCost());
J
JasonZhou404 已提交
684
      }
685
    }
J
JasonZhou404 已提交
686
  }
687
  if (final_node_ == nullptr) {
688
    ADEBUG << "Hybrid A searching return null ptr(open_set ran out)";
689 690
    return false;
  }
691
  if (!GetResult(result)) {
692
    ADEBUG << "GetResult failed";
693 694
    return false;
  }
Q
Qi Luo 已提交
695 696
  ADEBUG << "explored node num is " << explored_node_num;
  ADEBUG << "heuristic time is " << heuristic_time;
697 698 699
  ADEBUG << "reed shepp time is " << rs_time;
  ADEBUG << "hybrid astar total time is "
         << Clock::NowInSeconds() - astar_start_time;
700
  return true;
701
}
702
}  // namespace planning
703
}  // namespace apollo