From: <mur...@us...> - 2011-06-26 02:44:16
|
Revision: 165 http://python-control.svn.sourceforge.net/python-control/?rev=165&view=rev Author: murrayrm Date: 2011-06-26 02:44:09 +0000 (Sun, 26 Jun 2011) Log Message: ----------- * Made fixes to documentation (intro, matlab) * Got rid of unit test error in hsvd (convert slycot args to numpy array) * Tracked down possible errors in convert_test (still in progress) See ChangeLog for more detailed list of code that was modified Modified Paths: -------------- trunk/ChangeLog trunk/doc/intro.rst trunk/src/matlab.py trunk/src/statefbk.py trunk/src/timeresp.py trunk/src/xferfcn.py trunk/tests/convert_test.py Modified: trunk/ChangeLog =================================================================== --- trunk/ChangeLog 2011-06-22 19:26:01 UTC (rev 164) +++ trunk/ChangeLog 2011-06-26 02:44:09 UTC (rev 165) @@ -1,3 +1,27 @@ +2011-06-25 Richard Murray <murray@malabar.local> + + * src/xferfcn.py (TransferFunction._common_den): changed tolerance + for detecting complex valued poles to a user-settable parameter, + with default value 1e-8. This was an attempt to fix errors in the + convert_test.py unittest script (conversion routine was + misclassifying some poles as imaginary when they weren't). + + * src/xferfcn.py (_convertToTransferFunction): converted arguments + to tb04ad to numpy arrays; fixes a unit test error in convert_test.py. + + * src/statefbk.py (gram): convert system matrix passed to sb03md to + numpy array; this fixes a unit test error in modelsimp_test.py. + + * src/matlab.py (impulse): got rid of X0 argument for impulse + response (not implemented in MATLAB). + + * doc/intro.rst: added some quick start information + + * src/matlab.py: added documentation for step, impulse, initial, lsim + + * src/timeresp.py: fixed some MATLAB specific function names in + function doc strings + 2011-06-22 Richard Murray <murray@malabar.local> * doc/intro.rst: fixed some small types Modified: trunk/doc/intro.rst =================================================================== --- trunk/doc/intro.rst 2011-06-22 19:26:01 UTC (rev 164) +++ trunk/doc/intro.rst 2011-06-26 02:44:09 UTC (rev 165) @@ -8,9 +8,10 @@ that implement common operations for the analysis and design of feedback control systems. The initial goal is to implement all of the functionality required to work through the examples in the textbook -Feedback Systems by \xC5str\xF6m and Murray. A MATLAB compatibility package -(control.matlab) is available that provides functions corresponding to -the commands available in the MATLAB Control Systems Toolbox. +Feedback Systems by Astrom and Murray. A MATLAB compatibility package +(control.matlab) is available that provides many of the common +functions corresponding to commands available in the MATLAB Control +Systems Toolbox. In addition to the documentation here, there is a project wiki that contains some additional information about how to use the package @@ -26,3 +27,19 @@ * Transfer functions are only implemented for SISO systems (due to limitations in the underlying signals.lti class); use state space representations for MIMO systems. + +Getting Started +--------------- +1. Download the latest release from http:sf.net/projects/python-control/files. +2. Untar the source code in a temporary directory and run 'python setup.py + install' to build and install the code +3. To see if things are working correctly, run ipython -pylab and run the + script 'examples/secord-matlab.py'. This should generate a set response, + Bode plot and Nyquist plot for a simple second order system. +4. To see the commands that are available, run the following commands in + ipython:: + >>> import control + >>> ?control.matlab +5. If you want to have a MATLAB-like environment for running the control + toolbox, use:: + >>> from control.matlab import * Modified: trunk/src/matlab.py =================================================================== --- trunk/src/matlab.py 2011-06-22 19:26:01 UTC (rev 164) +++ trunk/src/matlab.py 2011-06-26 02:44:09 UTC (rev 165) @@ -1128,21 +1128,217 @@ # Call corresponding functions in timeresp, with arguments transposed def step(sys, T=None, X0=0., input=0, output=0, **keywords): + ''' + Step response of a linear system + + If the system has multiple inputs or outputs (MIMO), one input and one + output have to be selected for the simulation. The parameters `input` + and `output` do this. All other inputs are set to 0, all other outputs + are ignored. + + Parameters + ---------- + sys: StateSpace, or TransferFunction + LTI system to simulate + + T: array-like object, optional + Time vector (argument is autocomputed if not given) + + X0: array-like or number, optional + Initial condition (default = 0) + + Numbers are converted to constant arrays with the correct shape. + + input: int + Index of the input that will be used in this simulation. + + output: int + Index of the output that will be used in this simulation. + + **keywords: + Additional keyword arguments control the solution algorithm for the + differential equations. These arguments are passed on to the function + :func:`control.ForcedResponse`, which in turn passes them on to + :func:`scipy.integrate.odeint`. See the documentation for + :func:`scipy.integrate.odeint` for information about these + arguments. + + Returns + ------- + T: array + Time values of the output + + yout: array + Response of the system + + See Also + -------- + lsim, initial, impulse + + Examples + -------- + >>> T, yout = step(sys, T, X0) + ''' T, yout = timeresp.StepResponse(sys, T, X0, input, output, transpose = True, **keywords) return T, yout -def impulse(sys, T=None, X0=0., input=0, output=0, **keywords): - T, yout = timeresp.ImpulseResponse(sys, T, X0, input, output, +def impulse(sys, T=None, input=0, output=0, **keywords): + ''' + Impulse response of a linear system + + If the system has multiple inputs or outputs (MIMO), one input and + one output must be selected for the simulation. The parameters + `input` and `output` do this. All other inputs are set to 0, all + other outputs are ignored. + + Parameters + ---------- + sys: StateSpace, TransferFunction + LTI system to simulate + + T: array-like object, optional + Time vector (argument is autocomputed if not given) + + input: int + Index of the input that will be used in this simulation. + + output: int + Index of the output that will be used in this simulation. + + **keywords: + Additional keyword arguments control the solution algorithm for the + differential equations. These arguments are passed on to the function + :func:`lsim`, which in turn passes them on to + :func:`scipy.integrate.odeint`. See the documentation for + :func:`scipy.integrate.odeint` for information about these + arguments. + + Returns + ------- + T: array + Time values of the output + yout: array + Response of the system + + See Also + -------- + lsim, step, initial + + Examples + -------- + >>> T, yout = impulse(sys, T) + ''' + T, yout = timeresp.ImpulseResponse(sys, T, 0, input, output, transpose = True, **keywords) return T, yout def initial(sys, T=None, X0=0., input=0, output=0, **keywords): + ''' + Initial condition response of a linear system + + If the system has multiple inputs or outputs (MIMO), one input and one + output have to be selected for the simulation. The parameters `input` + and `output` do this. All other inputs are set to 0, all other outputs + are ignored. + + Parameters + ---------- + sys: StateSpace, or TransferFunction + LTI system to simulate + + T: array-like object, optional + Time vector (argument is autocomputed if not given) + + X0: array-like object or number, optional + Initial condition (default = 0) + + Numbers are converted to constant arrays with the correct shape. + + input: int + Index of the input that will be used in this simulation. + + output: int + Index of the output that will be used in this simulation. + + **keywords: + Additional keyword arguments control the solution algorithm for the + differential equations. These arguments are passed on to the function + :func:`lsim`, which in turn passes them on to + :func:`scipy.integrate.odeint`. See the documentation for + :func:`scipy.integrate.odeint` for information about these + arguments. + + + Returns + ------- + T: array + Time values of the output + yout: array + Response of the system + + See Also + -------- + lsim, step, impulse + + Examples + -------- + >>> T, yout = initial(sys, T, X0) + ''' T, yout = timeresp.InitialResponse(sys, T, X0, input, output, transpose = True, **keywords) return T, yout def lsim(sys, U=0., T=None, X0=0., **keywords): + ''' + Simulate the output of a linear system. + + As a convenience for parameters `U`, `X0`: + Numbers (scalars) are converted to constant arrays with the correct shape. + The correct shape is inferred from arguments `sys` and `T`. + + Parameters + ---------- + sys: Lti (StateSpace, or TransferFunction) + LTI system to simulate + + U: array-like or number, optional + Input array giving input at each time `T` (default = 0). + + If `U` is ``None`` or ``0``, a special algorithm is used. This special + algorithm is faster than the general algorithm, which is used otherwise. + + T: array-like + Time steps at which the input is defined, numbers must be (strictly + monotonic) increasing. + + X0: array-like or number, optional + Initial condition (default = 0). + + **keywords: + Additional keyword arguments control the solution algorithm for the + differential equations. These arguments are passed on to the function + :func:`scipy.integrate.odeint`. See the documentation for + :func:`scipy.integrate.odeint` for information about these + arguments. + + Returns + ------- + T: array + Time values of the output. + yout: array + Response of the system. + xout: array + Time evolution of the state vector. + + See Also + -------- + step, initial, impulse + + Examples + -------- + >>> T, yout, xout = lsim(sys, U, T, X0) + ''' T, yout, xout = timeresp.ForcedResponse(sys, T, U, X0, transpose = True, **keywords) return T, yout, xout Modified: trunk/src/statefbk.py =================================================================== --- trunk/src/statefbk.py 2011-06-22 19:26:01 UTC (rev 164) +++ trunk/src/statefbk.py 2011-06-26 02:44:09 UTC (rev 165) @@ -328,7 +328,7 @@ raise ControlSlycot("can't find slycot module 'sb03md'") n = sys.states U = np.zeros((n,n)) - A = sys.A + A = np.array(sys.A) # convert to NumPy array for slycot X,scale,sep,ferr,w = sb03md(n, C, A, U, dico, job='X', fact='N', trana=tra) gram = X return gram Modified: trunk/src/timeresp.py =================================================================== --- trunk/src/timeresp.py 2011-06-22 19:26:01 UTC (rev 164) +++ trunk/src/timeresp.py 2011-06-26 02:44:09 UTC (rev 165) @@ -419,11 +419,11 @@ See Also -------- - lsim, initial, impulse + ForcedResponse, InitialResponse, ImpulseResponse Examples -------- - >>> T, yout = step(sys, T, X0) + >>> T, yout = StepResponse(sys, T, X0) """ sys = _convertToStateSpace(sys) sys = _mimo2siso(sys, input, output, warn_conversion=True) @@ -491,11 +491,11 @@ See Also -------- - lsim, step, impulse + ForcedResponse, ImpulseResponse, StepResponse Examples -------- - >>> T, yout = InitialResponsesys, T, X0) + >>> T, yout = InitialResponse(sys, T, X0) """ sys = _convertToStateSpace(sys) sys = _mimo2siso(sys, input, output, warn_conversion=True) @@ -564,7 +564,7 @@ See Also -------- - lsim, step, initial + ForcedReponse, InitialResponse, StepResponse Examples -------- Modified: trunk/src/xferfcn.py =================================================================== --- trunk/src/xferfcn.py 2011-06-22 19:26:01 UTC (rev 164) +++ trunk/src/xferfcn.py 2011-06-26 02:44:09 UTC (rev 165) @@ -527,22 +527,46 @@ return out - def _common_den(self): - """Compute MIMO common denominator; return it and an adjusted numerator. + def _common_den(self, imag_tol=None): + """ + Compute MIMO common denominator; return it and an adjusted numerator. + This function computes the single denominator containing all + the poles of sys.den, and reports it as the array d. The + output numerator array n is modified to use the common + denominator; the coefficient arrays are also padded with zeros + to be the same size as d. n is an sys.outputs by sys.inputs + by len(d) array. + + Parameters + ---------- + imag_tol: float + Threshold for the imaginary part of a root to use in detecting + complex poles + + Returns + ------- + num: array + Multi-dimensional array of numerator coefficients. num[i][j] + gives the numerator coefficient array for the ith input and jth + output + + den: array + Array of coefficients for common denominator polynomial + + Examples + -------- >>> n, d = sys._common_den() - - computes the single denominator containing all the poles of sys.den, and - reports it as the array d. The output numerator array n is modified to - use the common denominator; the coefficient arrays are also padded with - zeros to be the same size as d. n is an sys.outputs-by-sys.inputs-by- - len(d) array. - + """ # Machine precision for floats. eps = finfo(float).eps + # Decide on the tolerance to use in deciding of a pole is complex + if (imag_tol == None): + imag_tol = 1e-8 #! TODO: figure out the right number to use + # A sorted list to keep track of cumulative poles found as we scan # self.den. poles = [] @@ -615,8 +639,9 @@ # To prevent buildup of imaginary part error, handle complex # pole pairs together. quad = polymul([1., -poles[n]], [1., -poles[n+1]]) - assert all(quad.imag < eps), "The quadratic has a nontrivial \ -imaginary part: %g" % quad.imag.max() + assert all(quad.imag < 10 * eps), \ + "The quadratic has a nontrivial imaginary part: %g" \ + % quad.imag.max() quad = quad.real den = polymul(den, quad) @@ -747,10 +772,10 @@ raise TypeError("If sys is a StateSpace, _convertToTransferFunction \ cannot take keywords.") - # Use Slycot to make the transformation. TODO: this is still somewhat - # buggy! - tfout = tb04ad(sys.states, sys.inputs, sys.outputs, sys.A, sys.B, sys.C, - sys.D,tol1=0.0) + # Use Slycot to make the transformation + # Make sure to convert system matrices to numpy arrays + tfout = tb04ad(sys.states, sys.inputs, sys.outputs, array(sys.A), + array(sys.B), array(sys.C), array(sys.D), tol1=0.0) # Preallocate outputs. num = [[[] for j in range(sys.inputs)] for i in range(sys.outputs)] Modified: trunk/tests/convert_test.py =================================================================== --- trunk/tests/convert_test.py 2011-06-22 19:26:01 UTC (rev 164) +++ trunk/tests/convert_test.py 2011-06-26 02:44:09 UTC (rev 165) @@ -29,9 +29,9 @@ # Maximum number of states to test + 1 self.maxStates = 20 # Maximum number of inputs and outputs to test + 1 - self.maxIO = 20 + self.maxIO = 10 # Set to True to print systems to the output. - self.debug = True + self.debug = False def printSys(self, sys, ind): """Print system to the standard output.""" @@ -40,9 +40,10 @@ print "sys%i:\n" % ind print sys - def testConvert(self, verbose=0): + def testConvert(self): """Test state space to transfer function conversion.""" #Currently it only tests that a TF->SS->TF generates an unchanged TF + verbose = self.debug #print __doc__ @@ -70,12 +71,14 @@ for inputNum in range(inputs): for outputNum in range(outputs): np.testing.assert_array_almost_equal(\ - tfOriginal.num[outputNum][inputNum],\ - tfTransformed.num[outputNum][inputNum]) + tfOriginal.num[outputNum][inputNum], \ + tfTransformed.num[outputNum][inputNum], \ + err_msg='numerator mismatch') np.testing.assert_array_almost_equal(\ - tfOriginal.den[outputNum][inputNum],\ - tfTransformed.den[outputNum][inputNum]) + tfOriginal.den[outputNum][inputNum], \ + tfTransformed.den[outputNum][inputNum], + err_msg='denominator mismatch') #To test the ss systems is harder because they aren't the same #realization. This could be done with checking that they have the This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |