Apollo 10.0
自动驾驶开放平台
mrac_controller.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2019 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 <cmath>
20#include <string>
21#include <vector>
22
23#include "Eigen/Dense"
24#include "Eigen/LU"
25#include "absl/strings/str_cat.h"
26
27#include "cyber/common/log.h"
29
30namespace apollo {
31namespace control {
32
36using Matrix = Eigen::MatrixXd;
37
38double MracController::Control(const double command, const Matrix state,
39 const double input_limit,
40 const double input_rate_limit) {
41 // check if the current sampling time is valid and the reference/adaption
42 // model well set up during the initialization
44 AERROR << "MRAC: model build failed; will work as a unity compensator. The "
45 "reference_model building status: "
47 << "; The adaption_model building status: "
48 << adaption_model_enabled_ << "; Current sampling time: " << ts_;
49 return command; // treat the mrac as a unity proportional controller
50 }
51
52 // update the state in the real actuation system
53 state_action_.col(0) = state;
54
55 // update the desired command in the real actuation system
56 input_desired_(0, 0) = command;
57
58 // update the command bounds for the real actuation system
59 bound_command_ = input_limit * bound_ratio_;
60 bound_command_rate_ = input_rate_limit * bound_ratio_;
61
62 // update the state in the reference system
64
65 double state_bounded = 0.0;
67 state_reference_(0, 0), state_reference_(0, 1), &state_bounded);
68 state_reference_(0, 0) = state_bounded;
69
70 // update the adaption laws including state adaption, command adaption and
71 // nonlinear components adaption
74
77
78 // revert the adaption law updates if they are beyond the clamping values
80 (gain_state_adaption_(0, 0) > 0.0 ||
83 gain_input_adaption_(0, 0) < 1.0)) {
86 }
87
88 // update the generated control based on the adaptive law
89 double control_unbounded =
90 gain_state_adaption_.col(0).transpose() * state_action_.col(0) +
92
93 double control = 0.0;
95 BoundOutput(control_unbounded, control_previous_, &control);
96
97 // update the anti-windup compensation if applied
98 AntiWindupCompensation(control_unbounded, control_previous_);
99
100 // update the previous value for next iteration
103 state_reference_.col(1) = state_reference_.col(0);
104 state_action_.col(1) = state_action_.col(0);
105 input_desired_.col(1) = input_desired_.col(0);
106 control_previous_ = control;
107 return control;
108}
109
111 // reset the overall states
112 ResetStates();
113 // reset the adaptive gains
114 ResetGains();
115 // reset all the externally-setting, non-conf control parameters
116 gamma_ratio_state_ = 1.0;
117 gamma_ratio_input_ = 1.0;
119}
120
122 // reset the inputs and outputs of the closed-loop MRAC controller
123 control_previous_ = 0.0;
124 input_desired_.setZero(1, 2);
125 // reset the internal states, anti-windup compensations and status
126 state_action_.setZero(model_order_, 2);
127 state_reference_.setZero(model_order_, 2);
131}
132
140
141void MracController::Init(const MracConf &mrac_conf,
142 const LatencyParam &latency_param, const double dt) {
143 control_previous_ = 0.0;
146 ts_ = dt;
147 // Initialize the common model parameters
148 model_order_ = mrac_conf.mrac_model_order();
149 // Initialize the saturation limits
151 // Initialize the system states
152 input_desired_ = Matrix::Zero(1, 2);
153 state_action_ = Matrix::Zero(model_order_, 2);
154 state_reference_ = Matrix::Zero(model_order_, 2);
155 // Initialize the adaptive control gains
156 gain_state_adaption_ = Matrix::Zero(model_order_, 2);
157 gain_input_adaption_ = Matrix::Ones(1, 2);
158 gain_nonlinear_adaption_ = Matrix::Zero(1, 2);
159 gain_state_clamping_ = Matrix::Zero(model_order_, 1);
160 gain_input_clamping_ = Matrix::Ones(1, 1);
161 gain_nonlinear_clamping_ = Matrix::Zero(1, 1);
162 gain_state_adaption_init_ = Matrix::Zero(model_order_, 1);
163 gain_input_adaption_init_ = Matrix::Ones(1, 1);
164 gain_nonlinear_adaption_init_ = Matrix::Zero(1, 1);
165 // Initialize the adaptive convergence gains and anti-windup gains
167 gamma_input_adaption_ = Matrix::Zero(1, 1);
168 gamma_nonlinear_adaption_ = Matrix::Zero(1, 1);
170 compensation_anti_windup_ = Matrix::Zero(model_order_, 2);
171 // Initialize the reference model parameters
173 matrix_b_reference_ = Matrix::Zero(model_order_, 1);
175 (SetReferenceModel(mrac_conf).ok() && BuildReferenceModel().ok());
176 // Initialize the adaption model parameters
178 matrix_b_adaption_ = Matrix::Zero(model_order_, 1);
180 (SetAdaptionModel(mrac_conf).ok() && BuildAdaptionModel().ok());
181 EstimateInitialGains(latency_param);
184}
185
187 const double Epsilon = 0.000001;
188 if (((mrac_conf.reference_time_constant() < Epsilon && model_order_ == 1)) ||
189 ((mrac_conf.reference_natural_frequency() < Epsilon &&
190 model_order_ == 2))) {
191 const auto error_msg = absl::StrCat(
192 "mrac controller error: reference model time-constant parameter: ",
193 mrac_conf.reference_time_constant(),
194 "and natural frequency parameter: ",
195 mrac_conf.reference_natural_frequency(),
196 " in configuration file are not reasonable with respect to the "
197 "reference model order: ",
199 AERROR << error_msg;
200 return Status(ErrorCode::CONTROL_INIT_ERROR, error_msg);
201 }
207 return Status::OK();
208}
209
211 const int p_size = mrac_conf.adaption_matrix_p_size();
212 const int x_size = mrac_conf.adaption_state_gain_size();
213 const int aw_size = mrac_conf.anti_windup_compensation_gain_size();
214 if (p_size != model_order_ * model_order_ || x_size > model_order_ ||
215 aw_size > model_order_) {
216 const auto error_msg = absl::StrCat(
217 "mrac controller error: adaption matrix p element number: ", p_size,
218 ", state gain number: ", x_size,
219 ", and anti-windup compensation gain number: ", aw_size,
220 " in configuration file do not match the model number: ", model_order_);
221 AERROR << error_msg;
222 return Status(ErrorCode::CONTROL_INIT_ERROR, error_msg);
223 }
224 for (int i = 0; i < model_order_; ++i) {
226 (i <= x_size - 1) ? mrac_conf.adaption_state_gain(i)
227 : mrac_conf.adaption_state_gain(x_size - 1);
228 gain_anti_windup_(i, i) =
229 (i <= aw_size - 1)
230 ? mrac_conf.anti_windup_compensation_gain(i)
231 : mrac_conf.anti_windup_compensation_gain(aw_size - 1);
232 for (int j = 0; j < model_order_; ++j) {
233 matrix_p_adaption_(i, j) =
234 mrac_conf.adaption_matrix_p(i * model_order_ + j);
235 }
236 }
239 return Status::OK();
240}
241
243 if (model_order_ > 2) {
244 const auto error_msg =
245 absl::StrCat("mrac controller error: reference model order ",
246 model_order_, " is beyond the designed range");
247 AERROR << error_msg;
248 return Status(ErrorCode::CONTROL_INIT_ERROR, error_msg);
249 }
250 if (model_order_ == 1) {
251 matrix_a_reference_(0, 0) = -1.0 / tau_reference_;
253 } else if (model_order_ == 2) {
254 matrix_a_reference_(0, 1) = 1.0;
258 }
259 return Status::OK();
260}
261
263 if (model_order_ > 2) {
264 const auto error_msg =
265 absl::StrCat("mrac controller error: adaption model order ",
266 model_order_, " is beyond the designed range");
267 AERROR << error_msg;
268 return Status(ErrorCode::CONTROL_INIT_ERROR, error_msg);
269 }
270 if (model_order_ == 1) {
271 matrix_b_adaption_(0, 0) = 1.0;
272 } else if (model_order_ == 2) {
274 }
276 const std::string error_msg =
277 "Solution of the algebraic Lyapunov equation is not symmetric positive "
278 "definite";
279 AERROR << error_msg;
280 return Status(ErrorCode::CONTROL_INIT_ERROR, error_msg);
281 }
282 return Status::OK();
283}
284
286 const Matrix matrix_p) const {
287 Matrix matrix_q = -matrix_p * matrix_a - matrix_a.transpose() * matrix_p;
288 Eigen::LLT<Matrix> llt_matrix_q(matrix_q);
289 // if matrix Q is not symmetric or the Cholesky decomposition (LLT) failed
290 // due to the matrix Q are not positive definite
291 return (matrix_q.isApprox(matrix_q.transpose()) &&
292 llt_matrix_q.info() != Eigen::NumericalIssue);
293}
294
296 const double rise_time_estimate =
297 latency_param.dead_time() + latency_param.rise_time();
298 const double settling_time_estimate =
299 latency_param.dead_time() + latency_param.settling_time();
300 Matrix matrix_a_estimate = Matrix::Zero(model_order_, model_order_);
301 Matrix matrix_b_estimate = Matrix::Zero(model_order_, 1);
302 Matrix matrix_a_clamping = Matrix::Zero(model_order_, model_order_);
303 Matrix matrix_b_clamping = Matrix::Zero(model_order_, 1);
304 if (model_order_ == 1 &&
305 (rise_time_estimate >= ts_ || settling_time_estimate >= ts_)) {
306 const double tau_estimate = (rise_time_estimate >= ts_)
307 ? rise_time_estimate / 2.2
308 : settling_time_estimate / 4.0;
309 // generate the initial adaptive gains
310 matrix_a_estimate(0, 0) = -1.0 / tau_estimate;
311 matrix_b_estimate(0, 0) = 1.0 / tau_estimate;
313 (matrix_a_reference_(0, 0) - matrix_a_estimate(0, 0)) /
314 matrix_b_estimate(0, 0);
316 matrix_b_reference_(0, 0) / matrix_b_estimate(0, 0);
317 // generate the clamping bounds for adaptive gains
319 matrix_a_clamping(0, 0) = -1.0 / tau_clamping_;
320 matrix_b_clamping(0, 0) = 1.0 / tau_clamping_;
322 (matrix_a_clamping(0, 0) - matrix_a_estimate(0, 0)) /
323 matrix_b_estimate(0, 0);
325 matrix_b_clamping(0, 0) / matrix_b_estimate(0, 0);
326 }
327 } else if (model_order_ == 2 &&
328 (rise_time_estimate >= ts_ && settling_time_estimate >= ts_)) {
329 const double wn_estimate = 1.8 / rise_time_estimate;
330 const double zeta_estimate =
331 4.6 / (rise_time_estimate * settling_time_estimate);
332 matrix_a_estimate(0, 1) = 1.0;
333 matrix_a_estimate(1, 0) = -wn_estimate * wn_estimate;
334 matrix_a_estimate(1, 1) = -2 * zeta_estimate * wn_estimate;
335 matrix_b_estimate(1, 0) = wn_estimate * wn_estimate;
337 (common::math::PseudoInverse<double, 2, 1>(matrix_b_estimate) *
338 (matrix_a_reference_ - matrix_a_estimate))
339 .transpose();
341 (common::math::PseudoInverse<double, 2, 1>(matrix_b_estimate) *
343 .transpose();
344 } else {
345 AWARN << "No pre-known actuation dynamics; the initial states of the "
346 "adaptive gains are set as zeros";
347 }
348}
349
351 Matrix matrix_i = Matrix::Identity(model_order_, model_order_);
352 state_reference_.col(0) =
353 (matrix_i - ts_ * 0.5 * matrix_a_reference_).inverse() *
354 ((matrix_i + ts_ * 0.5 * matrix_a_reference_) * state_reference_.col(1) +
355 ts_ * 0.5 * matrix_b_reference_ *
356 (input_desired_(0, 0) + input_desired_(0, 1)));
357}
358
359void MracController::UpdateAdaption(Matrix *law_adp, const Matrix state_adp,
360 const Matrix gain_adp) {
361 Matrix state_error = state_action_ - state_reference_;
362 law_adp->col(0) =
363 law_adp->col(1) -
364 0.5 * ts_ * gain_adp *
365 (state_adp.col(0) * (state_error.col(0).transpose() +
366 compensation_anti_windup_.col(0).transpose()) +
367 state_adp.col(1) * (state_error.col(1).transpose() +
368 compensation_anti_windup_.col(1).transpose())) *
370}
371
372void MracController::AntiWindupCompensation(const double control_command,
373 const double previous_command) {
374 Matrix offset_windup = Matrix::Zero(model_order_, 1);
375 offset_windup(0, 0) =
376 ((control_command > bound_command_) ? bound_command_ - control_command
377 : 0.0) +
378 ((control_command < -bound_command_) ? -bound_command_ - control_command
379 : 0.0);
380 if (model_order_ > 1) {
381 offset_windup(1, 0) =
382 ((control_command > previous_command + bound_command_rate_ * ts_)
383 ? bound_command_rate_ - (control_command - previous_command) / ts_
384 : 0.0) +
385 ((control_command < previous_command - bound_command_rate_ * ts_)
386 ? -bound_command_rate_ - (control_command - previous_command) / ts_
387 : 0.0);
388 }
390 compensation_anti_windup_.col(0) = gain_anti_windup_ * offset_windup;
391}
392
393int MracController::BoundOutput(const double output_unbounded,
394 const double previous_output, double *output) {
395 int status = 0;
396 if (output_unbounded > bound_command_ ||
397 output_unbounded > previous_output + bound_command_rate_ * ts_) {
398 *output = (bound_command_ < previous_output + bound_command_rate_ * ts_)
400 : previous_output + bound_command_rate_ * ts_;
401 // if output exceeds the upper bound, then status = 1; while if output
402 // changing rate exceeds the upper rate bound, then status = 2
403 status =
404 (bound_command_ < previous_output + bound_command_rate_ * ts_) ? 1 : 2;
405 } else if (output_unbounded < -bound_command_ ||
406 output_unbounded < previous_output - bound_command_rate_ * ts_) {
407 *output = (-bound_command_ > previous_output - bound_command_rate_ * ts_)
409 : previous_output - bound_command_rate_ * ts_;
410 // if output exceeds the lower bound, then status = -1; while if output
411 // changing rate exceeds the lower rate bound, then status = -2
412 status = (-bound_command_ > previous_output - bound_command_rate_ * ts_)
413 ? -1
414 : -2;
415 } else {
416 *output = output_unbounded;
417 // if output does not violate neither bound nor rate bound, then status = 0
418 status = 0;
419 }
420 return status;
421}
422
424 const Matrix &state_reference_init) {
425 if (state_reference_init.rows() != model_order_ ||
426 state_reference_init.cols() != 1) {
427 AWARN << "failed to set the initial reference states, due to the given "
428 "state size: "
429 << state_reference_init.rows() << " x " << state_reference_init.cols()
430 << " doesn't match the model order: " << model_order_;
431 } else {
432 state_reference_.col(1) = state_reference_init;
433 }
434}
435
436void MracController::SetInitialActionState(const Matrix &state_action_init) {
437 if (state_action_init.rows() != model_order_ ||
438 state_action_init.cols() != 1) {
439 AWARN << "failed to set the initial action states, due to the given "
440 "state size: "
441 << state_action_init.rows() << " x " << state_action_init.cols()
442 << " doesn't match the model order: " << model_order_;
443 } else {
444 state_action_.col(1) = state_action_init;
445 }
446}
447
448void MracController::SetInitialCommand(const double command_init) {
449 input_desired_(0, 1) = command_init;
450}
451
453 const Matrix &gain_state_adaption_init) {
454 if (gain_state_adaption_init.rows() != model_order_ ||
455 gain_state_adaption_init.cols() != 1) {
456 AWARN << "failed to set the initial state adaption gains, due to the given "
457 "state size: "
458 << gain_state_adaption_init.rows() << " x "
459 << gain_state_adaption_init.cols()
460 << " doesn't match the model order: " << model_order_;
461 } else {
462 gain_state_adaption_.col(1) = gain_state_adaption_init;
463 }
464}
465
467 const double gain_input_adaption_init) {
468 gain_input_adaption_(0, 1) = gain_input_adaption_init;
469}
470
472 const double gain_nonlinear_adaption_init) {
473 gain_nonlinear_adaption_(0, 1) = gain_nonlinear_adaption_init;
474}
475
476void MracController::SetStateAdaptionRate(const double ratio_state) {
477 if (ratio_state < 0.0) {
478 AWARN << "failed to set the state adaption rate, due to new ratio < 0; the "
479 "current ratio is still: "
481 } else {
482 gamma_ratio_state_ = ratio_state;
483 }
484}
485
486void MracController::SetInputAdaptionRate(const double ratio_input) {
487 if (ratio_input < 0.0) {
488 AWARN << "failed to set the input adaption rate, due to new ratio < 0; the "
489 "current ratio is still: "
491 } else {
492 gamma_ratio_input_ = ratio_input;
493 }
494}
495
496void MracController::SetNonlinearAdaptionRate(const double ratio_nonlinear) {
497 if (ratio_nonlinear < 0.0) {
498 AWARN << "failed to set the nonlinear adaption rate, due to new ratio < 0; "
499 "the current ratio is still: "
501 } else {
502 gamma_ratio_nonlinear_ = ratio_nonlinear;
503 }
504}
505
507
509
513
517
521
525
529
533
537
538} // namespace control
539} // namespace apollo
A general class to denote the return status of an API call.
Definition status.h:43
bool ok() const
check whether the return status is OK.
Definition status.h:67
static Status OK()
generate a success status.
Definition status.h:60
void SetInitialNonlinearAdaptionGain(const double gain_nonlinear_adaption_init)
set initial value of nonlinear adaption gain for mrac control
void ResetStates()
reset internal states for mrac controller
void SetInitialCommand(const double command_init)
set initial command (desired input)
common::Status BuildReferenceModel()
build mrac (1st or 2nd) order reference model in the discrete-time form, with the bilinear transform ...
common::Status BuildAdaptionModel()
build mrac (1st or 2nd) order adaptive dynamic model in the discrete-time form
void SetInitialActionState(const Eigen::MatrixXd &state_action_init)
set initial values for state components in actual actuator dynamics
common::Status SetAdaptionModel(const MracConf &mrac_conf)
state adaptive gain, desired adaptive gain and nonlinear-component adaptive gain
void SetInputAdaptionRate(const double ratio_input)
set convergence ratio for input components in adaptive dynamics
void EstimateInitialGains(const common::LatencyParam &latency_param)
estimate the initial states of the adaptive gains via known actuation dynamics approximation
double InputAdaptionRate() const
get convergence ratio for input components in adaptive dynamics
double NonlinearAdaptionRate() const
get convergence ratio for nonlinear components in adaptive dynamics
void SetNonlinearAdaptionRate(const double ratio_nonlinear)
set convergence ratio for nonlinear components in adaptive dynamics
Eigen::MatrixXd CurrentInputAdaptionGain() const
get current input adaptive gain for mrac control
void SetInitialInputAdaptionGain(const double gain_input_adaption_init)
set initial value of input adaption 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 ResetGains()
reset adaptive gains for mrac controller
void UpdateAdaption(Eigen::MatrixXd *law_adp, const Eigen::MatrixXd state_adp, const Eigen::MatrixXd gain_adp)
execute the adaption interation with respect to the designed law in discrete-time form,...
void Reset()
reset all the variables (including all the states, gains and externally-setting control parameters) f...
Eigen::MatrixXd CurrentNonlinearAdaptionGain() const
get current nonlinear adaptive gain for mrac control
common::Status SetReferenceModel(const MracConf &mrac_conf)
time constant, natural frequency and damping ratio
void AntiWindupCompensation(const double control_command, const double previous_command)
calculate the anti-windup compensation with respect to the integral windup issue
Eigen::MatrixXd gain_nonlinear_adaption_init_
int ControlSaturationStatus() const
get saturation status for control system
void SetInitialReferenceState(const Eigen::MatrixXd &state_reference_init)
set initial values for state components in reference model dynamics
double StateAdaptionRate() const
get convergence ratio for state components in adaptive dynamics
void SetStateAdaptionRate(const double ratio_state)
set convergence ratio for state components in adaptive dynamics
int BoundOutput(const double output_unbounded, const double previous_output, double *output_bounded)
bound the system output with the given bound and change-rate bound
bool CheckLyapunovPD(const Eigen::MatrixXd matrix_a, const Eigen::MatrixXd matrix_p) const
check if the solution of the algebraic Lyapunov Equation is symmetric positive definite
void SetInitialStateAdaptionGain(const Eigen::MatrixXd &gain_state_adaption_init)
set initial values of state adaption gains for mrac control
Eigen::MatrixXd CurrentReferenceState() const
get current state for reference system
Eigen::MatrixXd CurrentStateAdaptionGain() const
get current state adaptive gain for mrac control
void UpdateReference()
execute the reference state interation with respect to the designed inputs in discrete-time form,...
#define AERROR
Definition log.h:44
#define AWARN
Definition log.h:43
Defines some useful matrix operations.
class register implement
Definition arena_queue.h:37
repeated Vector rows
optional double adaption_nonlinear_gain
optional int32 mrac_model_order
optional double reference_damping_ratio
repeated double adaption_matrix_p
optional double reference_natural_frequency
optional double reference_time_constant
optional double clamping_time_constant
optional double adaption_desired_gain
repeated double adaption_state_gain
repeated double anti_windup_compensation_gain
optional double mrac_saturation_level