4545import scipy as sp
4646from . import statesp
4747from .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 ()
0 commit comments