rm_control
Loading...
Searching...
No Matches
lqr.h
Go to the documentation of this file.
1/*******************************************************************************
2 * BSD 3-Clause License
3 *
4 * Copyright (c) 2021, Qiayuan Liao
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * * Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE
25 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *******************************************************************************/
33
34//
35// Created by chenzheng on 3/20/21.
36//
37
38#pragma once
39
40#include <iostream>
41#include <Eigen/Dense>
42#include "eigen_types.h"
43
44template <typename T>
45class Lqr
46{
47public:
48 template <typename TA, typename TB, typename TQ, typename TR>
49 Lqr(const Eigen::MatrixBase<TA>& A, const Eigen::MatrixBase<TB>& B, const Eigen::MatrixBase<TQ>& Q,
50 const Eigen::MatrixBase<TR>& R)
51 : a_(A), b_(B), q_(Q), r_(R)
52 {
53 // check A
54 static_assert(TA::RowsAtCompileTime == TA::ColsAtCompileTime, "lqr: A should be square matrix");
55 // check B
56 static_assert(TB::RowsAtCompileTime == TA::RowsAtCompileTime, "lqr: B rows should be equal to A rows");
57 // check Q
58 static_assert(TQ::RowsAtCompileTime == TA::RowsAtCompileTime && TQ::ColsAtCompileTime == TA::ColsAtCompileTime,
59 "lqr: The rows and columns of Q should be equal to A");
60 // check R
61 static_assert(TR::RowsAtCompileTime == TB::ColsAtCompileTime && TR::ColsAtCompileTime == TB::ColsAtCompileTime,
62 "lqr: The rows and columns of R should be equal to the cols of B");
63
64 k_.resize(TB::ColsAtCompileTime, TB::RowsAtCompileTime);
65 k_.setZero();
66 }
67
68 bool solveRiccatiArimotoPotter(const Eigen::MatrixXd& A, const Eigen::MatrixXd& B, const Eigen::MatrixXd& Q,
69 const Eigen::MatrixXd& R, Eigen::MatrixXd& P)
70 {
71 int dim_x = A.rows();
72
73 // set Hamilton matrix
74 Eigen::MatrixXd ham = Eigen::MatrixXd::Zero(2 * dim_x, 2 * dim_x);
75 ham << A, -B * R.inverse() * B.transpose(), -Q, -A.transpose();
76
77 // calc eigenvalues and eigenvectors
78 Eigen::EigenSolver<Eigen::MatrixXd> eigs(ham);
79
80 // extract stable eigenvectors into 'eigvec'
81 Eigen::MatrixXcd eigvec = Eigen::MatrixXcd::Zero(2 * dim_x, dim_x);
82 int j = 0;
83 for (int i = 0; i < 2 * dim_x; ++i)
84 {
85 if (eigs.eigenvalues()[i].real() < 0.)
86 {
87 eigvec.col(j) = eigs.eigenvectors().block(0, i, 2 * dim_x, 1);
88 ++j;
89 }
90 }
91
92 // calc P with stable eigen vector matrix
93 Eigen::MatrixXcd vs_1, vs_2;
94 vs_1 = eigvec.block(0, 0, dim_x, dim_x);
95 vs_2 = eigvec.block(dim_x, 0, dim_x, dim_x);
96 P = (vs_2 * vs_1.inverse()).real();
97
98 return true;
99 }
100
101 bool computeK()
102 {
103 Eigen::SelfAdjointEigenSolver<Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> > q_solver(q_);
104 Eigen::SelfAdjointEigenSolver<Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> > r_solver(r_);
105 if (q_solver.info() != Eigen::Success || r_solver.info() != Eigen::Success)
106 return false;
107 // q (r) must be symmetric positive (semi) definite
108 for (int i = 0; i < q_solver.eigenvalues().cols(); ++i)
109 {
110 if (q_solver.eigenvalues()[i] < 0)
111 return false;
112 }
113 for (int i = 0; i < r_solver.eigenvalues().cols(); ++i)
114 {
115 if (r_solver.eigenvalues()[i] <= 0)
116 return false;
117 }
118 if (!isSymmetric(q_) || !isSymmetric(r_))
119 return false;
120
121 DMat<T> p;
122 if (solveRiccatiArimotoPotter(a_, b_, q_, r_, p))
123 k_ = r_.inverse() * (b_.transpose() * p.transpose());
124 return true;
125 }
126
127 Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> getK()
128 {
129 return k_;
130 }
131
132private:
133 bool isSymmetric(DMat<T> m)
134 {
135 for (int i = 0; i < m.rows() - 1; ++i)
136 {
137 for (int j = i + 1; j < m.cols(); ++j)
138 {
139 if (m(i, j - i) != m(j - i, i))
140 return false;
141 }
142 }
143 return true;
144 }
145
146 DMat<T> a_, b_, q_, r_, k_;
147};
Definition lqr.h:46
Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > getK()
Definition lqr.h:127
bool computeK()
Definition lqr.h:101
bool solveRiccatiArimotoPotter(const Eigen::MatrixXd &A, const Eigen::MatrixXd &B, const Eigen::MatrixXd &Q, const Eigen::MatrixXd &R, Eigen::MatrixXd &P)
Definition lqr.h:68
Lqr(const Eigen::MatrixBase< TA > &A, const Eigen::MatrixBase< TB > &B, const Eigen::MatrixBase< TQ > &Q, const Eigen::MatrixBase< TR > &R)
Definition lqr.h:49
typename Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > DMat
Definition eigen_types.h:146