qp_frenet_frame.cc 18.5 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;
A
Aaron Xiao 已提交
88
  for (double s = start_s_; s <= end_s_; s += resolution) {
89
    evaluated_knots_.push_back(s);
90 91 92
    hdmap_bound_.emplace_back(-inf, inf);
    static_obstacle_bound_.emplace_back(-inf, inf);
    dynamic_obstacle_bound_.emplace_back(-inf, inf);
93 94 95
  }

  // initialize calculation here
96 97
  CalculateHDMapBound();

98 99
  if (!CalculateObstacleBound()) {
    AERROR << "Calculate obstacle bound failed!";
D
Dong Li 已提交
100 101
    return false;
  }
102
  for (size_t i = 0; i < evaluated_knots_.size(); ++i) {
J
jiangyifei 已提交
103 104 105
    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);
106 107 108 109 110 111 112 113
    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);
J
jiangyifei 已提交
114
  }
D
Dong Li 已提交
115
  return true;
116 117
}

118 119
const ReferenceLine& QpFrenetFrame::GetReferenceLine() const {
  return reference_line_;
120 121 122
}

double QpFrenetFrame::feasible_longitudinal_upper_bound() const {
123
  return feasible_longitudinal_upper_bound_;
124 125
}

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

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

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

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

D
Dong Li 已提交
155 156
bool QpFrenetFrame::MapDynamicObstacleWithDecision(
    const PathObstacle& path_obstacle) {
157
  const Obstacle* ptr_obstacle = path_obstacle.obstacle();
158
  if (!path_obstacle.HasLateralDecision()) {
159
    ADEBUG << "object has no lateral decision";
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    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;

183
    for (const auto& corner_xy : corners) {
184
      common::SLPoint cur_point;
185
      if (!reference_line_.XYToSL(corner_xy, &cur_point)) {
186
        AERROR << "Fail to map xy point " << corner_xy.DebugString() << " to "
187 188 189 190 191 192
               << 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));
193
    }
194 195 196 197 198 199

    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);
200 201
      }

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
      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);
226 227 228 229 230 231
      }
    }
  }
  return true;
}

D
Dong Li 已提交
232 233
bool QpFrenetFrame::MapStaticObstacleWithDecision(
    const PathObstacle& path_obstacle) {
234
  const auto ptr_obstacle = path_obstacle.obstacle();
235
  if (!path_obstacle.HasLateralDecision()) {
236
    ADEBUG << "obstacle has no lateral decision";
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
    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;
254
    }
255 256 257 258 259 260 261 262
  } 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;
263 264 265 266 267
    }
  }
  return true;
}

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

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

286
  for (uint32_t i = 0; i < sl_corners.size(); ++i) {
287 288 289 290
    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 已提交
291 292 293
             << sl_corners[i + 1].DebugString();
      return false;
    }
294
  }
D
Dong Li 已提交
295
  return true;
296 297
}

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

307 308 309 310
  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 已提交
311
    return true;
312 313 314 315 316
  }

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

317
  for (uint32_t i = impact_index.first; i <= impact_index.second; ++i) {
318
    double weight = std::abs((evaluated_knots_[i] - near_point.s())) / distance;
319 320 321 322 323 324
    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) {
325
      boundary += vehicle_param_.width() / 2;
326 327
      (*constraint)[i].first = std::max(boundary, (*constraint)[i].first);
    } else if (nudge_side > 0) {
328
      boundary -= vehicle_param_.width() / 2;
329 330 331 332 333
      (*constraint)[i].second = std::min(boundary, (*constraint)[i].second);
    }

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

D
Dong Li 已提交
342 343 344 345 346
      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];
347 348 349
    }
  }

D
Dong Li 已提交
350
  return true;
351 352
}

353
std::pair<double, double> QpFrenetFrame::MapLateralConstraint(
D
Dong Li 已提交
354 355
    const common::SLPoint& start, const common::SLPoint& end,
    const int nudge_side, const double s_start, const double s_end) {
356 357 358 359 360
  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 已提交
361 362 363
  }
  double s_front = std::max(start.s(), s_start);
  double s_back = std::min(end.s(), s_end);
364

D
Dong Li 已提交
365 366
  double weight_back = 0.0;
  double weight_front = 0.0;
367

368
  if (Double::Compare((end.s() - start.s()), 0.0) > 0) {
D
Dong Li 已提交
369 370 371
    weight_back = (s_back - end.s()) / (end.s() - start.s());
    weight_front = (s_front - start.s()) / (end.s() - start.s());
  }
372

D
Dong Li 已提交
373 374
  common::SLPoint front = util::interpolate(start, end, weight_front);
  common::SLPoint back = util::interpolate(start, end, weight_back);
375

D
Dong Li 已提交
376 377 378 379 380 381
  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;
382
  }
D
Dong Li 已提交
383
  return result;
384 385
}

386
std::pair<uint32_t, uint32_t> QpFrenetFrame::FindInterval(
387
    const double start, const double end) const {
388 389 390 391
  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());
392 393
  uint32_t start_index = FindIndex(new_start);
  uint32_t end_index = FindIndex(new_end);
394

395 396
  if (end > evaluated_knots_[end_index] &&
      end_index + 1 != evaluated_knots_.size()) {
397 398 399 400 401 402
    end_index++;
  }

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

403
void QpFrenetFrame::CalculateHDMapBound() {
404
  for (uint32_t i = 0; i < hdmap_bound_.size(); ++i) {
405 406
    double left_bound = 0.0;
    double right_bound = 0.0;
407 408
    bool suc = reference_line_.GetLaneWidth(evaluated_knots_[i], &left_bound,
                                            &right_bound);
409
    if (!suc) {
410
      AWARN << "Extracting lane width failed at s = " << evaluated_knots_[i];
411
      right_bound = FLAGS_default_reference_line_width / 2;
412
      left_bound = FLAGS_default_reference_line_width / 2;
413
    }
414

415 416 417 418 419
    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 ("
420 421
             << hdmap_bound_[i].first << ", " << hdmap_bound_[i].second << ") "
             << std::endl;
422 423 424
      AERROR << "vehicle_param_.width = " << vehicle_param_.width();
      AERROR << "left_bound: " << left_bound;
      AERROR << "right_bound: " << right_bound;
425
      feasible_longitudinal_upper_bound_ =
426
          std::min(evaluated_knots_[i], feasible_longitudinal_upper_bound_);
427 428 429 430 431 432
      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();
433
    }
434
  }
435 436
}

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

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

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

478 479 480 481
  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;
482

483 484
  // If there is only one infinity in low and high point, then make it equal
  // to
485 486 487 488 489 490 491 492 493 494 495 496 497
  // 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;
  }

498 499 500 501 502
  if (std::isinf(low_first)) {
    bound->first = low_first;
  } else {
    bound->first = low_first * (1 - weight) + high_first * weight;
  }
503

504
  if (std::isinf(low_second)) {
505 506
    bound->second = low_second;
  }else {
507 508
    bound->second = low_second * (1 - weight) + high_second * weight;
  }
509 510 511
  return true;
}

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

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