qp_spline_st_graph.cc 18.6 KB
Newer Older
Z
Zhang Liangliang 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/******************************************************************************
 * 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.
 *****************************************************************************/

/**
 * @file qp_spline_st_graph.cc
 **/

D
Dong Li 已提交
21
#include "modules/planning/tasks/qp_spline_st_speed/qp_spline_st_graph.h"
Z
Zhang Liangliang 已提交
22

A
Aaron Xiao 已提交
23
#include <algorithm>
24
#include <limits>
A
Aaron Xiao 已提交
25
#include <string>
A
Aaron Xiao 已提交
26 27
#include <utility>

Z
Zhang Liangliang 已提交
28
#include "modules/common/log.h"
29
#include "modules/planning/common/planning_gflags.h"
30

Z
Zhang Liangliang 已提交
31 32 33
namespace apollo {
namespace planning {

34 35
using apollo::common::ErrorCode;
using apollo::common::Status;
D
Dong Li 已提交
36
using apollo::common::VehicleParam;
A
Aaron Xiao 已提交
37
using apollo::planning_internal::STGraphDebug;
Z
Zhang Liangliang 已提交
38

39 40
QpSplineStGraph::QpSplineStGraph(
    const QpSplineStSpeedConfig& qp_spline_st_speed_config,
41
    const VehicleParam& veh_param)
42
    : qp_spline_st_speed_config_(qp_spline_st_speed_config),
43 44 45 46
      t_knots_resolution_(
          qp_spline_st_speed_config_.total_time() /
          qp_spline_st_speed_config_.number_of_discrete_graph_t()),
      t_evaluated_resolution_(
47
          qp_spline_st_speed_config_.total_time() /
48 49 50
          qp_spline_st_speed_config_.number_of_evaluated_graph_t()) {
  Init();
}
Z
Zhang Liangliang 已提交
51

52
void QpSplineStGraph::Init() {
53
  // init knots
54 55
  double curr_t = 0.0;
  for (uint32_t i = 0;
56
       i <= qp_spline_st_speed_config_.number_of_discrete_graph_t(); ++i) {
57
    t_knots_.push_back(curr_t);
58
    curr_t += t_knots_resolution_;
59
  }
60 61

  // init evaluated t positions
62
  curr_t = 0;
63
  for (uint32_t i = 0;
64
       i <= qp_spline_st_speed_config_.number_of_evaluated_graph_t(); ++i) {
65
    t_evaluated_.push_back(curr_t);
66
    curr_t += t_evaluated_resolution_;
67
  }
68 69
}

70
Status QpSplineStGraph::Search(const StGraphData& st_graph_data,
71
                               const PathData& path_data,
72
                               SpeedData* const speed_data,
73
                               const std::pair<double, double>& accel_bound,
A
Aaron Xiao 已提交
74
                               STGraphDebug* st_graph_debug) {
75 76 77 78 79 80 81
  cruise_.clear();

  // reset spline generator
  spline_generator_.reset(new Spline1dGenerator(
      t_knots_, qp_spline_st_speed_config_.spline_order()));

  // start to search for best st points
82
  init_point_ = st_graph_data.init_point();
Z
Zhang Liangliang 已提交
83
  if (st_graph_data.path_data_length() <
84 85
      qp_spline_st_speed_config_.total_path_length()) {
    qp_spline_st_speed_config_.set_total_path_length(
Z
Zhang Liangliang 已提交
86 87 88
        st_graph_data.path_data_length());
  }

89
  if (!ApplyConstraint(st_graph_data.init_point(), st_graph_data.speed_limit(),
90 91
                       st_graph_data.st_boundaries(), accel_bound,
                       st_graph_debug)
92
           .ok()) {
93 94
    const std::string msg = "Apply constraint failed!";
    AERROR << msg;
95
    return Status(ErrorCode::PLANNING_ERROR, msg);
Z
Zhang Liangliang 已提交
96 97
  }

98 99
  if (!ApplyKernel(st_graph_data.st_boundaries(), st_graph_data.speed_limit(),
                   st_graph_debug)
100
           .ok()) {
101 102
    const std::string msg = "Apply kernel failed!";
    AERROR << msg;
103
    return Status(ErrorCode::PLANNING_ERROR, msg);
Z
Zhang Liangliang 已提交
104 105
  }

106
  if (!Solve().ok()) {
107 108
    const std::string msg = "Solve qp problem failed!";
    AERROR << msg;
109
    return Status(ErrorCode::PLANNING_ERROR, msg);
Z
Zhang Liangliang 已提交
110 111 112
  }

  // extract output
Z
Zhang Liangliang 已提交
113
  speed_data->Clear();
114
  const Spline1d& spline = spline_generator_->spline();
Z
Zhang Liangliang 已提交
115

116 117
  double t_output_resolution =
      qp_spline_st_speed_config_.output_time_resolution();
Z
Zhang Liangliang 已提交
118
  double time = 0.0;
119
  while (time < qp_spline_st_speed_config_.total_time() + t_output_resolution) {
Z
Zhang Liangliang 已提交
120
    double s = spline(time);
121
    double v = spline.Derivative(time);
122 123
    double a = spline.SecondOrderDerivative(time);
    double da = spline.ThirdOrderDerivative(time);
124
    speed_data->AppendSpeedPoint(s, time, v, a, da);
125
    time += t_output_resolution;
Z
Zhang Liangliang 已提交
126 127
  }

128
  return Status::OK();
Z
Zhang Liangliang 已提交
129 130
}

131
Status QpSplineStGraph::ApplyConstraint(
132
    const common::TrajectoryPoint& init_point, const SpeedLimit& speed_limit,
133 134 135
    const std::vector<StBoundary>& boundaries,
    const std::pair<double, double>& accel_bound,
    STGraphDebug* st_graph_debug) {
Z
Zhang Liangliang 已提交
136
  Spline1dConstraint* constraint =
137
      spline_generator_->mutable_spline_constraint();
Z
Zhang Liangliang 已提交
138
  // position, velocity, acceleration
Z
Zhang Liangliang 已提交
139

140
  if (!constraint->AddPointConstraint(0.0, 0.0)) {
141 142
    const std::string msg = "add st start point constraint failed";
    AERROR << msg;
143
    return Status(ErrorCode::PLANNING_ERROR, msg);
Z
Zhang Liangliang 已提交
144
  }
145
  ADEBUG << "init point constraint:" << init_point.DebugString();
146
  if (!constraint->AddPointDerivativeConstraint(0.0, init_point_.v())) {
147 148
    const std::string msg = "add st start point velocity constraint failed!";
    AERROR << msg;
149
    return Status(ErrorCode::PLANNING_ERROR, msg);
Z
Zhang Liangliang 已提交
150 151
  }

152
  if (!constraint->AddPointSecondDerivativeConstraint(
153
          spline_generator_->spline().x_knots().back(), 0.0)) {
154 155
    const std::string msg = "add st end point acceleration constraint failed!";
    AERROR << msg;
156
    return Status(ErrorCode::PLANNING_ERROR, msg);
Z
Zhang Liangliang 已提交
157 158 159
  }

  // monotone constraint
160
  if (!constraint->AddMonotoneInequalityConstraintAtKnots()) {
161
    const std::string msg = "add monotone inequality constraint failed!";
162
    AERROR << msg;
163
    return Status(ErrorCode::PLANNING_ERROR, msg);
Z
Zhang Liangliang 已提交
164 165 166
  }

  // smoothness constraint
167
  if (!constraint->AddSecondDerivativeSmoothConstraint()) {
168 169
    const std::string msg = "add smoothness joint constraint failed!";
    AERROR << msg;
170
    return Status(ErrorCode::PLANNING_ERROR, msg);
Z
Zhang Liangliang 已提交
171 172 173 174 175
  }

  // boundary constraint
  std::vector<double> s_upper_bound;
  std::vector<double> s_lower_bound;
176

177
  for (const double curr_t : t_evaluated_) {
Z
Zhang Liangliang 已提交
178 179
    double lower_s = 0.0;
    double upper_s = 0.0;
180 181 182
    GetSConstraintByTime(boundaries, curr_t,
                         qp_spline_st_speed_config_.total_path_length(),
                         &upper_s, &lower_s);
Z
Zhang Liangliang 已提交
183 184
    s_upper_bound.push_back(upper_s);
    s_lower_bound.push_back(lower_s);
185
    ADEBUG << "Add constraint by time: " << curr_t << " upper_s: " << upper_s
186
           << " lower_s: " << lower_s;
Z
Zhang Liangliang 已提交
187
  }
188 189 190

  DCHECK_EQ(t_evaluated_.size(), s_lower_bound.size());
  DCHECK_EQ(t_evaluated_.size(), s_upper_bound.size());
191
  if (!constraint->AddBoundary(t_evaluated_, s_lower_bound, s_upper_bound)) {
192
    const std::string msg = "Fail to apply distance constraints.";
193
    AERROR << msg;
194
    return Status(ErrorCode::PLANNING_ERROR, msg);
Z
Zhang Liangliang 已提交
195
  }
196

197
  // speed constraint
198 199
  std::vector<double> speed_upper_bound;
  if (!EstimateSpeedUpperBound(init_point, speed_limit, &speed_upper_bound)
200
           .ok()) {
201
    std::string msg = "Fail to estimate speed upper constraints.";
202 203 204
    AERROR << msg;
    return Status(ErrorCode::PLANNING_ERROR, msg);
  }
205

206
  std::vector<double> speed_lower_bound(t_evaluated_.size(), 0.0);
207 208 209

  DCHECK_EQ(t_evaluated_.size(), speed_upper_bound.size());
  DCHECK_EQ(t_evaluated_.size(), speed_lower_bound.size());
210 211 212 213 214 215 216 217

  for (size_t i = 0; i < t_evaluated_.size(); ++i) {
    auto speed_constraint = st_graph_debug->mutable_speed_constraint()->Add();
    speed_constraint->add_t(t_evaluated_[i]);
    speed_constraint->add_lower_bound(speed_lower_bound[i]);
    speed_constraint->add_upper_bound(speed_upper_bound[i]);
  }

218
  if (!constraint->AddDerivativeBoundary(t_evaluated_, speed_lower_bound,
219
                                         speed_upper_bound)) {
220 221 222 223
    const std::string msg = "Fail to apply speed constraints.";
    AERROR << msg;
    return Status(ErrorCode::PLANNING_ERROR, msg);
  }
224
  for (size_t i = 0; i < t_evaluated_.size(); ++i) {
225
    ADEBUG << "t_evaluated_: " << t_evaluated_[i]
226
           << "; speed_lower_bound: " << speed_lower_bound[i]
227
           << "; speed_upper_bound: " << speed_upper_bound[i];
228
  }
229

230
  // acceleration constraint
231 232 233
  std::vector<double> accel_lower_bound(t_evaluated_.size(), accel_bound.first);
  std::vector<double> accel_upper_bound(t_evaluated_.size(),
                                        accel_bound.second);
234

235 236 237 238 239 240 241 242 243
  bool has_follow = false;
  double delta_s = 1.0;
  for (const auto& boundary : boundaries) {
    if (boundary.boundary_type() == StBoundary::BoundaryType::FOLLOW) {
      has_follow = true;
      delta_s = std::fmin(
          delta_s, boundary.min_s() - fabs(boundary.characteristic_length()));
    }
  }
244
  if (FLAGS_enable_follow_accel_constraint && has_follow && delta_s < 0.0) {
245 246 247
    accel_upper_bound.front() = 0.0;
  } else {
    constexpr double kInitPointAccelRelaxedSpeed = 1.0;
Z
Zhang Liangliang 已提交
248 249 250 251 252 253
    constexpr double kInitPointAccelRelaxedRange = 0.4;

    if (init_point_.v() < kInitPointAccelRelaxedSpeed &&
        init_point_.a() < 0.0) {
      accel_lower_bound.front() = init_point_.a() - kInitPointAccelRelaxedRange;
      accel_upper_bound.front() = init_point_.a() + kInitPointAccelRelaxedRange;
254 255 256 257 258 259 260
    } else {
      if (!constraint->AddPointSecondDerivativeConstraint(0.0,
                                                          init_point_.a())) {
        const std::string msg =
            "Fail to apply init point acceleration constraints.";
        return Status(ErrorCode::PLANNING_ERROR, msg);
      }
261
    }
262 263
  }

264 265 266 267 268 269 270
  DCHECK_EQ(t_evaluated_.size(), accel_lower_bound.size());
  DCHECK_EQ(t_evaluated_.size(), accel_upper_bound.size());
  if (!constraint->AddSecondDerivativeBoundary(t_evaluated_, accel_lower_bound,
                                               accel_upper_bound)) {
    const std::string msg = "Fail to apply acceleration constraints.";
    return Status(ErrorCode::PLANNING_ERROR, msg);
  }
271 272 273 274 275
  for (size_t i = 0; i < t_evaluated_.size(); ++i) {
    ADEBUG << "t_evaluated_: " << t_evaluated_[i]
           << "; accel_lower_bound: " << accel_lower_bound[i]
           << "; accel_upper_bound: " << accel_upper_bound[i];
  }
276

277
  return Status::OK();
Z
Zhang Liangliang 已提交
278 279
}

280
Status QpSplineStGraph::ApplyKernel(const std::vector<StBoundary>& boundaries,
281 282
                                    const SpeedLimit& speed_limit,
                                    STGraphDebug* st_graph_debug) {
283
  Spline1dKernel* spline_kernel = spline_generator_->mutable_spline_kernel();
Z
Zhang Liangliang 已提交
284

285
  if (qp_spline_st_speed_config_.speed_kernel_weight() > 0) {
286
    spline_kernel->AddDerivativeKernelMatrix(
287
        qp_spline_st_speed_config_.speed_kernel_weight());
Z
Zhang Liangliang 已提交
288 289
  }

290
  if (qp_spline_st_speed_config_.accel_kernel_weight() > 0) {
291
    spline_kernel->AddSecondOrderDerivativeMatrix(
292
        qp_spline_st_speed_config_.accel_kernel_weight());
Z
Zhang Liangliang 已提交
293 294
  }

295
  if (qp_spline_st_speed_config_.jerk_kernel_weight() > 0) {
296
    spline_kernel->AddThirdOrderDerivativeMatrix(
297
        qp_spline_st_speed_config_.jerk_kernel_weight());
Z
Zhang Liangliang 已提交
298 299
  }

300 301 302
  if (AddCruiseReferenceLineKernel(speed_limit,
                                   qp_spline_st_speed_config_.cruise_weight(),
                                   st_graph_debug) != Status::OK()) {
303 304 305
    return Status(ErrorCode::PLANNING_ERROR, "QpSplineStGraph::ApplyKernel");
  }

306 307 308
  if (AddFollowReferenceLineKernel(boundaries,
                                   qp_spline_st_speed_config_.follow_weight(),
                                   st_graph_debug) != Status::OK()) {
309
    return Status(ErrorCode::PLANNING_ERROR, "QpSplineStGraph::ApplyKernel");
310
  }
311 312 313 314
  return Status::OK();
}

Status QpSplineStGraph::Solve() {
315
  return spline_generator_->Solve()
316 317 318
             ? Status::OK()
             : Status(ErrorCode::PLANNING_ERROR, "QpSplineStGraph::solve");
}
319

320
Status QpSplineStGraph::AddCruiseReferenceLineKernel(
321 322
    const SpeedLimit& speed_limit, const double weight,
    STGraphDebug* st_graph_debug) {
Z
Zhang Liangliang 已提交
323
  auto* spline_kernel = spline_generator_->mutable_spline_kernel();
324
  if (speed_limit.speed_limit_points().size() == 0) {
325 326 327
    std::string msg = "Fail to apply_kernel due to empty speed limits.";
    AERROR << msg;
    return Status(ErrorCode::PLANNING_ERROR, msg);
Z
Zhang Liangliang 已提交
328
  }
329
  double dist_ref = 0.0;
330
  auto kernel_cruise_ref = st_graph_debug->mutable_kernel_cruise_ref();
331
  cruise_.push_back(dist_ref);
332 333
  kernel_cruise_ref->mutable_t()->Add(t_evaluated_[0]);
  kernel_cruise_ref->mutable_cruise_line_s()->Add(dist_ref);
334 335
  for (uint32_t i = 1; i < t_evaluated_.size(); ++i) {
    dist_ref += (t_evaluated_[i] - t_evaluated_[i - 1]) *
336
                speed_limit.GetSpeedLimitByS(dist_ref);
337
    cruise_.push_back(dist_ref);
338 339
    kernel_cruise_ref->mutable_t()->Add(t_evaluated_[i]);
    kernel_cruise_ref->mutable_cruise_line_s()->Add(dist_ref);
340
  }
341
  DCHECK_EQ(t_evaluated_.size(), cruise_.size());
342

343
  for (std::size_t i = 0; i < t_evaluated_.size(); ++i) {
344
    ADEBUG << "Cruise Ref S: " << cruise_[i]
345
           << " Relative time: " << t_evaluated_[i] << std::endl;
346 347
  }

348
  if (t_evaluated_.size() > 0) {
349
    spline_kernel->AddReferenceLineKernelMatrix(
350 351
        t_evaluated_, cruise_,
        weight * qp_spline_st_speed_config_.total_time() / t_evaluated_.size());
352 353
  }
  spline_kernel->AddRegularization(0.01);
Z
Zhang Liangliang 已提交
354
  return Status::OK();
Z
Zhang Liangliang 已提交
355 356
}

357
Status QpSplineStGraph::AddFollowReferenceLineKernel(
358 359
    const std::vector<StBoundary>& boundaries, const double weight,
    STGraphDebug* st_graph_debug) {
360 361
  auto* spline_kernel = spline_generator_->mutable_spline_kernel();
  std::vector<double> ref_s;
362
  std::vector<double> filtered_evaluate_t;
363
  auto kernel_follow_ref = st_graph_debug->mutable_kernel_follow_ref();
364 365
  for (size_t i = 0; i < t_evaluated_.size(); ++i) {
    double curr_t = t_evaluated_[i];
366
    double s_min = std::numeric_limits<double>::infinity();
367 368
    bool success = false;
    for (const auto& boundary : boundaries) {
369
      if (boundary.boundary_type() != StBoundary::BoundaryType::FOLLOW) {
370 371
        continue;
      }
372 373 374
      if (curr_t < boundary.min_t() || curr_t > boundary.max_t()) {
        continue;
      }
375 376 377
      double s_upper = 0.0;
      double s_lower = 0.0;
      if (boundary.GetUnblockSRange(curr_t, &s_upper, &s_lower)) {
378
        success = true;
379 380 381
        s_min = std::min(s_min,
                         s_upper - boundary.characteristic_length() -
                             qp_spline_st_speed_config_.follow_drag_distance());
382 383
      }
    }
384
    if (success && s_min < cruise_[i]) {
385
      filtered_evaluate_t.push_back(curr_t);
386
      ref_s.push_back(s_min);
387 388
      kernel_follow_ref->mutable_t()->Add(curr_t);
      kernel_follow_ref->mutable_follow_line_s()->Add(s_min);
389 390
    }
  }
391
  DCHECK_EQ(filtered_evaluate_t.size(), ref_s.size());
392

393
  if (!ref_s.empty()) {
394
    spline_kernel->AddReferenceLineKernelMatrix(
395
        filtered_evaluate_t, ref_s,
396
        weight * qp_spline_st_speed_config_.total_time() / t_evaluated_.size());
397 398 399 400
  }

  for (std::size_t i = 0; i < filtered_evaluate_t.size(); ++i) {
    ADEBUG << "Follow Ref S: " << ref_s[i]
401
           << " Relative time: " << filtered_evaluate_t[i] << std::endl;
402
  }
403 404 405
  return Status::OK();
}

406
Status QpSplineStGraph::GetSConstraintByTime(
407
    const std::vector<StBoundary>& boundaries, const double time,
Z
Zhang Liangliang 已提交
408 409
    const double total_path_s, double* const s_upper_bound,
    double* const s_lower_bound) const {
410
  *s_upper_bound = total_path_s;
Z
Zhang Liangliang 已提交
411

412
  for (const StBoundary& boundary : boundaries) {
Z
Zhang Liangliang 已提交
413 414 415
    double s_upper = 0.0;
    double s_lower = 0.0;

416
    if (!boundary.GetUnblockSRange(time, &s_upper, &s_lower)) {
Z
Zhang Liangliang 已提交
417 418 419
      continue;
    }

420 421 422
    if (boundary.boundary_type() == StBoundary::BoundaryType::STOP ||
        boundary.boundary_type() == StBoundary::BoundaryType::FOLLOW ||
        boundary.boundary_type() == StBoundary::BoundaryType::YIELD) {
Z
Zhang Liangliang 已提交
423 424
      *s_upper_bound = std::fmin(*s_upper_bound, s_upper);
    } else {
425
      DCHECK(boundary.boundary_type() == StBoundary::BoundaryType::OVERTAKE);
Z
Zhang Liangliang 已提交
426 427 428 429
      *s_lower_bound = std::fmax(*s_lower_bound, s_lower);
    }
  }

430
  return Status::OK();
Z
Zhang Liangliang 已提交
431
}
432

433
Status QpSplineStGraph::EstimateSpeedUpperBound(
434
    const common::TrajectoryPoint& init_point, const SpeedLimit& speed_limit,
435 436
    std::vector<double>* speed_upper_bound) const {
  DCHECK_NOTNULL(speed_upper_bound);
437

438
  speed_upper_bound->clear();
439 440 441 442

  // use v to estimate position: not accurate, but feasible in cyclic
  // processing. We can do the following process multiple times and use
  // previous cycle's results for better estimation.
443
  const double v = init_point.v();
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

  if (static_cast<double>(t_evaluated_.size() +
                          speed_limit.speed_limit_points().size()) <
      t_evaluated_.size() * std::log(static_cast<double>(
                                speed_limit.speed_limit_points().size()))) {
    uint32_t i = 0;
    uint32_t j = 0;
    const double kDistanceEpsilon = 1e-6;
    while (i < t_evaluated_.size() &&
           j + 1 < speed_limit.speed_limit_points().size()) {
      const double distance = v * t_evaluated_[i];
      if (fabs(distance - speed_limit.speed_limit_points()[j].first) <
          kDistanceEpsilon) {
        speed_upper_bound->push_back(
            speed_limit.speed_limit_points()[j].second);
        ++i;
        ADEBUG << "speed upper bound:" << speed_upper_bound->back();
      } else if (distance < speed_limit.speed_limit_points()[j].first) {
        ++i;
      } else if (distance <= speed_limit.speed_limit_points()[j + 1].first) {
        speed_upper_bound->push_back(speed_limit.GetSpeedLimitByS(distance));
        ADEBUG << "speed upper bound:" << speed_upper_bound->back();
        ++i;
      } else {
        ++j;
      }
    }

    for (uint32_t k = speed_upper_bound->size(); k < t_evaluated_.size(); ++k) {
      speed_upper_bound->push_back(qp_spline_st_speed_config_.max_speed());
      ADEBUG << "speed upper bound:" << speed_upper_bound->back();
    }
  } else {
    auto cmp = [](const std::pair<double, double>& p1, const double s) {
      return p1.first < s;
    };

481
    const auto& speed_limit_points = speed_limit.speed_limit_points();
482 483 484 485 486 487 488
    for (const double t : t_evaluated_) {
      const double s = v * t;

      // NOTICE: we are using binary search here based on two assumptions:
      // (1) The s in speed_limit_points increase monotonically.
      // (2) The evaluated_t_.size() << number of speed_limit_points.size()
      //
489 490
      // If either of the two assumption is failed, a new algorithm must be
      // used
491
      // to replace the binary search.
492 493 494 495 496 497 498 499

      const auto& it = std::lower_bound(speed_limit_points.begin(),
                                        speed_limit_points.end(), s, cmp);
      if (it != speed_limit_points.end()) {
        speed_upper_bound->push_back(it->second);
      } else {
        speed_upper_bound->push_back(speed_limit_points.back().second);
      }
500
    }
501
  }
502 503

  const double kTimeBuffer = 2.0;
504
  const double kSpeedBuffer = 0.1;
505 506 507
  for (uint32_t k = 0; k < t_evaluated_.size() && t_evaluated_[k] < kTimeBuffer;
       ++k) {
    speed_upper_bound->at(k) =
508
        std::fmax(init_point_.v() + kSpeedBuffer, speed_upper_bound->at(k));
509
  }
510

511 512 513
  return Status::OK();
}

Z
Zhang Liangliang 已提交
514 515
}  // namespace planning
}  // namespace apollo