23#include <coin/IpIpoptApplication.hpp>
24#include <coin/IpSolveStatistics.hpp>
26#include "modules/common_msgs/basic_msgs/pnc_point.pb.h"
27#include "modules/planning/planning_base/proto/ipopt_return_status.pb.h"
48 smoothed_speed_limit_(0.0, 0.0, 0.0),
49 smoothed_path_curvature_(0.0, 0.0, 0.0) {}
52 const std::string& config_dir,
const std::string& name,
53 const std::shared_ptr<DependencyInjector>& injector) {
58 if (SpeedOptimizer::LoadConfig<PiecewiseJerkNonlinearSpeedOptimizerConfig>(
60 AERROR <<
"Failed to load config of PiecewiseJerkSpeedNonlinearOptimizer "
66Status PiecewiseJerkSpeedNonlinearOptimizer::Process(
69 if (speed_data ==
nullptr) {
70 const std::string msg =
"Null speed_data pointer";
72 return Status(ErrorCode::PLANNING_ERROR, msg);
76 const std::string msg =
"Speed Optimizer receives empty path data";
78 return Status(ErrorCode::PLANNING_ERROR, msg);
85 const auto problem_setups_status =
86 SetUpStatesAndBounds(path_data, *speed_data);
87 if (!problem_setups_status.ok()) {
89 return problem_setups_status;
92 std::vector<double> distance;
93 std::vector<double> velocity;
94 std::vector<double> acceleration;
96 const auto qp_start = std::chrono::system_clock::now();
98 const auto qp_smooth_status =
99 OptimizeByQP(speed_data, &distance, &velocity, &acceleration);
101 const auto qp_end = std::chrono::system_clock::now();
102 std::chrono::duration<double> qp_diff = qp_end - qp_start;
103 ADEBUG <<
"speed qp optimization takes " << qp_diff.count() * 1000.0 <<
" ms";
105 if (!qp_smooth_status.ok()) {
107 return qp_smooth_status;
110 const bool speed_limit_check_status = CheckSpeedLimitFeasibility();
112 if (speed_limit_check_status) {
113 const auto curvature_smooth_start = std::chrono::system_clock::now();
115 const auto path_curvature_smooth_status = SmoothPathCurvature(path_data);
117 const auto curvature_smooth_end = std::chrono::system_clock::now();
118 std::chrono::duration<double> curvature_smooth_diff =
119 curvature_smooth_end - curvature_smooth_start;
120 ADEBUG <<
"path curvature smoothing for nlp optimization takes "
121 << curvature_smooth_diff.count() * 1000.0 <<
" ms";
123 if (!path_curvature_smooth_status.ok()) {
125 return path_curvature_smooth_status;
128 const auto speed_limit_smooth_start = std::chrono::system_clock::now();
130 const auto speed_limit_smooth_status = SmoothSpeedLimit();
132 const auto speed_limit_smooth_end = std::chrono::system_clock::now();
133 std::chrono::duration<double> speed_limit_smooth_diff =
134 speed_limit_smooth_end - speed_limit_smooth_start;
135 ADEBUG <<
"speed limit smoothing for nlp optimization takes "
136 << speed_limit_smooth_diff.count() * 1000.0 <<
" ms";
138 if (!speed_limit_smooth_status.ok()) {
140 return speed_limit_smooth_status;
143 const auto nlp_start = std::chrono::system_clock::now();
145 const auto nlp_smooth_status =
146 OptimizeByNLP(&distance, &velocity, &acceleration);
148 const auto nlp_end = std::chrono::system_clock::now();
149 std::chrono::duration<double> nlp_diff = nlp_end - nlp_start;
150 ADEBUG <<
"speed nlp optimization takes " << nlp_diff.count() * 1000.0
153 if (!nlp_smooth_status.ok()) {
155 return nlp_smooth_status;
160 auto* speed_constraint =
162 for (
int i = 0; i < num_of_knots_; ++i) {
163 double t = i * delta_t_;
164 speed_constraint->add_t(t);
165 speed_constraint->add_upper_bound(
166 smoothed_speed_limit_.
Evaluate(0, distance[i]));
171 speed_data->
AppendSpeedPoint(distance[0], 0.0, velocity[0], acceleration[0],
173 for (
int i = 1; i < num_of_knots_; ++i) {
175 if (velocity[i] < 0.0) {
179 distance[i], delta_t_ * i, velocity[i], acceleration[i],
180 (acceleration[i] - acceleration[i - 1]) / delta_t_);
189Status PiecewiseJerkSpeedNonlinearOptimizer::SetUpStatesAndBounds(
190 const PathData& path_data,
const SpeedData& speed_data) {
192 const StGraphData& st_graph_data =
196 total_length_ = st_graph_data.path_length();
197 total_time_ = st_graph_data.total_time_by_conf();
198 num_of_knots_ =
static_cast<int>(total_time_ / delta_t_) + 1;
202 s_dot_init_ = st_graph_data.init_point().v();
203 s_ddot_init_ = st_graph_data.init_point().a();
206 s_dot_max_ = std::fmax(FLAGS_planning_upper_speed_limit,
207 st_graph_data.init_point().v());
210 const auto& veh_param =
212 s_ddot_max_ = veh_param.max_acceleration();
213 s_ddot_min_ = -1.0 * std::abs(veh_param.max_deceleration());
218 s_dddot_min_ = -std::abs(FLAGS_longitudinal_jerk_lower_bound);
219 s_dddot_max_ = FLAGS_longitudinal_jerk_upper_bound;
224 s_soft_bounds_.clear();
226 for (
int i = 0; i < num_of_knots_; ++i) {
227 double curr_t = i * delta_t_;
228 double s_lower_bound = 0.0;
229 double s_upper_bound = total_length_;
230 double s_soft_lower_bound = 0.0;
231 double s_soft_upper_bound = total_length_;
232 for (
const STBoundary* boundary : st_graph_data.st_boundaries()) {
233 double s_lower = 0.0;
234 double s_upper = 0.0;
235 if (!boundary->GetUnblockSRange(curr_t, &s_upper, &s_lower)) {
239 switch (boundary->boundary_type()) {
242 s_upper_bound = std::fmin(s_upper_bound, s_upper);
243 s_soft_upper_bound = std::fmin(s_soft_upper_bound, s_upper);
246 s_upper_bound = std::fmin(s_upper_bound, s_upper);
247 if (!speed_data.EvaluateByTime(curr_t, &sp)) {
248 const std::string msg =
249 "rough speed profile estimation for soft follow fence failed";
251 return Status(ErrorCode::PLANNING_ERROR, msg);
253 s_soft_upper_bound = std::fmin(
254 s_soft_upper_bound, s_upper - std::min(7.0, 2.5 * sp.v()));
257 s_lower_bound = std::fmax(s_lower_bound, s_lower);
258 s_soft_lower_bound = std::fmax(s_soft_lower_bound, s_lower + 10.0);
264 if (s_lower_bound > s_upper_bound) {
265 const std::string msg =
266 "s_lower_bound larger than s_upper_bound on STGraph";
268 return Status(ErrorCode::PLANNING_ERROR, msg);
270 s_soft_bounds_.emplace_back(s_soft_lower_bound, s_soft_upper_bound);
271 s_bounds_.emplace_back(s_lower_bound, s_upper_bound);
276 for (
int i = 0; i < num_of_knots_; ++i) {
277 double curr_t = i * delta_t_;
278 double s_lower_bound = 0.0;
279 double s_upper_bound = total_length_;
280 for (
const STBoundary* boundary : st_graph_data.st_boundaries()) {
281 double s_lower = 0.0;
282 double s_upper = 0.0;
283 if (!boundary->GetUnblockSRange(curr_t, &s_upper, &s_lower)) {
287 switch (boundary->boundary_type()) {
290 s_upper_bound = std::fmin(s_upper_bound, s_upper);
293 s_upper_bound = std::fmin(s_upper_bound, s_upper - 8.0);
296 s_lower_bound = std::fmax(s_lower_bound, s_lower);
302 if (s_lower_bound > s_upper_bound) {
303 const std::string msg =
304 "s_lower_bound larger than s_upper_bound on STGraph";
306 return Status(ErrorCode::PLANNING_ERROR, msg);
308 s_bounds_.emplace_back(s_lower_bound, s_upper_bound);
312 speed_limit_ = st_graph_data.speed_limit();
317bool PiecewiseJerkSpeedNonlinearOptimizer::CheckSpeedLimitFeasibility() {
319 static constexpr double kEpsilon = 1e-6;
321 if (init_speed_limit + kEpsilon < s_dot_init_) {
322 AERROR <<
"speed limit [" << init_speed_limit
323 <<
"] lower than initial speed[" << s_dot_init_ <<
"]";
329Status PiecewiseJerkSpeedNonlinearOptimizer::SmoothSpeedLimit() {
332 double delta_s = 2.0;
334 std::vector<double> speed_ref;
335 for (
int i = 0; i < 100; ++i) {
336 double path_s = i * delta_s;
338 speed_ref.emplace_back(limit);
339 debug.AddPoint(
"coarse_v_bounds_upper", path_s, limit);
341 std::array<double, 3> init_state = {speed_ref[0], 0.0, 0.0};
342 PiecewiseJerkPathProblem piecewise_jerk_problem(speed_ref.size(), delta_s,
344 piecewise_jerk_problem.set_x_bounds(0.0, 50.0);
345 piecewise_jerk_problem.set_dx_bounds(-100.0, 100.0);
346 piecewise_jerk_problem.set_ddx_bounds(-1000.0, 1000.0);
347 piecewise_jerk_problem.set_dddx_bound(-10000.0, 100000.0);
349 piecewise_jerk_problem.set_weight_x(0.0);
350 piecewise_jerk_problem.set_weight_dx(1.0);
351 piecewise_jerk_problem.set_weight_ddx(1.0);
352 piecewise_jerk_problem.set_weight_dddx(1.0);
354 piecewise_jerk_problem.set_x_ref(10.0, std::move(speed_ref));
356 if (!piecewise_jerk_problem.Optimize(4000)) {
357 const std::string msg =
"Smoothing speed limit failed";
359 return Status(ErrorCode::PLANNING_ERROR, msg);
362 std::vector<double> opt_x;
363 std::vector<double> opt_dx;
364 std::vector<double> opt_ddx;
366 opt_x = piecewise_jerk_problem.opt_x();
367 opt_dx = piecewise_jerk_problem.opt_dx();
368 opt_ddx = piecewise_jerk_problem.opt_ddx();
369 PiecewiseJerkTrajectory1d smoothed_speed_limit(opt_x.front(), opt_dx.front(),
372 for (
size_t i = 1; i < opt_ddx.size(); ++i) {
373 double j = (opt_ddx[i] - opt_ddx[i - 1]) / delta_s;
374 smoothed_speed_limit.AppendSegment(j, delta_s);
376 for (
size_t i = 0; i < opt_x.size(); i++) {
377 debug.AddPoint(
"smooth_v_bounds_upper", i * delta_s, opt_x[i]);
380 smoothed_speed_limit_ = smoothed_speed_limit;
385Status PiecewiseJerkSpeedNonlinearOptimizer::SmoothPathCurvature(
386 const PathData& path_data) {
389 const auto& cartesian_path = path_data.discretized_path();
390 const double delta_s = 0.5;
391 std::vector<double> path_curvature;
393 for (
double path_s = cartesian_path.front().s();
394 path_s < cartesian_path.back().s() + delta_s; path_s += delta_s) {
395 const auto& path_point = cartesian_path.Evaluate(path_s);
396 path_curvature.push_back(path_point.kappa());
397 debug.AddPoint(
"sk_corase", path_s, path_point.kappa());
399 const auto& path_init_point = cartesian_path.front();
400 std::array<double, 3> init_state = {path_init_point.kappa(),
401 path_init_point.dkappa(),
402 path_init_point.ddkappa()};
403 PiecewiseJerkPathProblem piecewise_jerk_problem(path_curvature.size(),
404 delta_s, init_state);
405 piecewise_jerk_problem.set_x_bounds(-1.0, 1.0);
406 piecewise_jerk_problem.set_dx_bounds(-10.0, 10.0);
407 piecewise_jerk_problem.set_ddx_bounds(-10.0, 10.0);
408 piecewise_jerk_problem.set_dddx_bound(-10.0, 10.0);
410 piecewise_jerk_problem.set_weight_x(0.0);
411 piecewise_jerk_problem.set_weight_dx(10.0);
412 piecewise_jerk_problem.set_weight_ddx(10.0);
413 piecewise_jerk_problem.set_weight_dddx(10.0);
415 piecewise_jerk_problem.set_x_ref(10.0, std::move(path_curvature));
417 if (!piecewise_jerk_problem.Optimize(1000)) {
418 const std::string msg =
"Smoothing path curvature failed";
420 return Status(ErrorCode::PLANNING_ERROR, msg);
423 std::vector<double> opt_x;
424 std::vector<double> opt_dx;
425 std::vector<double> opt_ddx;
427 opt_x = piecewise_jerk_problem.opt_x();
428 opt_dx = piecewise_jerk_problem.opt_dx();
429 opt_ddx = piecewise_jerk_problem.opt_ddx();
431 PiecewiseJerkTrajectory1d smoothed_path_curvature(
432 opt_x.front(), opt_dx.front(), opt_ddx.front());
434 for (
size_t i = 1; i < opt_ddx.size(); ++i) {
435 double j = (opt_ddx[i] - opt_ddx[i - 1]) / delta_s;
436 smoothed_path_curvature.AppendSegment(j, delta_s);
439 for (
size_t i = 0; i < opt_x.size(); ++i) {
440 debug.AddPoint(
"sk_smooth", delta_s * i, opt_x[i]);
443 smoothed_path_curvature_ = smoothed_path_curvature;
448Status PiecewiseJerkSpeedNonlinearOptimizer::OptimizeByQP(
449 SpeedData*
const speed_data, std::vector<double>* distance,
450 std::vector<double>* velocity, std::vector<double>* acceleration) {
451 std::array<double, 3> init_states = {s_init_, s_dot_init_, s_ddot_init_};
452 PiecewiseJerkSpeedProblem piecewise_jerk_problem(num_of_knots_, delta_t_,
454 piecewise_jerk_problem.set_dx_bounds(
455 0.0, std::fmax(FLAGS_planning_upper_speed_limit, init_states[1]));
456 piecewise_jerk_problem.set_ddx_bounds(s_ddot_min_, s_ddot_max_);
457 piecewise_jerk_problem.set_dddx_bound(s_dddot_min_, s_dddot_max_);
458 piecewise_jerk_problem.set_x_bounds(s_bounds_);
461 piecewise_jerk_problem.set_weight_x(0.0);
462 piecewise_jerk_problem.set_weight_dx(0.0);
463 piecewise_jerk_problem.set_weight_ddx(config_.
acc_weight());
464 piecewise_jerk_problem.set_weight_dddx(config_.
jerk_weight());
466 std::vector<double> x_ref;
467 for (
int i = 0; i < num_of_knots_; ++i) {
468 const double curr_t = i * delta_t_;
471 speed_data->EvaluateByTime(curr_t, &sp);
472 x_ref.emplace_back(sp.s());
473 debug.AddPoint(
"dp_st_curve", curr_t, sp.s());
475 piecewise_jerk_problem.set_x_ref(config_.
ref_s_weight(), std::move(x_ref));
478 if (!piecewise_jerk_problem.Optimize()) {
479 const std::string msg =
480 "Speed Optimization by Quadratic Programming failed. "
481 "st boundary is infeasible.";
483 return Status(ErrorCode::PLANNING_ERROR, msg);
486 *distance = piecewise_jerk_problem.opt_x();
487 *velocity = piecewise_jerk_problem.opt_dx();
488 *acceleration = piecewise_jerk_problem.opt_ddx();
489 for (
size_t i = 0; i < distance->size(); i++) {
490 const double curr_t = i * delta_t_;
491 debug.AddPoint(
"warm_start_st", curr_t, distance->at(i));
492 debug.AddPoint(
"warm_start_vt", curr_t, velocity->at(i));
493 debug.AddPoint(
"warm_start_at", curr_t, acceleration->at(i));
499Status PiecewiseJerkSpeedNonlinearOptimizer::OptimizeByNLP(
500 std::vector<double>* distance, std::vector<double>* velocity,
501 std::vector<double>* acceleration) {
502 static std::mutex mutex_tnlp;
505 auto ptr_interface =
new PiecewiseJerkSpeedNonlinearIpoptInterface(
506 s_init_, s_dot_init_, s_ddot_init_, delta_t_, num_of_knots_,
507 total_length_, s_dot_max_, s_ddot_min_, s_ddot_max_, s_dddot_min_,
510 ptr_interface->set_safety_bounds(s_bounds_);
511 for (
size_t i = 0; i < s_bounds_.size(); i++) {
512 debug.AddPoint(
"st_bounds_lower", i * delta_t_, s_bounds_[i].first);
513 debug.AddPoint(
"st_bounds_upper", i * delta_t_, s_bounds_[i].second);
516 const auto& warm_start_distance = *distance;
517 const auto& warm_start_velocity = *velocity;
518 const auto& warm_start_acceleration = *acceleration;
519 if (warm_start_distance.empty() || warm_start_velocity.empty() ||
520 warm_start_acceleration.empty() ||
521 warm_start_distance.size() != warm_start_velocity.size() ||
522 warm_start_velocity.size() != warm_start_acceleration.size()) {
523 const std::string msg =
524 "Piecewise jerk speed nonlinear optimizer warm start invalid!";
526 return Status(ErrorCode::PLANNING_ERROR, msg);
528 std::vector<std::vector<double>> warm_start;
529 std::size_t size = warm_start_distance.size();
530 for (std::size_t i = 0; i < size; ++i) {
531 warm_start.emplace_back(std::initializer_list<double>(
532 {warm_start_distance[i], warm_start_velocity[i],
533 warm_start_acceleration[i]}));
535 ptr_interface->set_warm_start(warm_start);
539 ptr_interface->set_reference_spatial_distance(*distance);
541 ptr_interface->set_w_reference_spatial_distance(10.0);
543 std::vector<double> spatial_potantial(num_of_knots_, total_length_);
544 ptr_interface->set_reference_spatial_distance(spatial_potantial);
545 ptr_interface->set_w_reference_spatial_distance(
550 ptr_interface->set_soft_safety_bounds(s_soft_bounds_);
554 ptr_interface->set_w_overall_a(config_.
acc_weight());
555 ptr_interface->set_w_overall_j(config_.
jerk_weight());
556 ptr_interface->set_w_overall_centripetal_acc(config_.
lat_acc_weight());
558 ptr_interface->set_reference_speed(cruise_speed_);
559 ptr_interface->set_w_reference_speed(config_.
ref_v_weight());
561 Ipopt::SmartPtr<Ipopt::TNLP> problem = ptr_interface;
562 Ipopt::SmartPtr<Ipopt::IpoptApplication> app = IpoptApplicationFactory();
564 app->Options()->SetIntegerValue(
"print_level", 0);
565 app->Options()->SetIntegerValue(
"max_iter", 1000);
567 Ipopt::ApplicationReturnStatus status = app->Initialize();
568 if (status != Ipopt::Solve_Succeeded) {
569 const std::string msg =
570 "Piecewise jerk speed nonlinear optimizer failed during initialization";
572 return Status(ErrorCode::PLANNING_ERROR, msg);
575 const auto start_timestamp = std::chrono::system_clock::now();
576 status = app->OptimizeTNLP(problem);
578 const auto end_timestamp = std::chrono::system_clock::now();
579 std::chrono::duration<double>
diff = end_timestamp - start_timestamp;
580 ADEBUG <<
"*** The optimization problem take time: " <<
diff.count() * 1000.0
583 if (status == Ipopt::Solve_Succeeded ||
584 status == Ipopt::Solved_To_Acceptable_Level) {
586 Ipopt::Index iter_count = app->Statistics()->IterationCount();
587 ADEBUG <<
"*** The problem solved in " << iter_count <<
" iterations!";
588 Ipopt::Number final_obj = app->Statistics()->FinalObjective();
589 ADEBUG <<
"*** The final value of the objective function is " << final_obj
592 const auto& ipopt_return_status =
593 IpoptReturnStatus_Name(
static_cast<IpoptReturnStatus
>(status));
594 if (ipopt_return_status.empty()) {
595 AERROR <<
"Solver ends with unknown failure code: "
596 <<
static_cast<int>(status);
598 AERROR <<
"Solver failure case is : " << ipopt_return_status;
600 const std::string msg =
"Piecewise jerk speed nonlinear optimizer failed";
602 return Status(ErrorCode::PLANNING_ERROR, msg);
605 ptr_interface->get_optimization_results(distance, velocity, acceleration);
606 for (
size_t i = 0; i < distance->size(); i++) {
607 debug.AddPoint(
"optimize_st_curve", i * delta_t_, (*distance)[i]);
608 debug.AddPoint(
"optimize_vt_curve", i * delta_t_, (*velocity)[i]);
609 debug.AddPoint(
"optimize_at_curve", i * delta_t_, (*acceleration)[i]);
610 debug.AddPoint(
"optimize_sv_curve", (*distance)[i], (*velocity)[i]);
A general class to denote the return status of an API call.
static Status OK()
generate a success status.
static const VehicleConfig & GetConfig()
Get the current vehicle configuration.
const DiscretizedPath & discretized_path() const
bool Init(const std::string &config_dir, const std::string &name, const std::shared_ptr< DependencyInjector > &injector) override
PiecewiseJerkSpeedNonlinearOptimizer()
double Evaluate(const std::uint32_t order, const double param) const
StGraphData * mutable_st_graph_data()
double GetCruiseSpeed() const
bool ReachedDestination() const
void AppendSpeedPoint(const double s, const double time, const double v, const double a, const double da)
double GetSpeedLimitByS(const double s) const
void RecordDebugInfo(const SpeedData &speed_data)
static void FillEnoughSpeedPoints(SpeedData *const speed_data)
planning_internal::STGraphDebug * mutable_st_graph_debug()
const std::string & Name() const
ReferenceLineInfo * reference_line_info_
virtual bool Init(const std::string &config_dir, const std::string &name, const std::shared_ptr< DependencyInjector > &injector)
Planning module main class.
#define UNIQUE_LOCK_MULTITHREAD(mutex_type)
float diff(Image< float > *I, int x1, int y1, int x2, int y2)
Dissimilarity measure between pixels
: data with map info and obstacle info
optional VehicleParam vehicle_param
optional double soft_s_bound_weight
optional double ref_v_weight
optional double ref_s_weight
optional double jerk_weight
optional bool use_soft_bound_in_nonlinear_speed_opt
optional double lat_acc_weight
optional bool use_warm_start
optional double s_potential_weight
optional bool use_smoothed_dp_guide_line
optional double acc_weight