From: <kk...@us...> - 2011-02-08 22:13:04
|
Revision: 39 http://python-control.svn.sourceforge.net/python-control/?rev=39&view=rev Author: kkchen Date: 2011-02-08 22:12:57 +0000 (Tue, 08 Feb 2011) Log Message: ----------- Removed scipy.signal.lti from statesp.py; added rss. The StateSpace class now uses seven data members: A, B, C, D, states, inputs, and outputs. scipy.signal.lti was removed because it does not support MIMO functionality. Also, a working rss has been added to statesp.py. It still has yet to be tested. Kevin K. Chen <kk...@pr...> Modified Paths: -------------- branches/control-0.4a/src/statesp.py Modified: branches/control-0.4a/src/statesp.py =================================================================== --- branches/control-0.4a/src/statesp.py 2011-02-08 22:12:53 UTC (rev 38) +++ branches/control-0.4a/src/statesp.py 2011-02-08 22:12:57 UTC (rev 39) @@ -55,14 +55,32 @@ # of the functions that already existing in that package to be used # directly. # -class StateSpace(signal.lti): +class StateSpace: """The StateSpace class is used to represent linear input/output systems. """ # Initialization - def __init__(self, *args, **keywords): - # First initialize the parent object - signal.lti.__init__(self, *args, **keywords) + def __init__(self, A, B, C, D): + self.A = A + self.B = B + self.C = C + self.D = D + self.states = A.shape[0] + self.inputs = B.shape[1] + self.outputs = C.shape[0] + + # Check that the matrix sizes are consistent. + if self.states != A.shape[1]: + raise ValueError("A must be square.") + if self.states != B.shape[0]: + raise ValueError("B must have the same row size as A.") + if self.states != C.shape[1]: + raise ValueError("C must have the same column size as A.") + if self.inputs != D.shape[1]: + raise ValueError("D must have the same column size as B.") + if self.outputs != D.shape[0]: + raise ValueError("D must have the same row size as C.") + # Style to use for printing def __str__(self): str = "A = " + self.A.__str__() + "\n\n" @@ -76,7 +94,7 @@ """Compute the response of a system to a list of frequencies""" # Generate and save a transfer function matrix #! TODO: This is currently limited to SISO systems - nout, nin = self.D.shape + #nout, nin = self.D.shape # Compute the denominator from the A matrix den = sp.poly1d(sp.poly(self.A)) @@ -99,7 +117,9 @@ return None # Compute poles and zeros - def poles(self): return sp.roots(sp.poly(self.A)) + def poles(self): + return sp.roots(sp.poly(self.A)) + def zeros(self): den = sp.poly1d(sp.poly(self.A)) @@ -248,3 +268,83 @@ else: raise TypeError("can't convert given type to StateSpace system") + +def rss(states=1, inputs=1, outputs=1): + """Create a stable random state space object.""" + + import numpy + from numpy.random import rand, randn + + # Make some poles for A. Preallocate a complex array. + poles = numpy.zeros(states) + numpy.zeros(states) * 0.j + i = 0 + while i < states - 1: + if rand() < 0.05 and i != 0: + # Small chance of copying poles, if we're not at the first element. + if poles[i-1].imag == 0: + # Copy previous real pole. + poles[i] = poles[i-1] + i += 1 + else: + # Copy previous complex conjugate pair of poles. + poles[i:i+2] = poles[i-2:i] + i += 2 + elif rand() < 0.6: + # Real pole. + poles[i] = -sp.exp(randn()) + 0.j + i += 1 + else: + # Complex conjugate pair of poles. + poles[i] = complex(-sp.exp(randn()), sp.exp(randn())) + poles[i+1] = complex(poles[i].real, -poles[i].imag) + i += 2 + # When we reach this point, we either have one or zero poles left to fill. + # Put a real pole if there is one space left. + if i == states - 1: + poles[i] = -sp.exp(randn()) + 0.j + + # Now put the poles in A as real blocks on the diagonal. + A = numpy.zeros((states, states)) + i = 0 + while i < states: + if poles[i].imag == 0: + A[i, i] = poles[i].real + i += 1 + else: + A[i, i] = A[i+1, i+1] = poles[i].real + A[i, i+1] = poles[i].imag + A[i+1, i] = -poles[i].imag + i += 2 + + # Finally, apply a transformation so that A is not block-diagonal. + while True: + T = randn(states, states) + try: + A = numpy.dot(numpy.linalg.solve(T, A), T) # A = T \ A * T + break + except numpy.linalg.linalg.LinAlgError: + # In the unlikely event that T is rank-deficient, iterate again. + pass + + # Make the remaining matrices. + B = randn(states, inputs) + C = randn(outputs, states) + D = randn(outputs, inputs) + + # Make masks to zero out some of the elements. + while True: + B_mask = rand(states, inputs) < 0.8 + if sp.any(B_mask): # Retry if we get all zeros. + break + while True: + C_mask = rand(outputs, states) < 0.8 + if sp.any(C_mask): # Retry if we get all zeros. + break + D_mask = rand(outputs, inputs) < 0.3 + + # Apply masks. + B = B * B_mask + C = C * C_mask + D = D * D_mask + + return StateSpace(A, B, C, D) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <kk...@us...> - 2011-02-08 22:13:28
|
Revision: 44 http://python-control.svn.sourceforge.net/python-control/?rev=44&view=rev Author: kkchen Date: 2011-02-08 22:13:22 +0000 (Tue, 08 Feb 2011) Log Message: ----------- Added tests for valid inputs to StateSpace in statesp.py. Kevin K. Chen <kk...@pr...> Modified Paths: -------------- branches/control-0.4a/src/statesp.py Modified: branches/control-0.4a/src/statesp.py =================================================================== --- branches/control-0.4a/src/statesp.py 2011-02-08 22:13:17 UTC (rev 43) +++ branches/control-0.4a/src/statesp.py 2011-02-08 22:13:22 UTC (rev 44) @@ -60,6 +60,28 @@ """ def __init__(self, A, B, C, D): + # Here we're going to convert inputs to matrices, if the user gave a non + # 2-D array or matrix type. + matrices = [A, B, C, D] + for i in range(len(matrices)): + if (isinstance(matrices[i], (int, long, float, complex))): + # Convert scalars to matrices, if necessary. + matrices[i] = sp.matrix(matrices[i]) + elif isinstance(matrices[i], sp.ndarray): + # Convert 0- or 1-D arrays to matrices, if necessary. + if len(matrices[i].shape) < 2: + matrices[i] = sp.matrix(matrices[i]) + elif len(matrices[i].shape) == 2: + # If we're already a 2-D array or a matrix, then perfect! + pass + else: + raise ValueError("A, B, C, and D cannot have > 2 \ + dimensions.") + else: + # If the user gave us a non-numeric type. + raise ValueError("A, B, C, and D must be arrays or matrices.") + [A, B, C, D] = matrices + self.A = A self.B = B self.C = C @@ -69,11 +91,6 @@ self.inputs = B.shape[1] self.outputs = C.shape[0] - # Check that the inputs are arrays or matrices. - if not (isinstance(A, sp.ndarray) and isinstance(B, sp.ndarray) and - isinstance(C, sp.ndarray) and isinstance(D, sp.ndarray)): - raise ValueError("A, B, C, and D must be arrays or matrices.") - # Check that the matrix sizes are consistent. if self.states != A.shape[1]: raise ValueError("A must be square.") @@ -389,4 +406,4 @@ C = C * Cmask D = D * Dmask - return StateSpace(A, B, C, D) \ No newline at end of file + return StateSpace(A, B, C, D) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <kk...@us...> - 2011-02-08 22:13:34
|
Revision: 45 http://python-control.svn.sourceforge.net/python-control/?rev=45&view=rev Author: kkchen Date: 2011-02-08 22:13:26 +0000 (Tue, 08 Feb 2011) Log Message: ----------- Minor changes to statesp.py related to scipy.signal.lti overhaul. Kevin K. Chen <kk...@pr...> Modified Paths: -------------- branches/control-0.4a/src/statesp.py Modified: branches/control-0.4a/src/statesp.py =================================================================== --- branches/control-0.4a/src/statesp.py 2011-02-08 22:13:22 UTC (rev 44) +++ branches/control-0.4a/src/statesp.py 2011-02-08 22:13:26 UTC (rev 45) @@ -281,16 +281,17 @@ # def convertToStateSpace(sys, inputs=1, outputs=1): """Convert a system to state space form (if needed)""" - if (isinstance(sys, StateSpace) or - isinstance(sys, xferfcn.TransferFunction)): + if isinstance(sys, StateSpace): # Already a state space system; just return it return sys - + elif isinstance(sys, xferfcn.TransferFunction): + pass # TODO: convert SS to TF elif (isinstance(sys, (int, long, float, complex))): # Generate a simple state space system of the desired dimension # The following Doesn't work due to inconsistencies in ltisys: # return StateSpace([[]], [[]], [[]], sp.eye(outputs, inputs)) - return StateSpace(-1, 0, 0, sp.eye(outputs, inputs)) + return StateSpace(-1, zeros((1, inputs)), zeros((outputs, 1)), + sp.eye(outputs, inputs)) else: raise TypeError("can't convert given type to StateSpace system") This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <kk...@us...> - 2011-02-08 22:13:54
|
Revision: 50 http://python-control.svn.sourceforge.net/python-control/?rev=50&view=rev Author: kkchen Date: 2011-02-08 22:13:47 +0000 (Tue, 08 Feb 2011) Log Message: ----------- Created Lti2 parent class and made StateSpace its child, in statesp.py. Added .DS_Store to .hgignore. Kevin K. Chen <kk...@pr...> Modified Paths: -------------- branches/control-0.4a/src/statesp.py Modified: branches/control-0.4a/src/statesp.py =================================================================== --- branches/control-0.4a/src/statesp.py 2011-02-08 22:13:44 UTC (rev 49) +++ branches/control-0.4a/src/statesp.py 2011-02-08 22:13:47 UTC (rev 50) @@ -46,19 +46,21 @@ import xferfcn from scipy import concatenate, zeros -# -# StateSpace class -# -# The StateSpace class is used throughout the control systems library to -# represent systems in state space form. This class is derived from -# the ltisys class defined in the scipy.signal package, allowing many -# of the functions that already existing in that package to be used -# directly. -# -class StateSpace: - """The StateSpace class is used to represent linear input/output systems. - """ +class Lti2: + """The Lti2 is a parent class to the StateSpace and TransferFunction child + classes. It only contains the number of inputs and outputs, but this can be + expanded in the future.""" + + def __init__(self, inputs=1, outputs=1): + # Data members common to StateSpace and TransferFunction. + self.inputs = inputs + self.outputs = outputs +class StateSpace(Lti2): + """The StateSpace class is used throughout the python-control library to + represent systems in state space form. This class is derived from the Lti2 + base class.""" + def __init__(self, A, B, C, D): # Here we're going to convert inputs to matrices, if the user gave a non # 2-D array or matrix type. @@ -88,8 +90,7 @@ self.D = D self.states = A.shape[0] - self.inputs = B.shape[1] - self.outputs = C.shape[0] + Lti2.__init__(self, B.shape[1], C.shape[0]) # Check that the matrix sizes are consistent. if self.states != A.shape[1]: @@ -103,8 +104,9 @@ if self.outputs != D.shape[0]: raise ValueError("D must have the same row size as C.") - # Style to use for printing def __str__(self): + """Style to use for printing.""" + str = "A = " + self.A.__str__() + "\n\n" str += "B = " + self.B.__str__() + "\n\n" str += "C = " + self.C.__str__() + "\n\n" @@ -113,7 +115,8 @@ # Method for generating the frequency response of the system def freqresp(self, omega=None): - """Compute the response of a system to a list of frequencies""" + """Compute the response of a system to a list of frequencies.""" + # Generate and save a transfer function matrix #! TODO: This is currently limited to SISO systems #nout, nin = self.D.shape @@ -154,12 +157,14 @@ # Negation of a system def __neg__(self): - """Negate a state space system""" + """Negate a state space system.""" + return StateSpace(self.A, self.B, -self.C, -self.D) # Addition of two transfer functions (parallel interconnection) def __add__(self, other): - """Add two state space systems""" + """Add two state space systems.""" + # Check for a couple of special cases if (isinstance(other, (int, long, float, complex))): # Just adding a scalar; put it in the D matrix @@ -186,17 +191,20 @@ # Reverse addition - just switch the arguments def __radd__(self, other): - """Add two state space systems""" + """Add two state space systems.""" + return self.__add__(other) # Subtraction of two transfer functions (parallel interconnection) def __sub__(self, other): - """Subtract two state space systems""" + """Subtract two state space systems.""" + return __add__(self, other.__neg__()) # Multiplication of two transfer functions (series interconnection) def __mul__(self, other): - """Serial interconnection between two state space systems""" + """Serial interconnection between two state space systems.""" + # Check for a couple of special cases if (isinstance(other, (int, long, float, complex))): # Just multiplying by a scalar; change the output @@ -225,6 +233,7 @@ # Just need to convert LH argument to a state space object def __rmul__(self, other): """Serial interconnection between two state space systems""" + # Check for a couple of special cases if (isinstance(other, (int, long, float, complex))): # Just multiplying by a scalar; change the input @@ -238,7 +247,8 @@ # Feedback around a state space system def feedback(self, other, sign=-1): - """Feedback interconnection between two state space systems""" + """Feedback interconnection between two state space systems.""" + # Check for special cases if (isinstance(other, (int, long, float, complex))): # Scalar feedback, create state space system that is this case @@ -281,6 +291,7 @@ # def convertToStateSpace(sys, inputs=1, outputs=1): """Convert a system to state space form (if needed)""" + if isinstance(sys, StateSpace): # Already a state space system; just return it return sys This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <kk...@us...> - 2011-02-08 22:17:03
|
Revision: 88 http://python-control.svn.sourceforge.net/python-control/?rev=88&view=rev Author: kkchen Date: 2011-02-08 22:16:57 +0000 (Tue, 08 Feb 2011) Log Message: ----------- Major bug fix in convertToStateSpace. Kevin K. Chen <kk...@pr...> Modified Paths: -------------- branches/control-0.4a/src/statesp.py Modified: branches/control-0.4a/src/statesp.py =================================================================== --- branches/control-0.4a/src/statesp.py 2011-02-08 22:16:53 UTC (rev 87) +++ branches/control-0.4a/src/statesp.py 2011-02-08 22:16:57 UTC (rev 88) @@ -437,7 +437,7 @@ # Repeat the common denominator along the rows. den = array([den for i in range(sys.outputs)]) - ssout = td04ad(sys.inputs, sys.outputs, index, num, den) + ssout = td04ad(sys.inputs, sys.outputs, index, den, num) return StateSpace(ssout[1], ssout[2], ssout[3], ssout[4]) elif isinstance(sys, (int, long, float, complex)): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <kk...@us...> - 2011-02-08 22:19:23
|
Revision: 115 http://python-control.svn.sourceforge.net/python-control/?rev=115&view=rev Author: kkchen Date: 2011-02-08 22:19:18 +0000 (Tue, 08 Feb 2011) Log Message: ----------- Changed warning message for td04ad Lauren Padilla <lpa...@pr...> Modified Paths: -------------- branches/control-0.4a/src/statesp.py Modified: branches/control-0.4a/src/statesp.py =================================================================== --- branches/control-0.4a/src/statesp.py 2011-02-08 22:19:13 UTC (rev 114) +++ branches/control-0.4a/src/statesp.py 2011-02-08 22:19:18 UTC (rev 115) @@ -455,7 +455,7 @@ den = array([den for i in range(sys.outputs)]) # TODO: transfer function to state space conversion is still buggy! print "Warning: transfer function to state space conversion by td04ad \ -is still buggy!" +is still buggy! Advise converting state space sys back to tf to verify the transformation was correct." #print num #print shape(num) ssout = td04ad(sys.inputs, sys.outputs, index, den, num) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <kk...@us...> - 2011-02-08 22:18:41
|
Revision: 108 http://python-control.svn.sourceforge.net/python-control/?rev=108&view=rev Author: kkchen Date: 2011-02-08 22:18:35 +0000 (Tue, 08 Feb 2011) Log Message: ----------- Bug fix in StateSpace._remove_useless_states. Kevin K. Chen <kk...@pr...> Modified Paths: -------------- branches/control-0.4a/src/statesp.py Modified: branches/control-0.4a/src/statesp.py =================================================================== --- branches/control-0.4a/src/statesp.py 2011-02-08 22:18:31 UTC (rev 107) +++ branches/control-0.4a/src/statesp.py 2011-02-08 22:18:35 UTC (rev 108) @@ -157,7 +157,7 @@ # Remove the useless states. if all(useless == range(self.states)): # All the states were useless. - self.A = 0 + self.A = zeros((1, 1)) self.B = zeros((1, self.inputs)) self.C = zeros((self.outputs, 1)) else: @@ -167,6 +167,10 @@ self.B = delete(self.B, useless, 0) self.C = delete(self.C, useless, 1) + self.states = self.A.shape[0] + self.inputs = self.B.shape[1] + self.outputs = self.C.shape[0] + def copy(self): """Return a deep copy of the instance.""" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |