qp_spline_st_graph.cc 18.2 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
          qp_spline_st_speed_config_.number_of_evaluated_graph_t()) {}
Z
Zhang Liangliang 已提交
49

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

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

  // init spline generator
68 69
  spline_generator_.reset(new Spline1dGenerator(
      t_knots_, qp_spline_st_speed_config_.spline_order()));
70 71
}

72
Status QpSplineStGraph::Search(const StGraphData& st_graph_data,
73
                               const PathData& path_data,
74
                               SpeedData* const speed_data,
A
Aaron Xiao 已提交
75
                               STGraphDebug* st_graph_debug) {
76
  init_point_ = st_graph_data.init_point();
Z
Zhang Liangliang 已提交
77
  if (st_graph_data.path_data_length() <
78 79
      qp_spline_st_speed_config_.total_path_length()) {
    qp_spline_st_speed_config_.set_total_path_length(
Z
Zhang Liangliang 已提交
80 81 82
        st_graph_data.path_data_length());
  }

A
Aaron Xiao 已提交
83
  // TODO(all): update config through veh physical limit here generate knots
Z
Zhang Liangliang 已提交
84

85 86
  // initialize time resolution and
  Init();
Z
Zhang Liangliang 已提交
87

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

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

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

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

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

126
  return Status::OK();
Z
Zhang Liangliang 已提交
127 128
}

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

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

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

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

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

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

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

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

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

202
  std::vector<double> speed_lower_bound(t_evaluated_.size(), 0.0);
203 204 205

  DCHECK_EQ(t_evaluated_.size(), speed_upper_bound.size());
  DCHECK_EQ(t_evaluated_.size(), speed_lower_bound.size());
206 207 208 209 210 211 212 213

  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]);
  }

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

226
  // acceleration constraint
227 228
  constexpr double kAccelLowerBound = -4.0;
  constexpr double kAccelUpperBound = 1.5;
229 230 231
  std::vector<double> accel_lower_bound(t_evaluated_.size(), kAccelLowerBound);
  std::vector<double> accel_upper_bound(t_evaluated_.size(), kAccelUpperBound);

232 233 234 235 236 237 238 239 240
  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()));
    }
  }
241
  if (FLAGS_enable_follow_accel_constraint && has_follow && delta_s < 0.0) {
242 243 244
    accel_upper_bound.front() = 0.0;
  } else {
    constexpr double kInitPointAccelRelaxedSpeed = 1.0;
245 246 247 248
    constexpr double kInitPointAccelRelaxedRange = 0.5;
    if (init_point_.v() > kInitPointAccelRelaxedSpeed) {
      accel_lower_bound.front() = init_point_.a() - kInitPointAccelRelaxedRange;
      accel_upper_bound.front() = init_point_.a() + kInitPointAccelRelaxedRange;
249
    }
250 251
  }

252 253 254 255 256 257 258
  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);
  }
259 260 261 262 263
  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];
  }
264

265
  return Status::OK();
Z
Zhang Liangliang 已提交
266 267
}

268
Status QpSplineStGraph::ApplyKernel(const std::vector<StBoundary>& boundaries,
269 270
                                    const SpeedLimit& speed_limit,
                                    STGraphDebug* st_graph_debug) {
271
  Spline1dKernel* spline_kernel = spline_generator_->mutable_spline_kernel();
Z
Zhang Liangliang 已提交
272

273
  if (qp_spline_st_speed_config_.speed_kernel_weight() > 0) {
274
    spline_kernel->AddDerivativeKernelMatrix(
275
        qp_spline_st_speed_config_.speed_kernel_weight());
Z
Zhang Liangliang 已提交
276 277
  }

278
  if (qp_spline_st_speed_config_.accel_kernel_weight() > 0) {
279
    spline_kernel->AddSecondOrderDerivativeMatrix(
280
        qp_spline_st_speed_config_.accel_kernel_weight());
Z
Zhang Liangliang 已提交
281 282
  }

283
  if (qp_spline_st_speed_config_.jerk_kernel_weight() > 0) {
284
    spline_kernel->AddThirdOrderDerivativeMatrix(
285
        qp_spline_st_speed_config_.jerk_kernel_weight());
Z
Zhang Liangliang 已提交
286 287
  }

288 289 290
  if (AddCruiseReferenceLineKernel(speed_limit,
                                   qp_spline_st_speed_config_.cruise_weight(),
                                   st_graph_debug) != Status::OK()) {
291 292 293
    return Status(ErrorCode::PLANNING_ERROR, "QpSplineStGraph::ApplyKernel");
  }

294 295 296
  if (AddFollowReferenceLineKernel(boundaries,
                                   qp_spline_st_speed_config_.follow_weight(),
                                   st_graph_debug) != Status::OK()) {
297
    return Status(ErrorCode::PLANNING_ERROR, "QpSplineStGraph::ApplyKernel");
298
  }
299 300 301 302
  return Status::OK();
}

Status QpSplineStGraph::Solve() {
303
  return spline_generator_->Solve()
304 305 306
             ? Status::OK()
             : Status(ErrorCode::PLANNING_ERROR, "QpSplineStGraph::solve");
}
307

