Skip to content

Commit e3d8690

Browse files
committed
Implement comments from pull request
1 parent 3e02778 commit e3d8690

3 files changed

Lines changed: 45 additions & 46 deletions

File tree

control/statefbk.py

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@
4545
import scipy as sp
4646
from . import statesp
4747
from .exception import ControlSlycot, ControlArgument, ControlDimension
48+
import warnings
4849

49-
__all__ = ['ctrb', 'obsv', 'gram', 'place', 'lqr']
50+
__all__ = ['ctrb', 'obsv', 'gram', 'place', 'lqr', 'place_varga', 'acker']
5051

5152

5253
# Pole placement
@@ -67,13 +68,12 @@ def place(A, B, p, method="YT", dtime=False, alpha=None):
6768
dtime: optional (useful only if method=="varga")
6869
False for continuous time pole placement or True for discrete time.
6970
The default is dtime=False.
71+
If dtime is not null, place_varga will leave the eigenvalues with modulus
72+
less than alpha untouched. Otherwise, place_varga will leave the eigenvalues with real
73+
real part less than alpha untouched.
7074
alpha: double scalar (useful only if method=="varga")
71-
If DICO='C', then _place_varga will leave the eigenvalues with real
72-
real part less than alpha untouched.
73-
If DICO='D', the _place_varga will leave eigenvalues with modulus
74-
less than alpha untouched.
7575
76-
By default (alpha=None), _place_varga computes alpha such that all
76+
By default (alpha=None), place_varga computes alpha such that all
7777
poles will be placed.
7878
7979
Returns
@@ -105,28 +105,28 @@ def place(A, B, p, method="YT", dtime=False, alpha=None):
105105
106106
See Also
107107
--------
108-
_acker, _place_varga
108+
acker, place_varga
109109
"""
110110
if method == "acker":
111-
return _acker(A, B, p)
111+
return acker(A, B, p)
112112
if method == "varga":
113113
try:
114-
return _place_varga(A, B, p, dtime=dtime, alpha=alpha)
114+
return place_varga(A, B, p, dtime=dtime, alpha=alpha)
115115
except ControlSlycot as error:
116-
print("[Pole placement] Fallback strategy: using Tits-Yang method from scipy.")
116+
warnings.warn("[Pole placement] Fallback strategy: using Tits-Yang method from scipy.")
117117
return place(A, B, p)
118118
else:
119119
try:
120120
from scipy.signal import place_poles
121121
except ImportError as error:
122-
print(error)
123-
print("[Pole placement] Fallback strategy: using Varga method from slycot.")
122+
warnings.warn(error)
123+
warnings.warn("[Pole placement] Fallback strategy: using Varga method from slycot.")
124124
try:
125-
K = _place_varga(A, B, p, dtime=dtime, alpha=alpha)
125+
K = place_varga(A, B, p, dtime=dtime, alpha=alpha)
126126
return K
127127
except ControlSlycot as error:
128-
print("[Pole placement] Fallback strategy: using Ackermanm method.")
129-
return _acker(A, B, p)
128+
warnings.warn("[Pole placement] Fallback strategy: using Ackermanm method.")
129+
return acker(A, B, p)
130130

131131
# Convert the system inputs to NumPy arrays
132132
A_mat = np.array(A)
@@ -147,18 +147,18 @@ def place(A, B, p, method="YT", dtime=False, alpha=None):
147147
try:
148148
result = place_poles(A_mat, B_mat, placed_eigs, method=method)
149149
except ValueError as error:
150-
print("[Pole placement] Redundant pole location. "
150+
warnings.warn("[Pole placement] Redundant pole location. "
151151
"Fallback strategy: using Ackermann method.")
152152
try:
153-
return _acker(A, B, p)
153+
return acker(A, B, p)
154154
except ValueError as error:
155-
print("Pole placement failed.")
155+
warnings.warn("Pole placement failed.")
156156
return None
157157
K = result.gain_matrix
158158
return K
159159

160160

161-
def _place_varga(A, B, p, dtime=False, alpha=None):
161+
def place_varga(A, B, p, dtime=False, alpha=None):
162162
"""Place closed loop eigenvalues
163163
K = place_varga(A, B, p, dtime=False, alpha=None)
164164
@@ -175,11 +175,10 @@ def _place_varga(A, B, p, dtime=False, alpha=None):
175175
---------------
176176
dtime: False for continuous time pole placement or True for discrete time.
177177
The default is dtime=False.
178+
If dtime is not null, place_varga will leave the eigenvalues with modulus
179+
less than alpha untouched. Otherwise, place_varga will leave the eigenvalues with real
180+
real part less than alpha untouched.
178181
alpha: double scalar
179-
If DICO='C', then place_varga will leave the eigenvalues with real
180-
real part less than alpha untouched.
181-
If DICO='D', the place_varga will leave eigenvalues with modulus
182-
less than alpha untouched.
183182
184183
By default (alpha=None), place_varga computes alpha such that all
185184
poles will be placed.
@@ -205,11 +204,11 @@ def _place_varga(A, B, p, dtime=False, alpha=None):
205204
--------
206205
>>> A = [[-1, -1], [0, 1]]
207206
>>> B = [[0], [1]]
208-
>>> K = _place_varga(A, B, [-2, -5])
207+
>>> K = place_varga(A, B, [-2, -5])
209208
210209
See Also:
211210
--------
212-
place, _acker
211+
place, acker
213212
"""
214213

215214
# Make sure that SLICOT is installed
@@ -236,8 +235,8 @@ def _place_varga(A, B, p, dtime=False, alpha=None):
236235

237236
if alpha is None:
238237
# SB01BD ignores eigenvalues with real part less than alpha
239-
# (if DICO = 'C') or with modulus less than alpha
240-
# (if DICO = 'D').
238+
# (if dico = 'C') or with modulus less than alpha
239+
# (if dico = 'D').
241240
if dtime:
242241
# For discrete time, slycot only cares about modulus, so just make
243242
# alpha the smallest it can be.
@@ -251,7 +250,7 @@ def _place_varga(A, B, p, dtime=False, alpha=None):
251250
# but does the trick
252251
alpha = -2 * abs(min(system_eigs.real))
253252
elif dtime and alpha < 0.0:
254-
raise ValueError("Need alpha > 0 when DICO='D'.")
253+
raise ValueError("Need alpha > 0 when dico='D'.")
255254

256255
# Call SLICOT routine to place the eigenvalues
257256
A_z, w, nfp, nap, nup, F, Z = \
@@ -262,11 +261,10 @@ def _place_varga(A, B, p, dtime=False, alpha=None):
262261
return -F
263262

264263

265-
def _acker(A, B, poles):
264+
# Contributed by Roberto Bucher <roberto.bucher@supsi.ch>
265+
def acker(A, B, poles):
266266
"""Pole placement using Ackermann method
267267
268-
Contributed by Roberto Bucher <roberto.bucher@supsi.ch>
269-
270268
Call:
271269
K = acker(A, B, poles)
272270
@@ -284,7 +282,7 @@ def _acker(A, B, poles):
284282
285283
See Also:
286284
--------
287-
place, _place_varga
285+
place, place_varga
288286
"""
289287

290288
# Convert the inputs to matrices
@@ -480,16 +478,16 @@ def obsv(A, C):
480478
return O
481479

482480

483-
def gram(sys, desired_computation_str):
481+
def gram(sys, gramian_type):
484482
"""Gramian (controllability or observability)
485483
486484
Parameters
487485
----------
488486
sys: StateSpace
489487
State-space system to compute Gramian for
490-
desired_computation_str: String
488+
gramian_type: String
491489
Type of desired computation.
492-
`desired_computation_str` is either 'c' (controllability) or 'o' (observability).
490+
`gramian_type` is either 'c' (controllability) or 'o' (observability).
493491
To compute the Cholesky factors of gramians use 'cf' (controllability)
494492
or 'of' (observability)
495493
@@ -502,7 +500,7 @@ def gram(sys, desired_computation_str):
502500
------
503501
ValueError
504502
* if system is not instance of StateSpace class
505-
* if `desired_computation_str` is not 'c', 'o', 'cf' or 'of'
503+
* if `gramian_type` is not 'c', 'o', 'cf' or 'of'
506504
* if system is unstable (sys.A has eigenvalues not in left half plane)
507505
508506
ImportError
@@ -521,7 +519,7 @@ def gram(sys, desired_computation_str):
521519
# Check for ss system object
522520
if not isinstance(sys, statesp.StateSpace):
523521
raise ValueError("System must be StateSpace!")
524-
if desired_computation_str not in ['c', 'o', 'cf', 'of']:
522+
if gramian_type not in ['c', 'o', 'cf', 'of']:
525523
raise ValueError("That type is not supported!")
526524

