Apollo 11.0
自动驾驶开放平台
lat_controller.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2017 The Apollo Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
18
19#include <algorithm>
20#include <iomanip>
21#include <utility>
22#include <vector>
23
24#include "Eigen/LU"
25#include "absl/strings/str_cat.h"
26
27#include "cyber/common/log.h"
28#include "cyber/time/clock.h"
36
37namespace apollo {
38namespace control {
39
44using Matrix = Eigen::MatrixXd;
46
47namespace {
48
49std::string GetLogFileName() {
50 time_t raw_time;
51 char name_buffer[80];
52 std::time(&raw_time);
53 std::tm time_tm;
54 localtime_r(&raw_time, &time_tm);
55 strftime(name_buffer, 80, "/tmp/steer_log_simple_optimal_%F_%H%M%S.csv",
56 &time_tm);
57 return std::string(name_buffer);
58}
59
60void WriteHeaders(std::ofstream &file_stream) {
61 file_stream << "current_lateral_error," << "current_ref_heading,"
62 << "current_heading," << "current_heading_error,"
63 << "heading_error_rate," << "lateral_error_rate,"
64 << "current_curvature," << "steer_angle,"
65 << "steer_angle_feedforward,"
66 << "steer_angle_lateral_contribution,"
67 << "steer_angle_lateral_rate_contribution,"
68 << "steer_angle_heading_contribution,"
69 << "steer_angle_heading_rate_contribution,"
70 << "steer_angle_feedback," << "steering_position," << "v"
71 << std::endl;
72}
73} // namespace
74
75LatController::LatController() : name_("LQR-based Lateral Controller") {
76 if (FLAGS_enable_csv_debug) {
77 steer_log_file_.open(GetLogFileName());
78 steer_log_file_ << std::fixed;
79 steer_log_file_ << std::setprecision(6);
80 WriteHeaders(steer_log_file_);
81 }
82 AINFO << "Using " << name_;
83}
84
86
89 common::VehicleConfigHelper::Instance()->GetConfig().vehicle_param();
90
92 if (ts_ <= 0.0) {
93 AERROR << "[LatController] Invalid control update interval.";
94 return false;
95 }
100 lat_based_lqr_controller_conf_.lookahead_station();
102 lat_based_lqr_controller_conf_.lookback_station();
104 lat_based_lqr_controller_conf_.lookahead_station_high_speed();
106 lat_based_lqr_controller_conf_.lookback_station_high_speed();
110 vehicle_param_.max_steer_angle() / M_PI * 180;
111 max_lat_acc_ = lat_based_lqr_controller_conf_.max_lateral_acceleration();
114
115 const double mass_fl = lat_based_lqr_controller_conf_.mass_fl();
116 const double mass_fr = lat_based_lqr_controller_conf_.mass_fr();
117 const double mass_rl = lat_based_lqr_controller_conf_.mass_rl();
118 const double mass_rr = lat_based_lqr_controller_conf_.mass_rr();
119 const double mass_front = mass_fl + mass_fr;
120 const double mass_rear = mass_rl + mass_rr;
121 mass_ = mass_front + mass_rear;
122
123 lf_ = wheelbase_ * (1.0 - mass_front / mass_);
124 lr_ = wheelbase_ * (1.0 - mass_rear / mass_);
125
126 // moment of inertia
127 iz_ = lf_ * lf_ * mass_front + lr_ * lr_ * mass_rear;
128
131
133
134 minimum_speed_protection_ = FLAGS_minimum_speed_protection;
135
136 return true;
137}
138
140 const canbus::Chassis *chassis) {
141 const std::string log_str = absl::StrCat(
142 debug->lateral_error(), ",", debug->ref_heading(), ",", debug->heading(),
143 ",", debug->heading_error(), ",", debug->heading_error_rate(), ",",
144 debug->lateral_error_rate(), ",", debug->curvature(), ",",
145 debug->steer_angle(), ",", debug->steer_angle_feedforward(), ",",
150 debug->steer_angle_feedback(), ",", chassis->steering_percentage(), ",",
151 injector_->vehicle_state()->linear_velocity());
152 if (FLAGS_enable_csv_debug) {
153 steer_log_file_ << log_str << std::endl;
154 }
155 ADEBUG << "Steer_Control_Detail: " << log_str;
156}
157
159 AINFO << name_ << " begin.";
160 AINFO << "[LatController parameters]" << " mass_: " << mass_ << ","
161 << " iz_: " << iz_ << "," << " lf_: " << lf_ << "," << " lr_: " << lr_;
162}
163
165 // Low pass filter
166 std::vector<double> den(3, 0.0);
167 std::vector<double> num(3, 0.0);
169 &den, &num);
171 lateral_error_filter_ = common::MeanFilter(static_cast<std::uint_fast8_t>(
172 lat_based_lqr_controller_conf_.mean_filter_window_size()));
173 heading_error_filter_ = common::MeanFilter(static_cast<std::uint_fast8_t>(
174 lat_based_lqr_controller_conf_.mean_filter_window_size()));
175}
176
177Status LatController::Init(std::shared_ptr<DependencyInjector> injector) {
178 if (!ControlTask::LoadConfig<LatBaseLqrControllerConf>(
180 AERROR << "failed to load control conf";
181 return Status(ErrorCode::CONTROL_INIT_ERROR,
182 "failed to load lat control_conf");
183 }
184 injector_ = injector;
185 if (!LoadControlConf()) {
186 AERROR << "failed to load control conf";
187 return Status(ErrorCode::CONTROL_COMPUTE_ERROR,
188 "failed to load control_conf");
189 }
190 // Matrix init operations.
191 const int matrix_size = basic_state_size_ + preview_window_;
194 matrix_adc_ = Matrix::Zero(matrix_size, matrix_size);
195 /*
196 A matrix (Gear Drive)
197 [0.0, 1.0, 0.0, 0.0;
198 0.0, (-(c_f + c_r) / m) / v, (c_f + c_r) / m,
199 (l_r * c_r - l_f * c_f) / m / v;
200 0.0, 0.0, 0.0, 1.0;
201 0.0, ((lr * cr - lf * cf) / i_z) / v, (l_f * c_f - l_r * c_r) / i_z,
202 (-1.0 * (l_f^2 * c_f + l_r^2 * c_r) / i_z) / v;]
203 */
204 matrix_a_(0, 1) = 1.0;
205 matrix_a_(1, 2) = (cf_ + cr_) / mass_;
206 matrix_a_(2, 3) = 1.0;
207 matrix_a_(3, 2) = (lf_ * cf_ - lr_ * cr_) / iz_;
208
209 matrix_a_coeff_ = Matrix::Zero(matrix_size, matrix_size);
210 matrix_a_coeff_(1, 1) = -(cf_ + cr_) / mass_;
211 matrix_a_coeff_(1, 3) = (lr_ * cr_ - lf_ * cf_) / mass_;
212 matrix_a_coeff_(3, 1) = (lr_ * cr_ - lf_ * cf_) / iz_;
213 matrix_a_coeff_(3, 3) = -1.0 * (lf_ * lf_ * cf_ + lr_ * lr_ * cr_) / iz_;
214
215 /*
216 b = [0.0, c_f / m, 0.0, l_f * c_f / i_z]^T
217 */
218 matrix_b_ = Matrix::Zero(basic_state_size_, 1);
219 matrix_bd_ = Matrix::Zero(basic_state_size_, 1);
220 matrix_bdc_ = Matrix::Zero(matrix_size, 1);
221 matrix_b_(1, 0) = cf_ / mass_;
222 matrix_b_(3, 0) = lf_ * cf_ / iz_;
224
225 matrix_state_ = Matrix::Zero(matrix_size, 1);
226 matrix_k_ = Matrix::Zero(1, matrix_size);
227 matrix_r_ = Matrix::Identity(1, 1);
228 matrix_q_ = Matrix::Zero(matrix_size, matrix_size);
229
230 int q_param_size = lat_based_lqr_controller_conf_.matrix_q_size();
231 int reverse_q_param_size =
232 lat_based_lqr_controller_conf_.reverse_matrix_q_size();
233 if (matrix_size != q_param_size || matrix_size != reverse_q_param_size) {
234 const auto error_msg = absl::StrCat(
235 "lateral controller error: matrix_q size: ", q_param_size,
236 "lateral controller error: reverse_matrix_q size: ",
237 reverse_q_param_size,
238 " in parameter file not equal to matrix_size: ", matrix_size);
239 AERROR << error_msg;
240 return Status(ErrorCode::CONTROL_COMPUTE_ERROR, error_msg);
241 }
242
243 for (int i = 0; i < q_param_size; ++i) {
244 matrix_q_(i, i) = lat_based_lqr_controller_conf_.matrix_q(i);
245 }
246
251
253 lat_based_lqr_controller_conf_.enable_reverse_leadlag_compensation();
254 if (enable_leadlag_) {
256 lat_based_lqr_controller_conf_.reverse_leadlag_conf(), ts_);
257 }
258
259 enable_mrac_ = lat_based_lqr_controller_conf_.enable_steer_mrac_control();
260 if (enable_mrac_) {
263 }
264
266 lat_based_lqr_controller_conf_.enable_look_ahead_back_control();
267
268 return Status::OK();
269}
270
272 if (FLAGS_enable_csv_debug && steer_log_file_.is_open()) {
273 steer_log_file_.close();
274 }
275}
276
278 const auto &lat_err_gain_scheduler =
279 lat_based_lqr_controller_conf_.lat_err_gain_scheduler();
280 const auto &heading_err_gain_scheduler =
281 lat_based_lqr_controller_conf_.heading_err_gain_scheduler();
282 AINFO << "Lateral control gain scheduler loaded";
284 for (const auto &scheduler : lat_err_gain_scheduler.scheduler()) {
285 xy1.push_back(std::make_pair(scheduler.speed(), scheduler.ratio()));
286 }
287 for (const auto &scheduler : heading_err_gain_scheduler.scheduler()) {
288 xy2.push_back(std::make_pair(scheduler.speed(), scheduler.ratio()));
289 }
290
293 << "Fail to load lateral error gain scheduler";
294
297 << "Fail to load heading error gain scheduler";
298}
299
301
302std::string LatController::Name() const { return name_; }
303
305 const localization::LocalizationEstimate *localization,
306 const canbus::Chassis *chassis,
307 const planning::ADCTrajectory *planning_published_trajectory,
308 ControlCommand *cmd) {
309 auto vehicle_state = injector_->vehicle_state();
310 auto previous_lon_debug = injector_->Get_previous_lon_debug_info();
311 auto target_tracking_trajectory = *planning_published_trajectory;
312
313 if (FLAGS_use_navigation_mode &&
314 lat_based_lqr_controller_conf_.enable_navigation_mode_position_update()) {
315 auto time_stamp_diff =
316 planning_published_trajectory->header().timestamp_sec() -
318
319 auto curr_vehicle_x = localization->pose().position().x();
320 auto curr_vehicle_y = localization->pose().position().y();
321
322 double curr_vehicle_heading = 0.0;
323 const auto &orientation = localization->pose().orientation();
324 if (localization->pose().has_heading()) {
325 curr_vehicle_heading = localization->pose().heading();
326 } else {
327 curr_vehicle_heading =
328 common::math::QuaternionToHeading(orientation.qw(), orientation.qx(),
329 orientation.qy(), orientation.qz());
330 }
331
332 // new planning trajectory
333 if (time_stamp_diff > 1.0e-6) {
334 init_vehicle_x_ = curr_vehicle_x;
335 init_vehicle_y_ = curr_vehicle_y;
336 init_vehicle_heading_ = curr_vehicle_heading;
337
339 planning_published_trajectory->header().timestamp_sec();
340 } else {
341 auto x_diff_map = curr_vehicle_x - init_vehicle_x_;
342 auto y_diff_map = curr_vehicle_y - init_vehicle_y_;
343 auto theta_diff = curr_vehicle_heading - init_vehicle_heading_;
344
345 auto cos_map_veh = std::cos(init_vehicle_heading_);
346 auto sin_map_veh = std::sin(init_vehicle_heading_);
347
348 auto x_diff_veh = cos_map_veh * x_diff_map + sin_map_veh * y_diff_map;
349 auto y_diff_veh = -sin_map_veh * x_diff_map + cos_map_veh * y_diff_map;
350
351 auto cos_theta_diff = std::cos(-theta_diff);
352 auto sin_theta_diff = std::sin(-theta_diff);
353
354 auto tx = -(cos_theta_diff * x_diff_veh - sin_theta_diff * y_diff_veh);
355 auto ty = -(sin_theta_diff * x_diff_veh + cos_theta_diff * y_diff_veh);
356
357 auto ptr_trajectory_points =
358 target_tracking_trajectory.mutable_trajectory_point();
359 std::for_each(
360 ptr_trajectory_points->begin(), ptr_trajectory_points->end(),
361 [&cos_theta_diff, &sin_theta_diff, &tx, &ty,
362 &theta_diff](common::TrajectoryPoint &p) {
363 auto x = p.path_point().x();
364 auto y = p.path_point().y();
365 auto theta = p.path_point().theta();
366
367 auto x_new = cos_theta_diff * x - sin_theta_diff * y + tx;
368 auto y_new = sin_theta_diff * x + cos_theta_diff * y + ty;
369 auto theta_new = common::math::NormalizeAngle(theta - theta_diff);
370
371 p.mutable_path_point()->set_x(x_new);
372 p.mutable_path_point()->set_y(y_new);
373 p.mutable_path_point()->set_theta(theta_new);
374 });
375 }
376 }
377
379 std::move(TrajectoryAnalyzer(&target_tracking_trajectory));
380
381 // Transform the coordinate of the planning trajectory from the center of the
382 // rear-axis to the center of mass, if conditions matched
383 if (((lat_based_lqr_controller_conf_.trajectory_transform_to_com_reverse() &&
384 vehicle_state->gear() == canbus::Chassis::GEAR_REVERSE) ||
385 (lat_based_lqr_controller_conf_.trajectory_transform_to_com_drive() &&
386 vehicle_state->gear() == canbus::Chassis::GEAR_DRIVE)) &&
389 }
390
391 // Re-build the vehicle dynamic models at reverse driving (in particular,
392 // replace the lateral translational motion dynamics with the corresponding
393 // kinematic models)
394 if (vehicle_state->gear() == canbus::Chassis::GEAR_REVERSE) {
395 /*
396 A matrix (Gear Reverse)
397 [0.0, 0.0, 1.0 * v 0.0;
398 0.0, (-(c_f + c_r) / m) / v, (c_f + c_r) / m,
399 (l_r * c_r - l_f * c_f) / m / v;
400 0.0, 0.0, 0.0, 1.0;
401 0.0, ((lr * cr - lf * cf) / i_z) / v, (l_f * c_f - l_r * c_r) / i_z,
402 (-1.0 * (l_f^2 * c_f + l_r^2 * c_r) / i_z) / v;]
403 */
406 matrix_a_(0, 1) = 0.0;
407 matrix_a_coeff_(0, 2) = 1.0;
408 } else {
409 /*
410 A matrix (Gear Drive)
411 [0.0, 1.0, 0.0, 0.0;
412 0.0, (-(c_f + c_r) / m) / v, (c_f + c_r) / m,
413 (l_r * c_r - l_f * c_f) / m / v;
414 0.0, 0.0, 0.0, 1.0;
415 0.0, ((lr * cr - lf * cf) / i_z) / v, (l_f * c_f - l_r * c_r) / i_z,
416 (-1.0 * (l_f^2 * c_f + l_r^2 * c_r) / i_z) / v;]
417 */
420 matrix_a_(0, 1) = 1.0;
421 matrix_a_coeff_(0, 2) = 0.0;
422 }
423 matrix_a_(1, 2) = (cf_ + cr_) / mass_;
424 matrix_a_(3, 2) = (lf_ * cf_ - lr_ * cr_) / iz_;
425 matrix_a_coeff_(1, 1) = -(cf_ + cr_) / mass_;
426 matrix_a_coeff_(1, 3) = (lr_ * cr_ - lf_ * cf_) / mass_;
427 matrix_a_coeff_(3, 1) = (lr_ * cr_ - lf_ * cf_) / iz_;
428 matrix_a_coeff_(3, 3) = -1.0 * (lf_ * lf_ * cf_ + lr_ * lr_ * cr_) / iz_;
429
430 /*
431 b = [0.0, c_f / m, 0.0, l_f * c_f / i_z]^T
432 */
433 matrix_b_(1, 0) = cf_ / mass_;
434 matrix_b_(3, 0) = lf_ * cf_ / iz_;
436
438
439 SimpleLateralDebug *debug = cmd->mutable_debug()->mutable_simple_lat_debug();
440 debug->Clear();
441
442 // Update state = [Lateral Error, Lateral Error Rate, Heading Error, Heading
443 // Error Rate, preview lateral error1 , preview lateral error2, ...]
444 UpdateState(debug, chassis);
445
446 UpdateMatrix();
447
448 // Compound discrete matrix with road preview model
450
451 // Adjust matrix_q_updated when in reverse gear
452 int q_param_size = lat_based_lqr_controller_conf_.matrix_q_size();
453 int reverse_q_param_size =
454 lat_based_lqr_controller_conf_.reverse_matrix_q_size();
455 if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {
456 for (int i = 0; i < reverse_q_param_size; ++i) {
457 matrix_q_(i, i) = lat_based_lqr_controller_conf_.reverse_matrix_q(i);
458 }
459 } else {
460 for (int i = 0; i < q_param_size; ++i) {
461 matrix_q_(i, i) = lat_based_lqr_controller_conf_.matrix_q(i);
462 }
463 }
464
465 uint num_iteration;
466 double result_diff;
467 // Add gain scheduler for higher speed steering
468 if (FLAGS_enable_gain_scheduler) {
469 matrix_q_updated_(0, 0) =
470 matrix_q_(0, 0) * lat_err_interpolation_->Interpolate(
471 std::fabs(vehicle_state->linear_velocity()));
472 matrix_q_updated_(2, 2) =
473 matrix_q_(2, 2) * heading_err_interpolation_->Interpolate(
474 std::fabs(vehicle_state->linear_velocity()));
477 &matrix_k_, &num_iteration, &result_diff);
478 } else {
481 &matrix_k_, &num_iteration, &result_diff);
482 }
483
484 ADEBUG << "LQR num_iteration is " << num_iteration
485 << ", max iteration threshold is " << lqr_max_iteration_
486 << "; result_diff is " << result_diff;
487
488 // feedback = - K * state
489 // Convert vehicle steer angle from rad to degree and then to steer degree
490 // then to 100% ratio
491 const double steer_angle_feedback = -(matrix_k_ * matrix_state_)(0, 0) * 180 /
492 M_PI * steer_ratio_ /
494
495 const double steer_angle_feedforward = ComputeFeedForward(debug->curvature());
496
497 double steer_angle = 0.0;
498 double steer_angle_feedback_augment = 0.0;
499 // Augment the feedback control on lateral error at the desired speed domain
500 if (enable_leadlag_) {
502 .enable_feedback_augment_on_high_speed() ||
503 std::fabs(vehicle_state->linear_velocity()) < low_speed_bound_) {
504 steer_angle_feedback_augment =
505 leadlag_controller_.Control(-matrix_state_(0, 0), ts_) * 180 / M_PI *
507 if (std::fabs(vehicle_state->linear_velocity()) >
509 // Within the low-high speed transition window, linerly interplolate the
510 // augment control gain for "soft" control switch
511 steer_angle_feedback_augment = common::math::lerp(
512 steer_angle_feedback_augment, low_speed_bound_ - low_speed_window_,
513 0.0, low_speed_bound_, std::fabs(vehicle_state->linear_velocity()));
514 }
515 }
516 }
517 steer_angle = steer_angle_feedback + steer_angle_feedforward +
518 steer_angle_feedback_augment;
519
520 // Compute the steering command limit with the given maximum lateral
521 // acceleration
522 const double steer_limit =
523 FLAGS_set_steer_limit ? std::atan(max_lat_acc_ * wheelbase_ /
524 (vehicle_state->linear_velocity() *
525 vehicle_state->linear_velocity())) *
526 steer_ratio_ * 180 / M_PI /
528 : 100.0;
529
530 const double steer_diff_with_max_rate =
531 lat_based_lqr_controller_conf_.enable_maximum_steer_rate_limit()
532 ? vehicle_param_.max_steer_angle_rate() * ts_ * 180 / M_PI /
534 : 100.0;
535
536 const double steering_position = chassis->steering_percentage();
537
538 // Re-compute the steering command if the MRAC control is enabled, with steer
539 // angle limitation and steer rate limitation
540 if (enable_mrac_) {
541 const int mrac_model_order =
542 lat_based_lqr_controller_conf_.steer_mrac_conf().mrac_model_order();
543 Matrix steer_state = Matrix::Zero(mrac_model_order, 1);
544 steer_state(0, 0) = chassis->steering_percentage();
545 if (mrac_model_order > 1) {
546 steer_state(1, 0) = (steering_position - pre_steering_position_) / ts_;
547 }
548 if (std::fabs(vehicle_state->linear_velocity()) >
549 FLAGS_minimum_speed_resolution) {
552 } else {
555 }
556 steer_angle = mrac_controller_.Control(
557 steer_angle, steer_state, steer_limit, steer_diff_with_max_rate / ts_);
558 // Set the steer mrac debug message
559 MracDebug *mracdebug = debug->mutable_steer_mrac_debug();
561 mracdebug->set_mrac_model_order(mrac_model_order);
562 for (int i = 0; i < mrac_model_order; ++i) {
563 mracdebug->add_mrac_reference_state(steer_reference(i, 0));
564 mracdebug->add_mrac_state_error(steer_state(i, 0) -
565 steer_reference(i, 0));
566 mracdebug->mutable_mrac_adaptive_gain()->add_state_adaptive_gain(
568 }
569 mracdebug->mutable_mrac_adaptive_gain()->add_input_adaptive_gain(
571 mracdebug->set_mrac_reference_saturation_status(
573 mracdebug->set_mrac_control_saturation_status(
575 }
576 pre_steering_position_ = steering_position;
577 debug->set_steer_mrac_enable_status(enable_mrac_);
578
579 // Clamp the steer angle with steer limitations at current speed
580 double steer_angle_limited =
581 common::math::Clamp(steer_angle, -steer_limit, steer_limit);
582 steer_angle = steer_angle_limited;
583 debug->set_steer_angle_limited(steer_angle_limited);
584
585 // Limit the steering command with the designed digital filter
586 steer_angle = digital_filter_.Filter(steer_angle);
587 steer_angle = common::math::Clamp(steer_angle, -100.0, 100.0);
588
589 // Check if the steer is locked and hence the previous steer angle should be
590 // executed
591 if (injector_->vehicle_state()->gear() != canbus::Chassis::GEAR_REVERSE) {
592 if ((std::abs(vehicle_state->linear_velocity()) <
593 lat_based_lqr_controller_conf_.lock_steer_speed() ||
594 previous_lon_debug->path_remain() <= 0) &&
595 vehicle_state->gear() == canbus::Chassis::GEAR_DRIVE &&
597 ADEBUG << "Into lock steer, path_remain is "
598 << previous_lon_debug->path_remain() << "linear_velocity is "
599 << vehicle_state->linear_velocity();
600 steer_angle = pre_steer_angle_;
601 }
602 }
603
604 // Set the steer commands
605 cmd->set_steering_target(common::math::Clamp(
606 steer_angle, pre_steer_angle_ - steer_diff_with_max_rate,
607 pre_steer_angle_ + steer_diff_with_max_rate));
608 cmd->set_steering_rate(FLAGS_steer_angle_rate);
609
611
612 // compute extra information for logging and debugging
613 const double steer_angle_lateral_contribution =
614 -matrix_k_(0, 0) * matrix_state_(0, 0) * 180 / M_PI * steer_ratio_ /
616
617 const double steer_angle_lateral_rate_contribution =
618 -matrix_k_(0, 1) * matrix_state_(1, 0) * 180 / M_PI * steer_ratio_ /
620
621 const double steer_angle_heading_contribution =
622 -matrix_k_(0, 2) * matrix_state_(2, 0) * 180 / M_PI * steer_ratio_ /
624
625 const double steer_angle_heading_rate_contribution =
626 -matrix_k_(0, 3) * matrix_state_(3, 0) * 180 / M_PI * steer_ratio_ /
628
629 debug->set_heading(driving_orientation_);
630 debug->set_steer_angle(steer_angle);
631 debug->set_steer_angle_feedforward(steer_angle_feedforward);
632 debug->set_steer_angle_lateral_contribution(steer_angle_lateral_contribution);
633 debug->set_steer_angle_lateral_rate_contribution(
634 steer_angle_lateral_rate_contribution);
635 debug->set_steer_angle_heading_contribution(steer_angle_heading_contribution);
636 debug->set_steer_angle_heading_rate_contribution(
637 steer_angle_heading_rate_contribution);
638 debug->set_steer_angle_feedback(steer_angle_feedback);
639 debug->set_steer_angle_feedback_augment(steer_angle_feedback_augment);
640 debug->set_steering_position(steering_position);
641 debug->set_ref_speed(vehicle_state->linear_velocity());
642
643 ProcessLogs(debug, chassis);
644 return Status::OK();
645}
646
648 matrix_state_.setZero();
649 if (enable_mrac_) {
651 }
652 return Status::OK();
653}
654
656 const canbus::Chassis *chassis) {
657 auto vehicle_state = injector_->vehicle_state();
658 if (FLAGS_use_navigation_mode) {
660 0.0, 0.0, driving_orientation_, vehicle_state->linear_velocity(),
661 vehicle_state->angular_velocity(), vehicle_state->linear_acceleration(),
662 trajectory_analyzer_, debug, chassis);
663 } else {
664 // Transform the coordinate of the vehicle states from the center of the
665 // rear-axis to the center of mass, if conditions matched
666 const auto &com = vehicle_state->ComputeCOMPosition(lr_);
668 vehicle_state->linear_velocity(),
669 vehicle_state->angular_velocity(),
670 vehicle_state->linear_acceleration(),
671 trajectory_analyzer_, debug, chassis);
672 }
673
674 // State matrix update;
675 // First four elements are fixed;
677 matrix_state_(0, 0) = debug->lateral_error_feedback();
678 matrix_state_(2, 0) = debug->heading_error_feedback();
679 } else {
680 matrix_state_(0, 0) = debug->lateral_error();
681 matrix_state_(2, 0) = debug->heading_error();
682 }
683 matrix_state_(1, 0) = debug->lateral_error_rate();
684 matrix_state_(3, 0) = debug->heading_error_rate();
685
686 // Next elements are depending on preview window size;
687 for (int i = 0; i < preview_window_; ++i) {
688 const double preview_time = ts_ * (i + 1);
689 const auto preview_point =
691
692 const auto matched_point = trajectory_analyzer_.QueryNearestPointByPosition(
693 preview_point.path_point().x(), preview_point.path_point().y());
694
695 const double dx =
696 preview_point.path_point().x() - matched_point.path_point().x();
697 const double dy =
698 preview_point.path_point().y() - matched_point.path_point().y();
699
700 const double cos_matched_theta =
701 std::cos(matched_point.path_point().theta());
702 const double sin_matched_theta =
703 std::sin(matched_point.path_point().theta());
704 const double preview_d_error =
705 cos_matched_theta * dy - sin_matched_theta * dx;
706
707 matrix_state_(basic_state_size_ + i, 0) = preview_d_error;
708 }
709}
710
712 double v;
713 // At reverse driving, replace the lateral translational motion dynamics with
714 // the corresponding kinematic models
715 if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE &&
716 !lat_based_lqr_controller_conf_.reverse_use_dynamic_model()) {
717 v = std::min(injector_->vehicle_state()->linear_velocity(),
719 matrix_a_(0, 2) = matrix_a_coeff_(0, 2) * v;
720 } else {
721 v = std::max(injector_->vehicle_state()->linear_velocity(),
723 matrix_a_(0, 2) = 0.0;
724 }
725 matrix_a_(1, 1) = matrix_a_coeff_(1, 1) / v;
726 matrix_a_(1, 3) = matrix_a_coeff_(1, 3) / v;
727 matrix_a_(3, 1) = matrix_a_coeff_(3, 1) / v;
728 matrix_a_(3, 3) = matrix_a_coeff_(3, 3) / v;
729 Matrix matrix_i = Matrix::Identity(matrix_a_.cols(), matrix_a_.cols());
730 matrix_ad_ = (matrix_i - ts_ * 0.5 * matrix_a_).inverse() *
731 (matrix_i + ts_ * 0.5 * matrix_a_);
732}
733
735 // Initialize preview matrix
737 matrix_bdc_.block(0, 0, basic_state_size_, 1) = matrix_bd_;
738 if (preview_window_ > 0) {
739 matrix_bdc_(matrix_bdc_.rows() - 1, 0) = 1;
740 // Update A matrix;
741 for (int i = 0; i < preview_window_ - 1; ++i) {
743 }
744 }
745}
746
747double LatController::ComputeFeedForward(double ref_curvature) const {
748 const double kv =
749 lr_ * mass_ / 2 / cf_ / wheelbase_ - lf_ * mass_ / 2 / cr_ / wheelbase_;
750
751 // Calculate the feedforward term of the lateral controller; then change it
752 // from rad to %
753 const double v = injector_->vehicle_state()->linear_velocity();
754 double steer_angle_feedforwardterm;
755 if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE &&
756 !lat_based_lqr_controller_conf_.reverse_use_dynamic_model()) {
757 steer_angle_feedforwardterm =
758 lat_based_lqr_controller_conf_.reverse_feedforward_ratio() *
759 wheelbase_ * ref_curvature * 180 / M_PI * steer_ratio_ /
761 } else {
762 steer_angle_feedforwardterm =
763 (wheelbase_ * ref_curvature + kv * v * v * ref_curvature -
764 matrix_k_(0, 2) *
765 (lr_ * ref_curvature -
766 lf_ * mass_ * v * v * ref_curvature / 2 / cr_ / wheelbase_)) *
768 }
769
770 return steer_angle_feedforwardterm;
771}
772
774 const double x, const double y, const double theta, const double linear_v,
775 const double angular_v, const double linear_a,
776 const TrajectoryAnalyzer &trajectory_analyzer, SimpleLateralDebug *debug,
777 const canbus::Chassis *chassis) {
778 TrajectoryPoint target_point;
779
780 if (lat_based_lqr_controller_conf_.query_time_nearest_point_only()) {
781 target_point = trajectory_analyzer.QueryNearestPointByAbsoluteTime(
783 } else {
784 if (FLAGS_use_navigation_mode &&
786 .enable_navigation_mode_position_update()) {
787 target_point = trajectory_analyzer.QueryNearestPointByAbsoluteTime(
789 } else {
790 target_point = trajectory_analyzer.QueryNearestPointByPosition(x, y);
791 }
792 }
793 const double dx = x - target_point.path_point().x();
794 const double dy = y - target_point.path_point().y();
795
796 debug->mutable_current_target_point()->mutable_path_point()->set_x(
797 target_point.path_point().x());
798 debug->mutable_current_target_point()->mutable_path_point()->set_y(
799 target_point.path_point().y());
800
801 ADEBUG << "x point: " << x << " y point: " << y;
802 ADEBUG << "match point information : " << target_point.ShortDebugString();
803
804 const double cos_target_heading = std::cos(target_point.path_point().theta());
805 const double sin_target_heading = std::sin(target_point.path_point().theta());
806
807 double lateral_error = cos_target_heading * dy - sin_target_heading * dx;
808 if (lat_based_lqr_controller_conf_.enable_navigation_mode_error_filter()) {
809 lateral_error = lateral_error_filter_.Update(lateral_error);
810 }
811
812 debug->set_lateral_error(lateral_error);
813
814 debug->set_ref_heading(target_point.path_point().theta());
815 double heading_error =
817 if (lat_based_lqr_controller_conf_.enable_navigation_mode_error_filter()) {
818 heading_error = heading_error_filter_.Update(heading_error);
819 }
820 debug->set_heading_error(heading_error);
821
822 // Within the low-high speed transition window, linerly interplolate the
823 // lookahead/lookback station for "soft" prediction window switch
824 double lookahead_station = 0.0;
825 double lookback_station = 0.0;
826 if (std::fabs(linear_v) >= low_speed_bound_) {
827 lookahead_station = lookahead_station_high_speed_;
828 lookback_station = lookback_station_high_speed_;
829 } else if (std::fabs(linear_v) < low_speed_bound_ - low_speed_window_) {
830 lookahead_station = lookahead_station_low_speed_;
831 lookback_station = lookback_station_low_speed_;
832 } else {
833 lookahead_station = common::math::lerp(
835 lookahead_station_high_speed_, low_speed_bound_, std::fabs(linear_v));
836 lookback_station = common::math::lerp(
838 lookback_station_high_speed_, low_speed_bound_, std::fabs(linear_v));
839 }
840
841 // Estimate the heading error with look-ahead/look-back windows as feedback
842 // signal for special driving scenarios
843 double heading_error_feedback;
844 if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {
845 heading_error_feedback = heading_error;
846 } else {
847 auto lookahead_point = trajectory_analyzer.QueryNearestPointByRelativeTime(
848 target_point.relative_time() +
849 lookahead_station /
850 (std::max(std::fabs(linear_v), 0.1) * std::cos(heading_error)));
851 heading_error_feedback = common::math::NormalizeAngle(
852 heading_error + target_point.path_point().theta() -
853 lookahead_point.path_point().theta());
854 }
855 debug->set_heading_error_feedback(heading_error_feedback);
856
857 // Estimate the lateral error with look-ahead/look-back windows as feedback
858 // signal for special driving scenarios
859 double lateral_error_feedback;
860 if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {
861 lateral_error_feedback =
862 lateral_error - lookback_station * std::sin(heading_error);
863 } else {
864 lateral_error_feedback =
865 lateral_error + lookahead_station * std::sin(heading_error);
866 }
867 debug->set_lateral_error_feedback(lateral_error_feedback);
868
869 auto lateral_error_dot = linear_v * std::sin(heading_error);
870 auto lateral_error_dot_dot = linear_a * std::sin(heading_error);
871 if (FLAGS_reverse_heading_control) {
872 if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {
873 lateral_error_dot = -lateral_error_dot;
874 lateral_error_dot_dot = -lateral_error_dot_dot;
875 }
876 }
877 auto centripetal_acceleration =
878 linear_v * linear_v / wheelbase_ *
879 std::tan(chassis->steering_percentage() / 100 *
881 debug->set_lateral_error_rate(lateral_error_dot);
882 debug->set_lateral_acceleration(lateral_error_dot_dot);
883 debug->set_lateral_centripetal_acceleration(centripetal_acceleration);
884 debug->set_lateral_jerk(
887
888 if (injector_->vehicle_state()->gear() == canbus::Chassis::GEAR_REVERSE) {
889 debug->set_heading_rate(-angular_v);
890 } else {
891 debug->set_heading_rate(angular_v);
892 }
893 debug->set_ref_heading_rate(target_point.path_point().kappa() *
894 target_point.v());
895 debug->set_heading_error_rate(debug->heading_rate() -
896 debug->ref_heading_rate());
897
898 debug->set_heading_acceleration(
900 debug->set_ref_heading_acceleration(
902 debug->set_heading_error_acceleration(debug->heading_acceleration() -
903 debug->ref_heading_acceleration());
906
907 debug->set_heading_jerk(
909 debug->set_ref_heading_jerk(
911 ts_);
912 debug->set_heading_error_jerk(debug->heading_jerk() -
913 debug->ref_heading_jerk());
916
917 debug->set_curvature(target_point.path_point().kappa());
918}
919
921 auto vehicle_state = injector_->vehicle_state();
922 driving_orientation_ = vehicle_state->heading();
924 // Reverse the driving direction if the vehicle is in reverse mode
925 if (FLAGS_reverse_heading_control) {
926 if (vehicle_state->gear() == canbus::Chassis::GEAR_REVERSE) {
929 // Update Matrix_b for reverse mode
931 ADEBUG << "Matrix_b changed due to gear direction";
932 }
933 }
934}
935
936} // namespace control
937} // namespace apollo
void set_coefficients(const std::vector< double > &denominators, const std::vector< double > &numerators)
set denominators and numerators
double Filter(const double x_insert)
Processes a new measurement with the filter.
The MeanFilter class is used to smoothen a series of noisy numbers, such as sensor data or the output...
Definition mean_filter.h:45
double Update(const double measurement)
Processes a new measurement in amortized constant time.
A general class to denote the return status of an API call.
Definition status.h:43
static Status OK()
generate a success status.
Definition status.h:60
std::vector< std::pair< double, double > > DataType
double ComputeFeedForward(double ref_curvature) const
TrajectoryAnalyzer trajectory_analyzer_
common::MeanFilter lateral_error_filter_
void Stop() override
stop Lateral controller
void UpdateState(SimpleLateralDebug *debug, const canbus::Chassis *chassis)
virtual ~LatController()
destructor
void ProcessLogs(const SimpleLateralDebug *debug, const canbus::Chassis *chassis)
common::MeanFilter heading_error_filter_
void ComputeLateralErrors(const double x, const double y, const double theta, const double linear_v, const double angular_v, const double linear_a, const TrajectoryAnalyzer &trajectory_analyzer, SimpleLateralDebug *debug, const canbus::Chassis *chassis)
common::Status Init(std::shared_ptr< DependencyInjector > injector) override
initialize Lateral Controller
common::Status Reset() override
reset Lateral Controller
common::DigitalFilter digital_filter_
LatBaseLqrControllerConf lat_based_lqr_controller_conf_
std::string Name() const override
Lateral controller name
std::unique_ptr< Interpolation1D > heading_err_interpolation_
std::shared_ptr< DependencyInjector > injector_
LeadlagController leadlag_controller_
common::Status ComputeControlCommand(const localization::LocalizationEstimate *localization, const canbus::Chassis *chassis, const planning::ADCTrajectory *trajectory, ControlCommand *cmd) override
compute steering target based on current vehicle status and target trajectory
std::unique_ptr< Interpolation1D > lat_err_interpolation_
common::VehicleParam vehicle_param_
void Init(const LeadlagConf &leadlag_conf, const double dt)
initialize lead/lag controller
virtual double Control(const double error, const double dt)
compute control value based on the error
void SetInputAdaptionRate(const double ratio_input)
set convergence ratio for input components in adaptive dynamics
Eigen::MatrixXd CurrentInputAdaptionGain() const
get current input adaptive gain for mrac control
virtual double Control(const double command, const Eigen::MatrixXd state, const double input_limit, const double input_rate_limit)
compute control value based on the original command
void Init(const MracConf &mrac_conf, const common::LatencyParam &latency_param, const double dt)
initialize mrac controller
int ReferenceSaturationStatus() const
get saturation status for reference system
void Reset()
reset all the variables (including all the states, gains and externally-setting control parameters) f...
int ControlSaturationStatus() const
get saturation status for control system
void SetStateAdaptionRate(const double ratio_state)
set convergence ratio for state components in adaptive dynamics
Eigen::MatrixXd CurrentReferenceState() const
get current state for reference system
Eigen::MatrixXd CurrentStateAdaptionGain() const
get current state adaptive gain for mrac control
process point query and conversion related to trajectory
common::TrajectoryPoint QueryNearestPointByPosition(const double x, const double y) const
query a point of trajectory that its position is closest to the given position.
void TrajectoryTransformToCOM(const double rear_to_com_distance)
Transform the current trajectory points to the center of mass(COM) of the vehicle,...
common::TrajectoryPoint QueryNearestPointByAbsoluteTime(const double t) const
query a point of trajectory that its absolute time is closest to the give time.
common::TrajectoryPoint QueryNearestPointByRelativeTime(const double t) const
query a point of trajectory that its relative time is closest to the give time.
a singleton clock that can be used to get the current timestamp.
Definition clock.h:39
static double NowInSeconds()
gets the current time in second.
Definition clock.cc:56
Math-related util functions.
Defines the LatController class.
Linear interpolation functions.
Solver for discrete-time linear quadratic problem.
#define ACHECK(cond)
Definition log.h:80
#define ADEBUG
Definition log.h:41
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
T lerp(const T &x0, const double t0, const T &x1, const double t1, const double t)
Linear interpolation between two points of type T.
void SolveLQRProblem(const Matrix &A, const Matrix &B, const Matrix &Q, const Matrix &R, const Matrix &M, const double tolerance, const uint max_num_iteration, Matrix *ptr_K, uint *iterate_num, double *result_diff)
T Clamp(const T value, T bound1, T bound2)
Clamp a value between two bounds.
Definition math_utils.h:155
double QuaternionToHeading(const double qw, const double qx, const double qy, const double qz)
Definition quaternion.h:56
double NormalizeAngle(const double angle)
Normalize angle to [-PI, PI).
Definition math_utils.cc:53
void LpfCoefficients(const double ts, const double cutoff_freq, std::vector< double > *denominators, std::vector< double > *numerators)
Get low-pass coefficients for digital filter.
class register implement
Definition arena_queue.h:37
Contains a number of helper functions related to quaternions.
optional DrivingMode driving_mode
optional float steering_percentage
Definition chassis.proto:88
optional double timestamp_sec
Definition header.proto:9
optional LatencyParam steering_latency_param
optional double steer_angle_lateral_rate_contribution
optional double steer_angle_heading_rate_contribution
optional apollo::localization::Pose pose
optional apollo::common::Quaternion orientation
Definition pose.proto:31
optional double heading
Definition pose.proto:48
optional apollo::common::PointENU position
Definition pose.proto:26
optional apollo::common::Header header