308
Status QpSplineStGraph::AddCruiseReferenceLineKernel(
309 310
    const SpeedLimit& speed_limit, const double weight,
    STGraphDebug* st_graph_debug) {
Z
Zhang Liangliang 已提交
311
  auto* spline_kernel = spline_generator_->mutable_spline_kernel();
312
  if (speed_limit.speed_limit_points().size() == 0) {
313 314 315
    std::string msg = "Fail to apply_kernel due to empty speed limits.";
    AERROR << msg;
    return Status(ErrorCode::PLANNING_ERROR, msg);
Z
Zhang Liangliang 已提交
316
  }
317
  double dist_ref = 0.0;
318
  auto kernel_cruise_ref = st_graph_debug->mutable_kernel_cruise_ref();
319
  cruise_.push_back(dist_ref);
320 321
  kernel_cruise_ref->mutable_t()->Add(t_evaluated_[0]);
  kernel_cruise_ref->mutable_cruise_line_s()->Add(dist_ref);
322 323
  for (uint32_t i = 1; i < t_evaluated_.size(); ++i) {
    dist_ref += (t_evaluated_[i] - t_evaluated_[i - 1]) *
324
                speed_limit.GetSpeedLimitByS(dist_ref);
325
    cruise_.push_back(dist_ref);
326 327
    kernel_cruise_ref->mutable_t()->Add(t_evaluated_[i]);
    kernel_cruise_ref->mutable_cruise_line_s()->Add(dist_ref);
328
  }
329
  DCHECK_EQ(t_evaluated_.size(), cruise_.size());
330

331
  for (std::size_t i = 0; i < t_evaluated_.size(); ++i) {
332
    ADEBUG << "Cruise Ref S: " << cruise_[i]
333
           << " Relative time: " << t_evaluated_[i] << std::endl;
334 335
  }

336
  if (t_evaluated_.size() > 0) {
337
    spline_kernel->AddReferenceLineKernelMatrix(
338 339
        t_evaluated_, cruise_,
        weight * qp_spline_st_speed_config_.total_time() / t_evaluated_.size());
340 341
  }
  spline_kernel->AddRegularization(0.01);
Z
Zhang Liangliang 已提交
342
  return Status::OK();
Z
Zhang Liangliang 已提交
343 344
}

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

381
  if (!ref_s.empty()) {
382
    spline_kernel->AddReferenceLineKernelMatrix(
383
        filtered_evaluate_t, ref_s,
384
        weight * qp_spline_st_speed_config_.total_time() / t_evaluated_.size());
385 386 387 388
  }

  for (std::size_t i = 0; i < filtered_evaluate_t.size(); ++i) {
    ADEBUG << "Follow Ref S: " << ref_s[i]
389
           << " Relative time: " << filtered_evaluate_t[i] << std::endl;
390
  }
391 392 393
  return Status::OK();
}

394
Status QpSplineStGraph::GetSConstraintByTime(
395
    const std::vector<StBoundary>& boundaries, const double time,
Z
Zhang Liangliang 已提交
396 397
    const double total_path_s, double* const s_upper_bound,
    double* const s_lower_bound) const {
398
  *s_upper_bound = total_path_s;
Z
Zhang Liangliang 已提交
399

400
  for (const StBoundary& boundary : boundaries) {
Z
Zhang Liangliang 已提交
401 402 403
    double s_upper = 0.0;
    double s_lower = 0.0;

404
    if (!boundary.GetUnblockSRange(time, &s_upper, &s_lower)) {
Z
Zhang Liangliang 已提交
405 406 407
      continue;
    }

408 409 410
    if (boundary.boundary_type() == StBoundary::BoundaryType::STOP ||
        boundary.boundary_type() == StBoundary::BoundaryType::FOLLOW ||
        boundary.boundary_type() == StBoundary::BoundaryType::YIELD) {
Z
Zhang Liangliang 已提交
411 412
      *s_upper_bound = std::fmin(*s_upper_bound, s_upper);
    } else {
413
      DCHECK(boundary.boundary_type() == StBoundary::BoundaryType::OVERTAKE);
Z
Zhang Liangliang 已提交
414 415 416 417
      *s_lower_bound = std::fmax(*s_lower_bound, s_lower);
    }
  }

418
  return Status::OK();
Z
Zhang Liangliang 已提交
419
}
420

421
Status QpSplineStGraph::EstimateSpeedUpperBound(
422
    const common::TrajectoryPoint& init_point, const SpeedLimit& speed_limit,
423 424
    std::vector<double>* speed_upper_bound) const {
  DCHECK_NOTNULL(speed_upper_bound);
425

426
  speed_upper_bound->clear();
427 428 429 430

  // 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.
431
  const double v = init_point.v();
432 433 434 435 436 437 438 439 440 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

  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;
    };

469
    const auto& speed_limit_points = speed_limit.speed_limit_points();
470 471 472 473 474 475 476
    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()
      //
477 478
      // If either of the two assumption is failed, a new algorithm must be
      // used
479
      // to replace the binary search.
480 481 482 483 484 485 486 487

      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);
      }
488
    }
489
  }
490 491

  const double kTimeBuffer = 2.0;
492
  const double kSpeedBuffer = 0.1;
493 494 495
  for (uint32_t k = 0; k < t_evaluated_.size() && t_evaluated_[k] < kTimeBuffer;
       ++k) {
    speed_upper_bound->at(k) =
496
        std::fmax(init_point_.v() + kSpeedBuffer, speed_upper_bound->at(k));
497
  }
498

499 500 501
  return Status::OK();
}

Z
Zhang Liangliang 已提交
502 503
}  // namespace planning
}  // namespace apollo