527525
# TODO: Check for continuous or discrete, only continuous supported right now
@@ -537,17 +535,17 @@ def gram(sys, desired_computation_str):
537535
if np.any(np.linalg.eigvals(sys.A).real >= 0.0):
538536
raise ValueError("The system is unstable.")
539537

540-
if desired_computation_str == 'c' or desired_computation_str == 'o':
538+
if gramian_type == 'c' or gramian_type == 'o':
541539
# Compute Gramian by the Slycot routine sb03md
542540
# make sure Slycot is installed
543541
try:
544542
from slycot import sb03md
545543
except ImportError:
546544
raise ControlSlycot("Can't find slycot module 'sb03md'.")
547-
if desired_computation_str == 'c':
545+
if gramian_type == 'c':
548546
tra = 'T'
549547
C = -np.dot(sys.B, sys.B.transpose())
550-
elif desired_computation_str == 'o':
548+
elif gramian_type == 'o':
551549
tra = 'N'
552550
C = -np.dot(sys.C.transpose(), sys.C)
553551
n = sys.states
@@ -557,7 +555,7 @@ def gram(sys, desired_computation_str):
557555
gram = X
558556
return gram
559557

560-
elif desired_computation_str == 'cf' or desired_computation_str == 'of':
558+
elif gramian_type == 'cf' or gramian_type == 'of':
561559
# Compute cholesky factored gramian from slycot routine sb03od
562560
try:
563561
from slycot import sb03od
@@ -567,12 +565,12 @@ def gram(sys, desired_computation_str):
567565
n = sys.states
568566
Q = np.zeros((n, n))
569567
A = np.array(sys.A) # convert to NumPy array for slycot
570-
if desired_computation_str == 'cf':
568+
if gramian_type == 'cf':
571569
m = sys.B.shape[1]
572570
B = np.zeros_like(A)
573571
B[0:m, 0:n] = sys.B.transpose()
574572
X, scale, w = sb03od(n, m, A.transpose(), Q, B, dico, fact='N', trans=tra)
575-
elif desired_computation_str == 'of':
573+
elif gramian_type == 'of':
576574
m = sys.C.shape[0]
577575
C = np.zeros_like(A)
578576
C[0:n, 0:m] = sys.C.transpose()

control/tests/test_statefbk.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from __future__ import print_function
77
import unittest
88
import numpy as np
9-
from control.statefbk import ctrb, obsv, place, _place_varga, lqr, gram, _acker
9+
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
1212
from control.mateqn import care, dare
@@ -144,7 +144,7 @@ def test_acker(self):
144144
poles = pole(des)
145145

146146
# Now place the poles using acker
147-
K = _acker(sys.A, sys.B, poles)
147+
K = acker(sys.A, sys.B, poles)
148148
new = ss(sys.A - sys.B * K, sys.B, sys.C, sys.D)
149149
placed = pole(new)
150150

@@ -197,7 +197,7 @@ def test_place_varga_continuous(self):
197197
B = self.B_siso
198198

199199
P = np.array([-2., -2.])
200-
K = _place_varga(A, B, P)
200+
K = place_varga(A, B, P)
201201
P_placed = np.linalg.eigvals(A - B.dot(K))
202202
# No guarantee of the ordering, so sort them
203203
P.sort()

doc/control.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ Control system synthesis
108108
.. autosummary::
109109
:toctree: generated/
110110

111+
acker
111112
h2syn
112113
hinfsyn
113114
lqr

0 commit comments

Comments
 (0)