qp_frenet_frame.cc 18.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/******************************************************************************
 * Copyright 2017 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.
 *****************************************************************************/

/**
Z
Zhang Liangliang 已提交
18
 * @file qp_frenet_frame.cc
19
 **/
D
Dong Li 已提交
20
#include "modules/planning/tasks/qp_spline_path/qp_frenet_frame.h"
D
Dong Li 已提交
21

22 23
#include <algorithm>
#include <limits>
D
Dong Li 已提交
24

Z
Zhang Liangliang 已提交
25 26 27
#include "modules/common/proto/pnc_point.pb.h"
#include "modules/planning/proto/planning.pb.h"

28
#include "modules/common/configs/vehicle_config_helper.h"
D
Dong Li 已提交
29 30
#include "modules/common/macro.h"
#include "modules/common/util/util.h"
31
#include "modules/planning/common/planning_gflags.h"
32
#include "modules/planning/common/planning_util.h"
D
Dong Li 已提交
33
#include "modules/planning/math/double.h"
34 35 36 37

namespace apollo {
namespace planning {

38
using common::SpeedPoint;
39
using ConstPathObstacleList = std::vector<const PathObstacle*>;
40

41 42
namespace {

A
Aaron Xiao 已提交
43
constexpr double kEpsilontol = 1e-6;
44
}
A
Aaron Xiao 已提交
45

46
QpFrenetFrame::QpFrenetFrame(const ReferenceLine& reference_line,
D
Dong Li 已提交
47
                             const ConstPathObstacleList& path_obstacles,
48 49 50 51 52
                             const SpeedData& speed_data,
                             const common::FrenetFramePoint& init_frenet_point,
                             const double start_s, const double end_s,
                             const double time_resolution)
    : reference_line_(reference_line),
D
Dong Li 已提交
53
      path_obstacles_(path_obstacles),
54 55 56 57
      speed_data_(speed_data),
      vehicle_param_(
          common::VehicleConfigHelper::instance()->GetConfig().vehicle_param()),
      init_frenet_point_(init_frenet_point),
58
      feasible_longitudinal_upper_bound_(reference_line_.map_path().length()),
59 60 61 62
      start_s_(start_s),
      end_s_(end_s),
      time_resolution_(time_resolution) {}

J
jiangyifei 已提交
63 64
bool QpFrenetFrame::Init(const uint32_t num_points,
                         apollo::planning_internal::Debug* planning_debug) {
65
  if (num_points < 2) {
66 67
    AERROR << "Number of s points [" << num_points
           << "] is too small to evaluate.";
D
Dong Li 已提交
68 69
    return false;
  }
70

J
jiangyifei 已提交
71 72 73
  apollo::planning_internal::SLFrameDebug* sl_frame =
      planning_debug->mutable_planning_data()->mutable_sl_frame()->Add();

74 75
  constexpr double kMinDistance = 1.0e-8;
  if (std::fabs(start_s_ - end_s_) < kMinDistance) {
76
    AERROR << "Not enough s distance. start_s: " << start_s_
77
           << ", end_s: " << end_s_ << ", kMinDistance: " << kMinDistance;
D
Dong Li 已提交
78 79
    return false;
  }
80

81
  if (!CalculateDiscretizedVehicleLocation()) {
D
Dong Li 已提交
82 83 84
    AERROR << "Fail to calculate discretized vehicle location!";
    return false;
  }
85

86
  const auto inf = std::numeric_limits<double>::infinity();
87
  const double resolution = (end_s_ - start_s_) / num_points;
88
  double s = start_s_;
89
  while (s <= end_s_) {
90
    evaluated_knots_.push_back(s);
91 92 93
    hdmap_bound_.emplace_back(-inf, inf);
    static_obstacle_bound_.emplace_back(-inf, inf);
    dynamic_obstacle_bound_.emplace_back(-inf, inf);
94
    s += resolution;
95 96 97
  }

  // initialize calculation here
98 99
  CalculateHDMapBound();

J
jiangyifei 已提交
100

101 102
  if (!CalculateObstacleBound()) {
    AERROR << "Calculate obstacle bound failed!";
D
Dong Li 已提交
103 104
    return false;
  }
J
jiangyifei 已提交
105 106 107 108 109 110 111 112 113 114 115 116 117
  for (int i = 0; i < evaluated_knots_.size(); i++) {
    sl_frame->mutable_sampled_s()->Add(evaluated_knots_[i]);
    sl_frame->mutable_map_lower_bound()->Add(hdmap_bound_[i].first);
    sl_frame->mutable_map_upper_bound()->Add(hdmap_bound_[i].second);
    sl_frame->mutable_static_obstacle_lower_bound()
        ->Add(static_obstacle_bound_[i].first);
    sl_frame->mutable_static_obstacle_upper_bound()
        ->Add(static_obstacle_bound_[i].second);
    sl_frame->mutable_dynamic_obstacle_lower_bound()
        ->Add(dynamic_obstacle_bound_[i].first);
    sl_frame->mutable_dynamic_obstacle_upper_bound()
        ->Add(dynamic_obstacle_bound_[i].second);
  }
D
Dong Li 已提交
118
  return true;
119 120
}

121 122
const ReferenceLine& QpFrenetFrame::GetReferenceLine() const {
  return reference_line_;
123 124 125
}

double QpFrenetFrame::feasible_longitudinal_upper_bound() const {
126
  return feasible_longitudinal_upper_bound_;
127 128
}

129 130
bool QpFrenetFrame::GetMapBound(const double s,
                                std::pair<double, double>* const bound) const {
131
  return GetBound(s, hdmap_bound_, bound);
132 133
}

134
bool QpFrenetFrame::GetStaticObstacleBound(
135
    const double s, std::pair<double, double>* const bound) const {
136
  return GetBound(s, static_obstacle_bound_, bound);
137 138
}

139
bool QpFrenetFrame::GetDynamicObstacleBound(
140
    const double s, std::pair<double, double>* const bound) const {
141
  return GetBound(s, dynamic_obstacle_bound_, bound);
142 143
}

144
bool QpFrenetFrame::CalculateDiscretizedVehicleLocation() {
145 146
  double relative_time = 0.0;
  while (relative_time < speed_data_.TotalTime()) {
147
    SpeedPoint veh_point;
148
    if (!speed_data_.EvaluateByTime(relative_time, &veh_point)) {
D
Dong Li 已提交
149 150 151
      AERROR << "Fail to get speed point at relative time " << relative_time;
      return false;
    }
152
    veh_point.set_t(relative_time);
153
    discretized_vehicle_location_.push_back(std::move(veh_point));
154
    relative_time += time_resolution_;
155 156 157 158
  }
  return true;
}

D
Dong Li 已提交
159 160
bool QpFrenetFrame::MapDynamicObstacleWithDecision(
    const PathObstacle& path_obstacle) {
161
  const Obstacle* ptr_obstacle = path_obstacle.Obstacle();
162
  if (!path_obstacle.HasLateralDecision()) {
163
    ADEBUG << "object has no lateral decision";
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
    return false;
  }
  const auto& decision = path_obstacle.LateralDecision();
  if (!decision.has_nudge()) {
    AWARN << "only support nudge now";
    return true;
  }
  const auto& nudge = decision.nudge();
  double buffer = std::fabs(nudge.distance_l());

  int nudge_side = (nudge.type() == ObjectNudge::RIGHT_NUDGE) ? 1 : -1;

  for (const SpeedPoint& veh_point : discretized_vehicle_location_) {
    double time = veh_point.t();
    common::TrajectoryPoint trajectory_point =
        ptr_obstacle->GetPointAtTime(time);
    common::math::Box2d obs_box =
        ptr_obstacle->GetBoundingBox(trajectory_point);
    // project obs_box on reference line
    std::vector<common::math::Vec2d> corners;
    obs_box.GetAllCorners(&corners);
    std::vector<common::SLPoint> sl_corners;

187
    for (const auto& corner_xy : corners) {
188
      common::SLPoint cur_point;
189
      if (!reference_line_.XYToSL(corner_xy, &cur_point)) {
190
        AERROR << "Fail to map xy point " << corner_xy.DebugString() << " to "
191 192 193 194 195 196
               << cur_point.ShortDebugString();
        return false;
      }
      // shift box base on buffer
      cur_point.set_l(cur_point.l() - buffer * nudge_side);
      sl_corners.push_back(std::move(cur_point));
197
    }
198 199 200 201 202 203

    for (uint32_t i = 0; i < sl_corners.size(); ++i) {
      common::SLPoint sl_first = sl_corners[i % sl_corners.size()];
      common::SLPoint sl_second = sl_corners[(i + 1) % sl_corners.size()];
      if (sl_first.s() < sl_second.s()) {
        std::swap(sl_first, sl_second);
204 205
      }

206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
      std::pair<double, double> bound = MapLateralConstraint(
          sl_first, sl_second, nudge_side,
          veh_point.s() - vehicle_param_.back_edge_to_center(),
          veh_point.s() + vehicle_param_.front_edge_to_center());

      // update bound map
      double s_resolution = std::fabs(veh_point.v() * time_resolution_);
      double updated_start_s =
          init_frenet_point_.s() + veh_point.s() - s_resolution;
      double updated_end_s =
          init_frenet_point_.s() + veh_point.s() + s_resolution;
      if (updated_end_s > evaluated_knots_.back() ||
          updated_start_s < evaluated_knots_.front()) {
        continue;
      }
      std::pair<uint32_t, uint32_t> update_index_range =
          FindInterval(updated_start_s, updated_end_s);

      for (uint32_t j = update_index_range.first; j < update_index_range.second;
           ++j) {
        dynamic_obstacle_bound_[j].first =
            std::max(bound.first, dynamic_obstacle_bound_[j].first);
        dynamic_obstacle_bound_[j].second =
            std::min(bound.second, dynamic_obstacle_bound_[j].second);
230 231 232 233 234 235
      }
    }
  }
  return true;
}

D
Dong Li 已提交
236 237
bool QpFrenetFrame::MapStaticObstacleWithDecision(
    const PathObstacle& path_obstacle) {
Y
Yajia Zhang 已提交
238
  const auto ptr_obstacle = path_obstacle.Obstacle();
239
  if (!path_obstacle.HasLateralDecision()) {
240
    ADEBUG << "obstacle has no lateral decision";
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
    return false;
  }
  const auto& decision = path_obstacle.LateralDecision();
  if (!decision.has_nudge()) {
    AWARN << "only support nudge decision now";
    return true;
  }
  const auto& nudge = decision.nudge();
  const double buffer = std::fabs(nudge.distance_l());
  if (nudge.type() == ObjectNudge::RIGHT_NUDGE) {
    const auto& box = ptr_obstacle->PerceptionBoundingBox();
    std::vector<common::math::Vec2d> corners;
    box.GetAllCorners(&corners);
    if (!MapPolygon(corners, buffer, -1, &static_obstacle_bound_)) {
      AERROR << "fail to map polygon with id " << path_obstacle.Id()
             << " in qp frenet frame";
      return false;
258
    }
259 260 261 262 263 264 265 266
  } else {
    const auto& box = ptr_obstacle->PerceptionBoundingBox();
    std::vector<common::math::Vec2d> corners;
    box.GetAllCorners(&corners);
    if (!MapPolygon(corners, buffer, 1, &static_obstacle_bound_)) {
      AERROR << "fail to map polygon with id " << path_obstacle.Id()
             << "in qp frenet frame";
      return false;
267 268 269 270 271
    }
  }
  return true;
}

272
bool QpFrenetFrame::MapPolygon(
273
    const std::vector<common::math::Vec2d>& corners, const double buffer,
Y
Yajia Zhang 已提交
274
    const int nudge_side,
275
    std::vector<std::pair<double, double>>* const bound_map) {
D
Dong Li 已提交
276
  std::vector<common::SLPoint> sl_corners;
277

278
  for (const auto& corner_xy : corners) {
D
Dong Li 已提交
279
    common::SLPoint cur_point;
280
    if (!reference_line_.XYToSL(corner_xy, &cur_point)) {
281
      AERROR << "Fail to map xy point " << corner_xy.DebugString() << " to "
D
Dong Li 已提交
282 283 284
             << cur_point.DebugString();
      return false;
    }
285
    // shift box base on buffer
286
    cur_point.set_l(cur_point.l() + buffer * nudge_side);
287 288 289
    sl_corners.push_back(std::move(cur_point));
  }

290
  for (uint32_t i = 0; i < sl_corners.size(); ++i) {
291 292 293 294
    if (!MapLine(sl_corners[i % sl_corners.size()],
                 sl_corners[(i + 1) % sl_corners.size()], nudge_side,
                 bound_map)) {
      AERROR << "Map box line (sl) " << sl_corners[i].DebugString() << "->"
D
Dong Li 已提交
295 296 297
             << sl_corners[i + 1].DebugString();
      return false;
    }
298
  }
D
Dong Li 已提交
299
  return true;
300 301
}

302
bool QpFrenetFrame::MapLine(
D
Dong Li 已提交
303 304
    const common::SLPoint& start, const common::SLPoint& end,
    const int nudge_side,
305
    std::vector<std::pair<double, double>>* const constraint) {
D
Dong Li 已提交
306 307
  common::SLPoint near_point = (start.s() < end.s() ? start : end);
  common::SLPoint further_point = (start.s() < end.s() ? end : start);
308
  std::pair<uint32_t, uint32_t> impact_index =
309
      FindInterval(near_point.s(), further_point.s());
310

311 312 313 314
  if ((near_point.s() < start_s_ - vehicle_param_.back_edge_to_center() &&
       further_point.s() < start_s_ - vehicle_param_.back_edge_to_center()) ||
      (near_point.s() > end_s_ + vehicle_param_.front_edge_to_center() &&
       further_point.s() > end_s_ + vehicle_param_.front_edge_to_center())) {
D
Dong Li 已提交
315
    return true;
316 317 318 319 320
  }

  double distance =
      std::max(std::abs(near_point.s() - further_point.s()), 1e-8);

321
  for (uint32_t i = impact_index.first; i <= impact_index.second; ++i) {
322
    double weight = std::abs((evaluated_knots_[i] - near_point.s())) / distance;
323 324 325 326 327 328
    weight = std::max(weight, 0.0);
    weight = std::min(weight, 1.0);
    double boundary =
        near_point.l() * (1 - weight) + further_point.l() * weight;

    if (nudge_side < 0) {
329
      boundary += vehicle_param_.width() / 2;
330 331
      (*constraint)[i].first = std::max(boundary, (*constraint)[i].first);
    } else if (nudge_side > 0) {
332
      boundary -= vehicle_param_.width() / 2;
333 334 335 336 337
      (*constraint)[i].second = std::min(boundary, (*constraint)[i].second);
    }

    if ((*constraint)[i].second < (*constraint)[i].first + 0.3) {
      if (i > 0) {
338 339 340
        feasible_longitudinal_upper_bound_ =
            std::min(evaluated_knots_[i - 1] - kEpsilontol,
                     feasible_longitudinal_upper_bound_);
341
      } else {
342
        feasible_longitudinal_upper_bound_ = start_s_;
D
Dong Li 已提交
343
        return true;
344 345
      }

D
Dong Li 已提交
346 347 348 349 350
      AERROR << "current mapping constraint, sl point impact index "
             << "near_point: " << near_point.DebugString()
             << "further_point: " << further_point.DebugString()
             << "impact_index: " << impact_index << "(*constraint)[" << i << "]"
             << (*constraint)[i];
351 352 353
    }
  }

D
Dong Li 已提交
354
  return true;
355 356
}

357
std::pair<double, double> QpFrenetFrame::MapLateralConstraint(
D
Dong Li 已提交
358 359
    const common::SLPoint& start, const common::SLPoint& end,
    const int nudge_side, const double s_start, const double s_end) {
360 361 362 363 364
  double inf = std::numeric_limits<double>::infinity();
  std::pair<double, double> result = std::make_pair(-inf, inf);

  if (start.s() > s_end || end.s() < s_start) {
    return result;
D
Dong Li 已提交
365 366 367
  }
  double s_front = std::max(start.s(), s_start);
  double s_back = std::min(end.s(), s_end);
368

D
Dong Li 已提交
369 370
  double weight_back = 0.0;
  double weight_front = 0.0;
371

372
  if (Double::Compare((end.s() - start.s()), 0.0) > 0) {
D
Dong Li 已提交
373 374 375
    weight_back = (s_back - end.s()) / (end.s() - start.s());
    weight_front = (s_front - start.s()) / (end.s() - start.s());
  }
376

D
Dong Li 已提交
377 378
  common::SLPoint front = util::interpolate(start, end, weight_front);
  common::SLPoint back = util::interpolate(start, end, weight_back);
379

D
Dong Li 已提交
380 381 382 383 384 385
  if (nudge_side > 0) {
    double l = std::min(front.l(), back.l());
    result.second = l;
  } else if (nudge_side < 0) {
    double l = std::max(front.l(), back.l());
    result.first = l;
386
  }
D
Dong Li 已提交
387
  return result;
388 389
}

390
std::pair<uint32_t, uint32_t> QpFrenetFrame::FindInterval(
391
    const double start, const double end) const {
392 393 394 395
  double new_start = std::max(start - vehicle_param_.front_edge_to_center(),
                              evaluated_knots_.front());
  double new_end = std::min(end + vehicle_param_.back_edge_to_center(),
                            evaluated_knots_.back());
396 397
  uint32_t start_index = FindIndex(new_start);
  uint32_t end_index = FindIndex(new_end);
398

399 400
  if (end > evaluated_knots_[end_index] &&
      end_index + 1 != evaluated_knots_.size()) {
401 402 403 404 405 406
    end_index++;
  }

  return std::make_pair(start_index, end_index);
}

407
void QpFrenetFrame::CalculateHDMapBound() {
408
  for (uint32_t i = 0; i < hdmap_bound_.size(); ++i) {
409 410
    double left_bound = 0.0;
    double right_bound = 0.0;
A
Aaron Xiao 已提交
411 412
    bool suc = reference_line_.GetLaneWidth(evaluated_knots_[i],
                                            &left_bound, &right_bound);
413
    if (!suc) {
414
      AWARN << "Extracting lane width failed at s = " << evaluated_knots_[i];
415
      right_bound = FLAGS_default_reference_line_width / 2;
416
      left_bound = FLAGS_default_reference_line_width / 2;
417
    }
418

419 420 421 422 423
    hdmap_bound_[i].first = -right_bound + vehicle_param_.width() / 2;
    hdmap_bound_[i].second = left_bound - vehicle_param_.width() / 2;

    if (hdmap_bound_[i].first >= hdmap_bound_[i].second) {
      AERROR << "HD Map bound at " << evaluated_knots_[i] << " is infeasible ("
424 425
             << hdmap_bound_[i].first << ", " << hdmap_bound_[i].second << ") "
             << std::endl;
426 427 428
      AERROR << "vehicle_param_.width = " << vehicle_param_.width();
      AERROR << "left_bound: " << left_bound;
      AERROR << "right_bound: " << right_bound;
429
      feasible_longitudinal_upper_bound_ =
430
          std::min(evaluated_knots_[i], feasible_longitudinal_upper_bound_);
431 432 433 434 435 436
      common::SLPoint sl;
      sl.set_s(evaluated_knots_[i]);
      common::math::Vec2d xy;
      reference_line_.SLToXY(sl, &xy);
      AERROR << "evaluated_knot x: " << std::fixed << xy.x()
             << " y: " << xy.y();
437
    }
438
  }
439 440
}

441
bool QpFrenetFrame::CalculateObstacleBound() {
Y
Yajia Zhang 已提交
442
  for (const auto ptr_path_obstacle : path_obstacles_) {
443 444 445
    if (!ptr_path_obstacle->HasLateralDecision()) {
      continue;
    }
Y
Yajia Zhang 已提交
446 447 448
    if (ptr_path_obstacle->Obstacle()->IsStatic()) {
      if (!MapStaticObstacleWithDecision(*ptr_path_obstacle)) {
        AERROR << "mapping obstacle with id [" << ptr_path_obstacle->Id()
449 450 451 452
               << "] failed in qp frenet frame.";
        return false;
      }
    } else {
Y
Yajia Zhang 已提交
453 454
      if (!MapDynamicObstacleWithDecision(*ptr_path_obstacle)) {
        AERROR << "mapping obstacle with id [" << ptr_path_obstacle->Id()
455 456 457
               << "] failed in qp frenet frame.";
        return false;
      }
D
Dong Li 已提交
458
    }
459 460 461 462
  }
  return true;
}

463
bool QpFrenetFrame::GetBound(
464
    const double s, const std::vector<std::pair<double, double>>& map_bound,
465
    std::pair<double, double>* const bound) const {
466 467
  if (Double::Compare(s, start_s_, 1e-8) < 0 ||
      Double::Compare(s, end_s_, 1e-8) > 0) {
468
    AERROR << "Evaluate s location " << s
469 470
           << ", is out of trajectory frenet frame range (" << start_s_ << ", "
           << end_s_ << ")";
D
Dong Li 已提交
471 472
    return false;
  }
473 474

  // linear bound interpolation
475
  uint32_t lower_index = FindIndex(s);
476 477
  const double s_low = evaluated_knots_[lower_index];
  const double s_high = evaluated_knots_[lower_index + 1];
478
  double weight = Double::Compare(s_low, s_high, 1e-8) < 0
479 480
                      ? (s - s_low) / (s_high - s_low)
                      : 0.0;
481

482 483 484 485
  double low_first = map_bound[lower_index].first;
  double low_second = map_bound[lower_index].second;
  double high_first = map_bound[lower_index + 1].first;
  double high_second = map_bound[lower_index + 1].second;
486

487 488
  // If there is only one infinity in low and high point, then make it equal
  // to
489 490 491 492 493 494 495 496 497 498 499 500 501
  // the not inf one.
  if (std::isinf(low_first) && !std::isinf(high_first)) {
    low_first = high_first;
  } else if (!std::isinf(low_first) && std::isinf(high_first)) {
    high_first = low_first;
  }

  if (std::isinf(low_second) && !std::isinf(high_second)) {
    low_second = high_second;
  } else if (!std::isinf(low_second) && std::isinf(high_second)) {
    high_second = low_second;
  }

502 503 504 505 506
  if (std::isinf(low_first)) {
    bound->first = low_first;
  } else {
    bound->first = low_first * (1 - weight) + high_first * weight;
  }
507

508 509 510
  if (std::isinf(low_second)) {
    bound->second = low_second * (1 - weight) + high_second * weight;
  }
511 512 513
  return true;
}

514
uint32_t QpFrenetFrame::FindIndex(const double s) const {
515
  auto lower_bound =
516
      std::lower_bound(evaluated_knots_.begin() + 1, evaluated_knots_.end(), s);
517
  return std::min(
518
             static_cast<uint32_t>(evaluated_knots_.size() - 1),
519 520
             static_cast<uint32_t>(lower_bound - evaluated_knots_.begin())) -
         1;
521 522 523
}

}  // namespace planning
D
Dong Li 已提交
524
}  // namespace apollo