algebraic_riccati_discrete.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement
4  *
5  * Copyright (c) 2020,
6  * TU Dortmund - Institute of Control Theory and Systems Engineering.
7  * All rights reserved.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <https://www.gnu.org/licenses/>.
21  *
22  * Authors: Christoph Rösmann
23  *********************************************************************/
24 
26 
29 #include <corbo-numerics/schur.h>
30 
31 #include <corbo-core/console.h>
32 
33 #include <Eigen/Cholesky>
34 #include <Eigen/Core>
35 #include <Eigen/Dense>
36 #include <algorithm>
37 
38 namespace corbo {
39 
41  const Eigen::Ref<const Eigen::MatrixXd>& Q, const Eigen::Ref<const Eigen::MatrixXd>& R, Eigen::MatrixXd& X,
42  Eigen::MatrixXd* G)
43 {
44  assert(is_square(A));
45  assert(have_equal_size(A, Q));
46  assert(A.rows() == B.rows());
47  assert(R.rows() == B.cols());
48  assert(is_square(R));
49  const int p = A.rows(); // dim_states
50 
51  Eigen::LDLT<Eigen::MatrixXd> R_cholesky(R);
52  Eigen::MatrixXd R_inv_B_t = R_cholesky.solve(B.transpose());
53  Eigen::MatrixXd A_inv_trans = A.inverse().transpose(); // A must be invertible
54 
55  // Construct Hamiltonian matrix
56  // H = [ A+B R^-1 B^T (A^-1)^T Q -B R^-1 B^T (A^-1)^T]
57  // [-(A^-1)^T Q (A^-1)^T ]
58  Eigen::MatrixXd H(2 * p, 2 * p);
59  H << A + B * R_inv_B_t * A_inv_trans * Q, -B * R_inv_B_t * A_inv_trans, -A_inv_trans * Q, A_inv_trans;
60 
61  bool success = solveRiccatiHamiltonianSchur(H, X);
62 
63  if (G && success)
64  {
65  Eigen::LDLT<Eigen::MatrixXd> G_cholesky(R + B.transpose() * X * B);
66  *G = G_cholesky.solve(B.transpose()) * X * A;
67  }
68  return success;
69 }
70 
71 bool AlgebraicRiccatiDiscrete::solveRiccatiHamiltonianSchur(const Eigen::MatrixXd& H, Eigen::MatrixXd& X)
72 {
73  assert(is_square(H));
74  assert(H.rows() % 2 == 0);
75 
76  // perform schur to find an orthogonal transformation that reduces H to real Schur form
78 
79  Eigen::MatrixXd T = schur.matrixT();
80  Eigen::MatrixXd U = schur.matrixU();
81 
82  // reorder Schur form such that all negative eigenvalues are located on the upper left blocks.
83  int subspace_dim;
84  bool success = reorder_schur_blocks(T, U, AlgebraicRiccatiDiscrete::isInsideUnitCircle, &subspace_dim, false);
85 
86  const int p = H.rows() / 2;
87 
88  if (!success || p != subspace_dim) return false;
89 
90  // U = [U1; U2
91  // U2; U3]
92 
93  // solution X can be obtained by X = U2 * U1^-1
94  // since U1.inverse() is not recomended due to numerical effects
95  // we must rewrite the equation in order to facilitate solving with Eigen:
96  // X^T = (U2 * U1^-1)^T = (U1^T)^(-1) U2^T
97  Eigen::JacobiSVD<Eigen::MatrixXd> U1_svd(U.block(0, 0, p, p).transpose(), Eigen::ComputeThinU | Eigen::ComputeThinV);
98  X = U1_svd.solve(U.block(p, 0, p, p).transpose()); // X is symmetric, hence no final transpose
99  return true;
100 }
101 
104 {
105  assert(is_square(A));
106  assert(A.rows() == B.rows());
107  assert(A.rows() == G.cols());
108  assert(G.rows() == B.cols());
109 
110  Eigen::VectorXcd eig = (A - B * G).eigenvalues();
111  for (int i = 0; i < eig.size(); ++i)
112  {
113  if (std::abs(eig[i]) >= 1) return false;
114  }
115  return true;
116 }
117 
118 } // namespace corbo
corbo::have_equal_size
bool have_equal_size(const Eigen::MatrixBase< DerivedA > &matrix1, const Eigen::MatrixBase< DerivedB > &matrix2)
Determine if two matrices exhibit equal sizes/dimensions.
Definition: matrix_utilities.h:93
algebraic_riccati_discrete.h
corbo
Definition: communication/include/corbo-communication/utilities.h:37
corbo::AlgebraicRiccatiDiscrete::isClosedLoopStable
static bool isClosedLoopStable(const Eigen::Ref< const Eigen::MatrixXd > &A, const Eigen::Ref< const Eigen::MatrixXd > &B, const Eigen::Ref< const Eigen::MatrixXd > &G)
Determine if the closed-loop system is stable.
Definition: algebraic_riccati_discrete.cpp:124
X
#define X
Definition: icosphere.cpp:20
console.h
value_comparison.h
Eigen::LDLT::solve
const Solve< LDLT, Rhs > solve(const MatrixBase< Rhs > &b) const
Definition: LDLT.h:200
abs
EIGEN_DEVICE_FUNC const EIGEN_STRONG_INLINE AbsReturnType abs() const
Definition: ArrayCwiseUnaryOps.h:43
Eigen::RealSchur
Performs a real Schur decomposition of a square matrix.
Definition: RealSchur.h:54
corbo::is_square
bool is_square(const Eigen::MatrixBase< Derived > &matrix)
Determine if a given matrix is square.
Definition: matrix_utilities.h:65
B
MatrixType B(b, *n, *nrhs, *ldb)
corbo::AlgebraicRiccatiDiscrete::solve
static bool solve(const Eigen::Ref< const Eigen::MatrixXd > &A, const Eigen::Ref< const Eigen::MatrixXd > &B, const Eigen::Ref< const Eigen::MatrixXd > &Q, const Eigen::Ref< const Eigen::MatrixXd > &R, Eigen::MatrixXd &X, Eigen::MatrixXd *G=nullptr)
Solve discrete-time algebraic Riccati equation.
Definition: algebraic_riccati_discrete.cpp:62
Eigen::ComputeThinU
@ ComputeThinU
Definition: Constants.h:385
matrix_utilities.h
Eigen::LDLT
Robust Cholesky decomposition of a matrix with pivoting.
Definition: LDLT.h:50
eig
SelfAdjointEigenSolver< PlainMatrixType > eig(mat, computeVectors?ComputeEigenvectors:EigenvaluesOnly)
Eigen::JacobiSVD
Two-sided Jacobi SVD decomposition of a rectangular matrix.
Definition: ForwardDeclarations.h:258
Eigen::ComputeThinV
@ ComputeThinV
Definition: Constants.h:389
Eigen::Ref
A matrix or vector expression mapping an existing expression.
Definition: Ref.h:192
schur.h
A
MatrixType A(a, *n, *n, *lda)
corbo::AlgebraicRiccatiDiscrete::solveRiccatiHamiltonianSchur
static bool solveRiccatiHamiltonianSchur(const Eigen::MatrixXd &H, Eigen::MatrixXd &X)
Solve Hamiltonian via Schur method.
Definition: algebraic_riccati_discrete.cpp:93
corbo::AlgebraicRiccatiDiscrete::isInsideUnitCircle
static bool isInsideUnitCircle(const Eigen::Ref< const Eigen::MatrixXd > &block)
Definition: algebraic_riccati_discrete.h:176
corbo::reorder_schur_blocks
bool reorder_schur_blocks(Eigen::Ref< Eigen::MatrixXd > T, Eigen::Ref< Eigen::MatrixXd > Q, Predicate predicate, int *subspace_dim, bool standardize)
Definition: schur.hpp:57


control_box_rst
Author(s): Christoph Rösmann
autogenerated on Wed Mar 2 2022 00:05:36