Skip to content

Commit 1dd0ca2

Browse files
authored
Merge pull request #281 from dbacc/master
Added option for computing anti-stabilizing solutions of Riccati equations
2 parents bd0e515 + be598c1 commit 1dd0ca2

File tree

2 files changed

+60
-11
lines changed

2 files changed

+60
-11
lines changed

control/mateqn.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ def dlyap(A,Q,C=None,E=None):
411411

412412
#### Riccati equation solvers care and dare
413413

414-
def care(A,B,Q,R=None,S=None,E=None):
414+
def care(A, B, Q, R=None, S=None, E=None, stabilizing=True):
415415
""" (X,L,G) = care(A,B,Q,R=None) solves the continuous-time algebraic Riccati
416416
equation
417417
@@ -527,7 +527,11 @@ def care(A,B,Q,R=None,S=None,E=None):
527527
raise e
528528

529529
try:
530-
X,rcond,w,S_o,U,A_inv = sb02md(n,A,G,Q,'C')
530+
if stabilizing:
531+
sort = 'S'
532+
else:
533+
sort = 'U'
534+
X, rcond, w, S_o, U, A_inv = sb02md(n, A, G, Q, 'C', sort=sort)
531535
except ValueError as ve:
532536
if ve.info < 0 or ve.info > 5:
533537
e = ValueError(ve.message)
@@ -613,8 +617,12 @@ def care(A,B,Q,R=None,S=None,E=None):
613617
# Solve the generalized algebraic Riccati equation by calling the
614618
# Slycot function sg02ad
615619
try:
616-
rcondu,X,alfar,alfai,beta,S_o,T,U,iwarn = \
617-
sg02ad('C','B','N','U','N','N','S','R',n,m,0,A,E,B,Q,R,S)
620+
if stabilizing:
621+
sort = 'S'
622+
else:
623+
sort = 'U'
624+
rcondu, X, alfar, alfai, beta, S_o, T, U, iwarn = \
625+
sg02ad('C', 'B', 'N', 'U', 'N', 'N', sort, 'R', n, m, 0, A, E, B, Q, R, S)
618626
except ValueError as ve:
619627
if ve.info < 0 or ve.info > 7:
620628
e = ValueError(ve.message)
@@ -671,7 +679,7 @@ def care(A,B,Q,R=None,S=None,E=None):
671679
else:
672680
raise ControlArgument("Invalid set of input parameters.")
673681

674-
def dare(A,B,Q,R,S=None,E=None):
682+
def dare(A, B, Q, R, S=None, E=None, stabilizing=True):
675683
""" (X,L,G) = dare(A,B,Q,R) solves the discrete-time algebraic Riccati
676684
equation
677685
@@ -692,8 +700,8 @@ def dare(A,B,Q,R,S=None,E=None):
692700
matrix :math:`G = (B^T X B + R)^{-1} (B^T X A + S^T)` and the closed loop
693701
eigenvalues L, i.e., the eigenvalues of A - B G , E.
694702
"""
695-
if S is not None or E is not None:
696-
return dare_old(A, B, Q, R, S, E)
703+
if S is not None or E is not None or not stabilizing:
704+
return dare_old(A, B, Q, R, S, E, stabilizing)
697705
else:
698706
Rmat = asmatrix(R)
699707
Qmat = asmatrix(Q)
@@ -702,7 +710,7 @@ def dare(A,B,Q,R,S=None,E=None):
702710
L = eigvals(A - B.dot(G))
703711
return X, L, G
704712

705-
def dare_old(A,B,Q,R,S=None,E=None):
713+
def dare_old(A, B, Q, R, S=None, E=None, stabilizing=True):
706714
# Make sure we can import required slycot routine
707715
try:
708716
from slycot import sb02md
@@ -795,7 +803,12 @@ def dare_old(A,B,Q,R,S=None,E=None):
795803
raise e
796804

797805
try:
798-
X,rcond,w,S,U,A_inv = sb02md(n,A,G,Q,'D')
806+
if stabilizing:
807+
sort = 'S'
808+
else:
809+
sort = 'U'
810+
811+
X, rcond, w, S, U, A_inv = sb02md(n, A, G, Q, 'D', sort=sort)
799812
except ValueError as ve:
800813
if ve.info < 0 or ve.info > 5:
801814
e = ValueError(ve.message)
@@ -884,8 +897,12 @@ def dare_old(A,B,Q,R,S=None,E=None):
884897
# Solve the generalized algebraic Riccati equation by calling the
885898
# Slycot function sg02ad
886899
try:
887-
rcondu,X,alfar,alfai,beta,S_o,T,U,iwarn = \
888-
sg02ad('D','B','N','U','N','N','S','R',n,m,0,A,E,B,Q,R,S)
900+
if stabilizing:
901+
sort = 'S'
902+
else:
903+
sort = 'U'
904+
rcondu, X, alfar, alfai, beta, S_o, T, U, iwarn = \
905+
sg02ad('D', 'B', 'N', 'U', 'N', 'N', sort, 'R', n, m, 0, A, E, B, Q, R, S)
889906
except ValueError as ve:
890907
if ve.info < 0 or ve.info > 7:
891908
e = ValueError(ve.message)

control/tests/statefbk_test.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from control.statefbk import ctrb, obsv, place, place_varga, lqr, gram, acker
1010
from control.matlab import *
1111
from control.exception import slycot_check, ControlDimension
12+
from control.mateqn import care, dare
1213

1314
class TestStatefbk(unittest.TestCase):
1415
"""Test state feedback functions"""
@@ -298,6 +299,37 @@ def test_LQR_3args(self):
298299
K, S, poles = lqr(sys, Q, R)
299300
self.check_LQR(K, S, poles, Q, R)
300301

302+
@unittest.skipIf(not slycot_check(), "slycot not installed")
303+
def test_care(self):
304+
#unit test for stabilizing and anti-stabilizing feedbacks
305+
#continuous-time
306+
307+
A = np.diag([1,-1])
308+
B = np.identity(2)
309+
Q = np.identity(2)
310+
R = np.identity(2)
311+
S = 0 * B
312+
E = np.identity(2)
313+
X, L , G = care(A, B, Q, R, S, E, stabilizing=True)
314+
assert np.all(np.real(L) < 0)
315+
X, L , G = care(A, B, Q, R, S, E, stabilizing=False)
316+
assert np.all(np.real(L) > 0)
317+
318+
@unittest.skipIf(not slycot_check(), "slycot not installed")
319+
def test_dare(self):
320+
#discrete-time
321+
A = np.diag([0.5,2])
322+
B = np.identity(2)
323+
Q = np.identity(2)
324+
R = np.identity(2)
325+
S = 0 * B
326+
E = np.identity(2)
327+
X, L , G = dare(A, B, Q, R, S, E, stabilizing=True)
328+
assert np.all(np.abs(L) < 1)
329+
X, L , G = dare(A, B, Q, R, S, E, stabilizing=False)
330+
assert np.all(np.abs(L) > 1)
331+
332+
301333
def test_suite():
302334
return unittest.TestLoader().loadTestsFromTestCase(TestStatefbk)
303335

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy