centroidal_control_collection
PreviewControl.h
Go to the documentation of this file.
1 /* Author: Masaki Murooka */
2 
3 #pragma once
4 
5 #include <memory>
6 
7 #include <CCC/StateSpaceModel.h>
8 #include <CCC/console.h>
9 
10 namespace CCC
11 {
21 template<int StateDim, int InputDim, int OutputDim>
23 {
24 public:
26  using StateDimVector = Eigen::Matrix<double, StateDim, 1>;
27 
29  using InputDimVector = Eigen::Matrix<double, InputDim, 1>;
30 
32  using OutputDimVector = Eigen::Matrix<double, OutputDim, 1>;
33 
36 
37 public:
39  struct WeightParam
40  {
43 
46 
51  WeightParam(const OutputDimVector & _output = OutputDimVector::Zero(),
52  const InputDimVector & _input = InputDimVector::Zero())
53  : output(_output), input(_input)
54  {
55  }
56  };
57 
58 public:
59  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
60 
67  PreviewControl(const std::shared_ptr<_StateSpaceModel> & model,
68  double horizon_duration,
69  double horizon_dt,
70  const WeightParam & weight_param)
71  : model_(model), horizon_dt_(horizon_dt), horizon_steps_(static_cast<int>(std::ceil(horizon_duration / horizon_dt)))
72  {
73  if(horizon_duration <= 0 || horizon_dt <= 0)
74  {
75  throw std::runtime_error("[PreviewControl] Input arguments are invalid. horizon_duration: "
76  + std::to_string(horizon_duration) + ", horizon_dt: " + std::to_string(horizon_dt));
77  }
78 
79  calcGain(weight_param);
80  }
81 
86  InputDimVector calcOptimalInput(const StateDimVector & x, const Eigen::VectorXd & ref_output_seq) const
87  {
88  return -K_ * x + F_ * ref_output_seq;
89  }
90 
91 protected:
93  void calcGain(const WeightParam & weight_param)
94  {
95  // 0. Calculate discrete system matrices
96  if(model_->dt_ != horizon_dt_)
97  {
98  model_->calcDiscMatrix(horizon_dt_);
99  }
100 
101  const Eigen::Matrix<double, StateDim, StateDim> & A = model_->Ad_;
102  const Eigen::Matrix<double, StateDim, InputDim> & B = model_->Bd_;
103  const Eigen::Matrix<double, OutputDim, StateDim> & C = model_->C_;
104 
105  Eigen::Matrix<double, OutputDim, OutputDim> Q = weight_param.output.asDiagonal();
106  Eigen::Matrix<double, InputDim, InputDim> R = weight_param.input.asDiagonal();
107  Eigen::Matrix<double, InputDim, InputDim> Rinv = weight_param.input.cwiseInverse().asDiagonal();
108 
109  // int stateDim = model_->stateDim();
110  int inputDim = model_->inputDim();
111  int outputDim = model_->outputDim();
112 
113  // 1. Calculate P by solving the Discrete-time Algebraic Riccati Equation
114  // (DARE) ref. https://scicomp.stackexchange.com/a/35394
115  int iterMax = 10000;
116  double relNormThre = 1e-8;
117  Eigen::Matrix<double, StateDim, StateDim> A0 = A;
118  Eigen::Matrix<double, StateDim, StateDim> G0 = B * Rinv * B.transpose();
119  Eigen::Matrix<double, StateDim, StateDim> H0 = C.transpose() * Q * C;
120  Eigen::Matrix<double, StateDim, StateDim> A1, G1, H1;
121  for(int i = 0; i < iterMax; i++)
122  {
123  Eigen::Matrix<double, StateDim, StateDim> I_GH_inv =
124  (Eigen::Matrix<double, StateDim, StateDim>::Identity() + G0 * H0).inverse();
125  A1 = A0 * I_GH_inv * A0;
126  G1 = G0 + A0 * I_GH_inv * G0 * A0.transpose();
127  H1 = H0 + A0.transpose() * H0 * I_GH_inv * A0;
128 
129  double relNorm = (H1 - H0).norm() / H1.norm();
130  if(relNorm < relNormThre)
131  {
132  break;
133  }
134  else if(i == iterMax - 1)
135  {
136  CCC_WARN_STREAM("[PreviewControl] Solution of Riccati equation did not converged: " << relNorm << " > "
137  << relNormThre);
138  }
139 
140  A0 = A1;
141  G0 = G1;
142  H0 = H1;
143  }
144  P_ = H1;
145 
146  riccati_error_ = (P_
147  - (A.transpose() * P_ * A + C.transpose() * Q * C
148  - A.transpose() * P_ * B * (R + B.transpose() * P_ * B).inverse() * B.transpose() * P_ * A))
149  .norm();
150 
151  // 2. Calculate the feedback gain (K and f)
152  Eigen::Matrix<double, InputDim, InputDim> R_BtPB_inv = (R + B.transpose() * P_ * B).inverse();
153  // 2.1 Calculate K
154  K_ = R_BtPB_inv * B.transpose() * P_ * A;
155  // 2.2 Calculate f
156  F_.resize(inputDim, horizon_steps_ * outputDim);
157  Eigen::Matrix<double, StateDim, StateDim> A_BK = A - B * K_;
158  Eigen::Matrix<double, StateDim, StateDim> fSub = Eigen::Matrix<double, StateDim, StateDim>::Identity();
159  for(int i = 0; i < horizon_steps_; i++)
160  {
161  if(i < horizon_steps_ - 1)
162  {
163  F_.middleCols(i * outputDim, outputDim) = R_BtPB_inv * B.transpose() * fSub * C.transpose() * Q;
164  }
165  else
166  {
167  // See https://github.com/euslisp/jskeus/pull/551
168  F_.middleCols(i * outputDim, outputDim) = R_BtPB_inv * B.transpose() * fSub * P_ * C.transpose();
169  }
170  fSub = fSub * A_BK.transpose();
171  }
172  }
173 
174 public:
176  std::shared_ptr<_StateSpaceModel> model_;
177 
179  double horizon_dt_ = 0;
180 
182  int horizon_steps_ = -1;
183 
185  double riccati_error_ = 0;
186 
187 protected:
189  Eigen::Matrix<double, StateDim, StateDim> P_;
190 
192  Eigen::Matrix<double, InputDim, StateDim> K_;
193 
195  Eigen::Matrix<double, InputDim, Eigen::Dynamic> F_;
196 };
197 } // namespace CCC
CCC_WARN_STREAM
#define CCC_WARN_STREAM
Definition: console.h:11
CCC::PreviewControl::calcOptimalInput
InputDimVector calcOptimalInput(const StateDimVector &x, const Eigen::VectorXd &ref_output_seq) const
Calculate optimal input.
Definition: PreviewControl.h:86
CCC::PreviewControl::horizon_dt_
double horizon_dt_
Discretization timestep in horizon [sec].
Definition: PreviewControl.h:179
CCC::PreviewControl::WeightParam::WeightParam
WeightParam(const OutputDimVector &_output=OutputDimVector::Zero(), const InputDimVector &_input=InputDimVector::Zero())
Constructor.
Definition: PreviewControl.h:51
CCC::PreviewControl::F_
Eigen::Matrix< double, InputDim, Eigen::Dynamic > F_
Preview gain.
Definition: PreviewControl.h:195
CCC::PreviewControl::P_
Eigen::Matrix< double, StateDim, StateDim > P_
Solution of the algebraic Riccati equation.
Definition: PreviewControl.h:189
CCC::PreviewControl::PreviewControl
EIGEN_MAKE_ALIGNED_OPERATOR_NEW PreviewControl(const std::shared_ptr< _StateSpaceModel > &model, double horizon_duration, double horizon_dt, const WeightParam &weight_param)
Constructor.
Definition: PreviewControl.h:67
console.h
CCC::PreviewControl::calcGain
void calcGain(const WeightParam &weight_param)
Calculate the gain of the preview control.
Definition: PreviewControl.h:93
CCC::PreviewControl::riccati_error_
double riccati_error_
Error of algebraic Riccati equation.
Definition: PreviewControl.h:185
CCC::PreviewControl< 3, 1, 1 >::InputDimVector
Eigen::Matrix< double, InputDim, 1 > InputDimVector
Type of input vector.
Definition: PreviewControl.h:29
CCC::PreviewControl::K_
Eigen::Matrix< double, InputDim, StateDim > K_
Feedback gain.
Definition: PreviewControl.h:192
CCC::PreviewControl::WeightParam::output
OutputDimVector output
Output weight.
Definition: PreviewControl.h:42
CCC::PreviewControl< 3, 1, 1 >::OutputDimVector
Eigen::Matrix< double, OutputDim, 1 > OutputDimVector
Type of output vector.
Definition: PreviewControl.h:32
CCC::StateSpaceModel
State-space model.
Definition: StateSpaceModel.h:21
CCC
Definition: CommonModels.h:7
CCC::PreviewControl::model_
std::shared_ptr< _StateSpaceModel > model_
State-space model.
Definition: PreviewControl.h:176
CCC::PreviewControl::WeightParam::input
InputDimVector input
Input weight.
Definition: PreviewControl.h:45
CCC::PreviewControl::horizon_steps_
int horizon_steps_
Number of steps in horizon.
Definition: PreviewControl.h:182
CCC::PreviewControl
Preview control.
Definition: PreviewControl.h:22
CCC::PreviewControl::WeightParam
Weight parameter.
Definition: PreviewControl.h:39
StateSpaceModel.h
CCC::PreviewControl< 3, 1, 1 >::StateDimVector
Eigen::Matrix< double, StateDim, 1 > StateDimVector
Type of state vector.
Definition: PreviewControl.h:26