- 3.0.2 optimal control module.
GNRiccatiSolver-impl.hpp
Go to the documentation of this file.
1 /**********************************************************************************************************************
2 This file is part of the Control Toolbox (https://github.com/ethz-adrl/control-toolbox), copyright by ETH Zurich.
3 Licensed under the BSD-2 license (see LICENSE file in main directory)
4  **********************************************************************************************************************/
5 
6 #pragma once
7 
8 namespace ct {
9 namespace optcon {
10 
11 
12 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
13 GNRiccatiSolver<STATE_DIM, CONTROL_DIM, SCALAR>::GNRiccatiSolver(const std::shared_ptr<LQOCProblem_t>& lqocProblem)
14  : LQOCSolver<STATE_DIM, CONTROL_DIM, SCALAR>(lqocProblem), N_(-1)
15 {
16  Eigen::initParallel();
17  Eigen::setNbThreads(settings_.nThreadsEigen);
18 }
19 
20 
21 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
23 {
25 }
26 
27 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
29 {
30  for (int i = this->lqocProblem_->getNumberOfStages() - 1; i >= 0; i--)
32 }
33 
34 
35 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
37 {
38  if (N == this->lqocProblem_->getNumberOfStages() - 1)
40 
42 
43  if (N > 0)
44  computeCostToGo(N);
45 }
46 
47 
48 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
50 {
51  settings_ = settings;
52  H_corrFix_ = settings_.epsilon * ControlMatrix::Identity();
53 }
54 
55 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
57 {
58  LQOCProblem_t& p = *this->lqocProblem_;
59 
60  this->x_sol_[0].setZero(); // should always be zero (fixed init state)
61 
62  for (int k = 0; k < this->lqocProblem_->getNumberOfStages(); k++)
63  {
65  this->u_sol_[k] = this->lv_[k] + this->L_[k] * this->x_sol_[k];
66 
68  this->x_sol_[k + 1] = p.A_[k] * this->x_sol_[k] + p.B_[k] * (this->u_sol_[k]) + p.b_[k];
69  }
70 }
71 
72 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
74 { /*no action required, already computed in backward pass */
75 }
76 
77 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
79 { /*no action required, already computed in backward pass*/
80 }
81 
82 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
84 {
85  return smallestEigenvalue_;
86 }
87 
88 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
89 void GNRiccatiSolver<STATE_DIM, CONTROL_DIM, SCALAR>::setProblemImpl(std::shared_ptr<LQOCProblem_t> lqocProblem)
90 {
91  if (lqocProblem->isConstrained())
92  {
93  throw std::runtime_error(
94  "Selected wrong solver - GNRiccatiSolver cannot handle constrained problems. Use a different solver");
95  }
96 
97  const int& N = lqocProblem->getNumberOfStages();
99 }
100 
101 
102 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
104 {
105  if (N <= 0)
106  return;
107 
108  if (N_ == N)
109  return;
110 
111  gv_.resize(N);
112  G_.resize(N);
113 
114  H_.resize(N);
115  Hi_.resize(N);
116  Hi_inverse_.resize(N);
117 
118  this->lv_.resize(N);
119  this->L_.resize(N);
120 
121  this->x_sol_.resize(N + 1);
122  this->u_sol_.resize(N);
123 
124  sv_.resize(N + 1);
125  S_.resize(N + 1);
126 
127  N_ = N;
128 }
129 
130 
131 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
133 {
135  smallestEigenvalue_ = std::numeric_limits<SCALAR>::infinity();
136 
137  // initialize quadratic approximation of cost to go
138  const int& N = this->lqocProblem_->getNumberOfStages();
139  LQOCProblem_t& p = *this->lqocProblem_;
140 
141  S_[N] = p.Q_[N];
142  sv_[N] = p.qv_[N];
143 }
144 
145 
146 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
148 {
149  LQOCProblem_t& p = *this->lqocProblem_;
150 
151  S_[k] = p.Q_[k];
152  S_[k].noalias() += p.A_[k].transpose() * S_[k + 1] * p.A_[k];
153  S_[k].noalias() -= this->L_[k].transpose() * Hi_[k] * this->L_[k];
154 
155  S_[k] = 0.5 * (S_[k] + S_[k].transpose()).eval();
156 
157  sv_[k] = p.qv_[k];
158  sv_[k].noalias() += p.A_[k].transpose() * sv_[k + 1];
159  sv_[k].noalias() += p.A_[k].transpose() * S_[k + 1] * p.b_[k];
160  sv_[k].noalias() += this->L_[k].transpose() * Hi_[k] * this->lv_[k];
161  sv_[k].noalias() += this->L_[k].transpose() * gv_[k];
162  sv_[k].noalias() += G_[k].transpose() * this->lv_[k];
163 }
164 
165 
166 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
168 {
169  LQOCProblem_t& p = *this->lqocProblem_;
170 
171  gv_[k] = p.rv_[k];
172  gv_[k].noalias() += p.B_[k].transpose() * sv_[k + 1];
173  gv_[k].noalias() += p.B_[k].transpose() * S_[k + 1].template selfadjointView<Eigen::Lower>() * p.b_[k];
174 
175  G_[k] = p.P_[k];
176  //G_[k].noalias() += B_[k].transpose() * S_[k+1] * A_[k];
177  G_[k].noalias() += p.B_[k].transpose() * S_[k + 1].template selfadjointView<Eigen::Lower>() * p.A_[k];
178 
179  H_[k] = p.R_[k];
180  //H_[k].noalias() += B_[k].transpose() * S_[k+1] * B_[k];
181  H_[k].noalias() += p.B_[k].transpose() * S_[k + 1].template selfadjointView<Eigen::Lower>() * p.B_[k];
182 
184  {
185  if (settings_.epsilon > 1e-10)
186  Hi_[k] = H_[k] + settings_.epsilon * ControlMatrix::Identity();
187  else
188  Hi_[k] = H_[k];
189 
191  {
192  // compute eigenvalues with eigenvectors enabled
193  eigenvalueSolver_.compute(Hi_[k], Eigen::ComputeEigenvectors);
194  const ControlMatrix& V = eigenvalueSolver_.eigenvectors().real();
195  const ControlVector& lambda = eigenvalueSolver_.eigenvalues();
196 
197  smallestEigenvalue_ = std::min(smallestEigenvalue_, lambda.minCoeff());
198 
199  // Corrected Eigenvalue Matrix
200  ControlMatrix D = ControlMatrix::Zero();
201  // make D positive semi-definite (as described in IV. B.)
202  D.diagonal() = lambda.cwiseMax(settings_.epsilon);
203 
204  // reconstruct H
205  ControlMatrix Hi_regular = V * D * V.transpose();
206 
207  // invert D
208  ControlMatrix D_inverse = ControlMatrix::Zero();
209  // eigenvalue-wise inversion
210  D_inverse.diagonal() = -1.0 * D.diagonal().cwiseInverse();
211  ControlMatrix Hi_inverse_regular = V * D_inverse * V.transpose();
212 
213  if (!Hi_inverse_[k].isApprox(Hi_inverse_regular, 1e-4))
214  {
215  std::cout << "warning, inverses not identical at " << k << std::endl;
216  std::cout << "Hi_inverse_fixed - Hi_inverse_regular: " << std::endl
217  << Hi_inverse_[k] - Hi_inverse_regular << std::endl
218  << std::endl;
219  }
220  }
221 
222  Hi_inverse_[k] = -Hi_[k].template selfadjointView<Eigen::Lower>().llt().solve(ControlMatrix::Identity());
223 
224  // calculate FB gain update
225  this->L_[k].noalias() = Hi_inverse_[k].template selfadjointView<Eigen::Lower>() * G_[k];
226 
227  // calculate FF update
228  this->lv_[k].noalias() = Hi_inverse_[k].template selfadjointView<Eigen::Lower>() * gv_[k];
229  }
230  else
231  {
232  // compute eigenvalues with eigenvectors enabled
233  eigenvalueSolver_.compute(H_[k], Eigen::ComputeEigenvectors);
234  const ControlMatrix& V = eigenvalueSolver_.eigenvectors().real();
235  const ControlVector& lambda = eigenvalueSolver_.eigenvalues();
236 
238  {
239  smallestEigenvalue_ = std::min(smallestEigenvalue_, lambda.minCoeff());
240  }
241 
242  // Corrected Eigenvalue Matrix
243  ControlMatrix D = ControlMatrix::Zero();
244  // make D positive semi-definite (as described in IV. B.)
245  D.diagonal() = lambda.cwiseMax(settings_.epsilon);
246 
247  // reconstruct H
248  Hi_[k].noalias() = V * D * V.transpose();
249 
250  // invert D
251  ControlMatrix D_inverse = ControlMatrix::Zero();
252  // eigenvalue-wise inversion
253  D_inverse.diagonal() = -1.0 * D.diagonal().cwiseInverse();
254  Hi_inverse_[k].noalias() = V * D_inverse * V.transpose();
255 
256  // calculate FB gain update
257  this->L_[k].noalias() = Hi_inverse_[k] * G_[k];
258 
259  // calculate FF update
260  this->lv_[k].noalias() = Hi_inverse_[k] * gv_[k];
261  }
262 }
263 
264 
265 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
267 {
268 #ifdef MATLAB_FULL_LOG
269 
270  matFile_.open("GNRiccatiSolver.mat");
271 
272  matFile_.put("sv", sv_.toImplementation());
273  matFile_.put("S", S_.toImplementation());
274  matFile_.put("L", this->L_.toImplementation());
275  matFile_.put("H", H_.toImplementation());
276  matFile_.put("Hi_", Hi_.toImplementation());
277  matFile_.put("Hi_inverse", Hi_inverse_.toImplementation());
278  matFile_.put("G", G_.toImplementation());
279  matFile_.put("gv", gv_.toImplementation());
280 
281  matFile_.close();
282 #endif
283 }
284 
285 template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR>
287 {
288  // do nothing
289 }
290 
291 
292 } // namespace optcon
293 } // namespace ct
ct::core::StateControlMatrixArray< STATE_DIM, CONTROL_DIM, SCALAR > B_
Definition: LQOCProblem.hpp:207
ct::core::StateMatrixArray< STATE_DIM, SCALAR > A_
affine, time-varying system dynamics in discrete time
Definition: LQOCProblem.hpp:206
void changeNumberOfStages(int N)
Definition: GNRiccatiSolver-impl.hpp:103
ct::core::ControlMatrixArray< CONTROL_DIM, SCALAR > R_
Definition: LQOCProblem.hpp:219
ct::core::FeedbackArray< STATE_DIM, CONTROL_DIM, SCALAR > P_
LQ approximation of the cross terms of the cost function.
Definition: LQOCProblem.hpp:222
virtual void compute_lv() override
compute iLQR-style lv
Definition: GNRiccatiSolver-impl.hpp:78
size_t nThreadsEigen
number of threads, for MP version
Definition: NLOptConSettings.hpp:274
ControlMatrix H_corrFix_
Definition: GNRiccatiSolver.hpp:88
virtual void setProblemImpl(std::shared_ptr< LQOCProblem_t > lqocProblem) override
Definition: GNRiccatiSolver-impl.hpp:89
SCALAR smallestEigenvalue_
Definition: GNRiccatiSolver.hpp:95
ct::core::FeedbackArray< STATE_DIM, CONTROL_DIM, SCALAR > L_
Definition: LQOCSolver.hpp:111
ct::core::StateVectorArray< STATE_DIM, SCALAR > b_
Definition: LQOCProblem.hpp:208
virtual void solve() override
solve the LQOC problem
Definition: GNRiccatiSolver-impl.hpp:28
virtual SCALAR getSmallestEigenvalue() override
return the smallest eigenvalue
Definition: GNRiccatiSolver-impl.hpp:83
virtual void configure(const NLOptConSettings &settings) override
Definition: GNRiccatiSolver-impl.hpp:49
double epsilon
the prefix to be stored before the matfile name for logging
Definition: NLOptConSettings.hpp:261
ct::core::StateMatrixArray< STATE_DIM, SCALAR > Q_
Definition: LQOCProblem.hpp:215
Settings for the NLOptCon algorithm.
Definition: NLOptConSettings.hpp:198
CppAD::AD< CppAD::cg::CG< double > > SCALAR
std::shared_ptr< LQOCProblem_t > lqocProblem_
Definition: LQOCSolver.hpp:107
void initializeCostToGo()
Definition: GNRiccatiSolver-impl.hpp:132
void designController(size_t k)
Definition: GNRiccatiSolver-impl.hpp:167
Defines a Linear-Quadratic Optimal Control Problem, which is optionally constrained.
Definition: LQOCProblem.hpp:57
void logToMatlab()
Definition: GNRiccatiSolver-impl.hpp:266
GNRiccatiSolver(const std::shared_ptr< LQOCProblem_t > &lqocProblem=nullptr)
Definition: GNRiccatiSolver-impl.hpp:13
for i
Definition: mpc_unittest_plotting.m:14
bool fixedHessianCorrection
the maximum admissible number of NLOptCon main iterations
Definition: NLOptConSettings.hpp:270
ControlMatrixArray H_
Definition: GNRiccatiSolver.hpp:85
StateMatrixArray S_
Definition: GNRiccatiSolver.hpp:91
ControlMatrixArray Hi_inverse_
Definition: GNRiccatiSolver.hpp:87
core::ControlVectorArray< CONTROL_DIM, SCALAR > u_sol_
Definition: LQOCSolver.hpp:110
void computeCostToGo(size_t k)
Definition: GNRiccatiSolver-impl.hpp:147
virtual void computeStatesAndControls() override
extract the solution (can be overriden if additional extraction steps required in specific solver) ...
Definition: GNRiccatiSolver-impl.hpp:56
virtual void computeFeedbackMatrices() override
return TVLQR feedback matrices
Definition: GNRiccatiSolver-impl.hpp:73
ct::core::ControlVectorArray< CONTROL_DIM, SCALAR > lv_
Definition: LQOCSolver.hpp:112
ControlMatrixArray Hi_
Definition: GNRiccatiSolver.hpp:86
ct::core::ControlVectorArray< CONTROL_DIM, SCALAR > rv_
LQ approximation of the pure control penalty.
Definition: LQOCProblem.hpp:218
ct::core::StateVectorArray< STATE_DIM, SCALAR > qv_
LQ approximation of the pure state penalty, including terminal state penalty.
Definition: LQOCProblem.hpp:214
FeedbackArray G_
Definition: GNRiccatiSolver.hpp:83
bool recordSmallestEigenvalue
perform Hessian regularization by incrementing the eigenvalues by epsilon.
Definition: NLOptConSettings.hpp:271
ControlVectorArray gv_
Definition: GNRiccatiSolver.hpp:82
virtual void initializeAndAllocate() override
a method reserved for memory allocation (e.g. required for HPIPM)
Definition: GNRiccatiSolver-impl.hpp:286
int N_
Definition: GNRiccatiSolver.hpp:93
Eigen::SelfAdjointEigenSolver< Eigen::Matrix< SCALAR, CONTROL_DIM, CONTROL_DIM > > eigenvalueSolver_
Eigenvalue solver, used for inverting the Hessian and for regularization.
Definition: GNRiccatiSolver.hpp:98
core::StateVectorArray< STATE_DIM, SCALAR > x_sol_
Definition: LQOCSolver.hpp:109
NLOptConSettings settings_
Definition: GNRiccatiSolver.hpp:80
virtual void solveSingleStage(int N) override
Definition: GNRiccatiSolver-impl.hpp:36
StateVectorArray sv_
Definition: GNRiccatiSolver.hpp:90
Definition: LQOCSolver.hpp:22