def test_point_to_point(self): # Machine precision for floats. eps = np.finfo(float).eps for states in range(1, self.maxStates): # Start with a random system linsys = matlab.rss(states, 1, 1) # Make sure the system is not degenerate Cmat = ctrl.ctrb(linsys.A, linsys.B) if (np.linalg.matrix_rank(Cmat) != states): if (self.debug): print(" skipping (not reachable)") continue if (self.debug): print(linsys) # Create a flat system representation flatsys = tg.LinearFlatSystem(linsys) # Generate several different initial and final conditions for i in range(self.numTests): x0 = np.random.rand(linsys.states) xf = np.random.rand(linsys.states) Tf = np.random.randn() # Generate a trajectory from start to stop systraj = tg.point_to_point(flatsys, x0, xf, Tf) xd, ud = systraj.eval((0,Tf)) np.testing.assert_array_almost_equal(x0, xd[0,:], decimal=4) np.testing.assert_array_almost_equal(xf, xd[1,:], decimal=4)
def test_discrete(self): # Test discrete time frequency response # SISO state space systems with either fixed or unspecified sampling times sys = rss(3, 1, 1) siso_ss1d = StateSpace(sys.A, sys.B, sys.C, sys.D, 0.1) siso_ss2d = StateSpace(sys.A, sys.B, sys.C, sys.D, True) # MIMO state space systems with either fixed or unspecified sampling times A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]] B = [[1., 4.], [-3., -3.], [-2., 1.]] C = [[4., 2., -3.], [1., 4., 3.]] D = [[-2., 4.], [0., 1.]] mimo_ss1d = StateSpace(A, B, C, D, 0.1) mimo_ss2d = StateSpace(A, B, C, D, True) # SISO transfer functions siso_tf1d = TransferFunction([1, 1], [1, 2, 1], 0.1) siso_tf2d = TransferFunction([1, 1], [1, 2, 1], True) # Go through each system and call the code, checking return types for sys in (siso_ss1d, siso_ss2d, mimo_ss1d, mimo_ss2d, siso_tf1d, siso_tf2d): # Set frequency range to just below Nyquist freq (for Bode) omega_ok = np.linspace(10e-4, 0.99, 100) * np.pi / sys.dt # Test frequency response ret = sys.freqresp(omega_ok) # Check for warning if frequency is out of range import warnings warnings.simplefilter('always', UserWarning) # don't supress with warnings.catch_warnings(record=True) as w: # Set up warnings filter to only show warnings in control module warnings.filterwarnings("ignore") warnings.filterwarnings("always", module="control") # Look for a warning about sampling above Nyquist frequency omega_bad = np.linspace(10e-4, 1.1, 10) * np.pi / sys.dt ret = sys.freqresp(omega_bad) print("len(w) =", len(w)) self.assertEqual(len(w), 1) self.assertIn("above", str(w[-1].message)) self.assertIn("Nyquist", str(w[-1].message)) # Test bode plots (currently only implemented for SISO) if (sys.inputs == 1 and sys.outputs == 1): # Generic call (frequency range calculated automatically) ret_ss = bode(sys) # Convert to transfer function and test bode again systf = tf(sys) ret_tf = bode(systf) # Make sure we can pass a frequency range bode(sys, omega_ok) else: # Calling bode should generate a not implemented error self.assertRaises(NotImplementedError, bode, (sys, ))
def testMinrealBrute(self): for n, m, p in permutations(range(1, 6), 3): s = matlab.rss(n, p, m) sr = s.minreal() if s.states > sr.states: self.nreductions += 1 else: np.testing.assert_array_almost_equal(np.sort(eigvals(s.A)), np.sort(eigvals(sr.A))) for i in range(m): for j in range(p): ht1 = matlab.tf( matlab.ss(s.A, s.B[:, i], s.C[j, :], s.D[j, i])) ht2 = matlab.tf( matlab.ss(sr.A, sr.B[:, i], sr.C[j, :], sr.D[j, i])) try: self.assert_numden_almost_equal( ht1.num[0][0], ht2.num[0][0], ht1.den[0][0], ht2.den[0][0]) except Exception as e: # for larger systems, the tf minreal's # the original rss, but not the balanced one if n < 6: raise e self.assertEqual(self.nreductions, 2)
def test_discrete(self): # Test discrete time frequency response # SISO state space systems with either fixed or unspecified sampling times sys = rss(3, 1, 1) siso_ss1d = StateSpace(sys.A, sys.B, sys.C, sys.D, 0.1) siso_ss2d = StateSpace(sys.A, sys.B, sys.C, sys.D, True) # MIMO state space systems with either fixed or unspecified sampling times A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]] B = [[1., 4.], [-3., -3.], [-2., 1.]] C = [[4., 2., -3.], [1., 4., 3.]] D = [[-2., 4.], [0., 1.]] mimo_ss1d = StateSpace(A, B, C, D, 0.1) mimo_ss2d = StateSpace(A, B, C, D, True) # SISO transfer functions siso_tf1d = TransferFunction([1, 1], [1, 2, 1], 0.1) siso_tf2d = TransferFunction([1, 1], [1, 2, 1], True) # Go through each system and call the code, checking return types for sys in (siso_ss1d, siso_ss2d, mimo_ss1d, mimo_ss2d, siso_tf1d, siso_tf2d): # Set frequency range to just below Nyquist freq (for Bode) omega_ok = np.linspace(10e-4,0.99,100) * np.pi/sys.dt # Test frequency response ret = sys.freqresp(omega_ok) # Check for warning if frequency is out of range import warnings warnings.simplefilter('always', UserWarning) # don't supress with warnings.catch_warnings(record=True) as w: # Set up warnings filter to only show warnings in control module warnings.filterwarnings("ignore") warnings.filterwarnings("always", module="control") # Look for a warning about sampling above Nyquist frequency omega_bad = np.linspace(10e-4,1.1,10) * np.pi/sys.dt ret = sys.freqresp(omega_bad) print("len(w) =", len(w)) self.assertEqual(len(w), 1) self.assertIn("above", str(w[-1].message)) self.assertIn("Nyquist", str(w[-1].message)) # Test bode plots (currently only implemented for SISO) if (sys.inputs == 1 and sys.outputs == 1): # Generic call (frequency range calculated automatically) ret_ss = bode(sys) # Convert to transfer function and test bode again systf = tf(sys); ret_tf = bode(systf) # Make sure we can pass a frequency range bode(sys, omega_ok) else: # Calling bode should generate a not implemented error self.assertRaises(NotImplementedError, bode, (sys,))
def setUp(self): """Set up a SISO and MIMO system to test operations on.""" # Single input, single output continuous and discrete time systems sys = matlab.rss(3, 1, 1) self.siso_ss1 = StateSpace(sys.A, sys.B, sys.C, sys.D) self.siso_ss1c = StateSpace(sys.A, sys.B, sys.C, sys.D, 0.0) self.siso_ss1d = StateSpace(sys.A, sys.B, sys.C, sys.D, 0.1) self.siso_ss2d = StateSpace(sys.A, sys.B, sys.C, sys.D, 0.2) self.siso_ss3d = StateSpace(sys.A, sys.B, sys.C, sys.D, True) # Two input, two output continuous time system A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]] B = [[1., 4.], [-3., -3.], [-2., 1.]] C = [[4., 2., -3.], [1., 4., 3.]] D = [[-2., 4.], [0., 1.]] self.mimo_ss1 = StateSpace(A, B, C, D) self.mimo_ss1c = StateSpace(A, B, C, D, 0) # Two input, two output discrete time system self.mimo_ss1d = StateSpace(A, B, C, D, 0.1) # Same system, but with a different sampling time self.mimo_ss2d = StateSpace(A, B, C, D, 0.2) # Single input, single output continuus and discrete transfer function self.siso_tf1 = TransferFunction([1, 1], [1, 2, 1]) self.siso_tf1c = TransferFunction([1, 1], [1, 2, 1], 0) self.siso_tf1d = TransferFunction([1, 1], [1, 2, 1], 0.1) self.siso_tf2d = TransferFunction([1, 1], [1, 2, 1], 0.2) self.siso_tf3d = TransferFunction([1, 1], [1, 2, 1], True)
def test_point_to_point(self): # Machine precision for floats. eps = np.finfo(float).eps for states in range(1, self.maxStates): # Start with a random system linsys = matlab.rss(states, 1, 1) # Make sure the system is not degenerate Cmat = ctrl.ctrb(linsys.A, linsys.B) if (np.linalg.matrix_rank(Cmat) != states): if (self.debug): print(" skipping (not reachable)") continue if (self.debug): print(linsys) # Create a flat system representation flatsys = tg.LinearFlatSystem(linsys) # Generate several different initial and final conditions for i in range(self.numTests): x0 = np.random.rand(linsys.states) xf = np.random.rand(linsys.states) Tf = np.random.randn() # Generate a trajectory from start to stop systraj = tg.point_to_point(flatsys, x0, xf, Tf) xd, ud = systraj.eval((0, Tf)) np.testing.assert_array_almost_equal(x0, xd[0, :], decimal=4) np.testing.assert_array_almost_equal(xf, xd[1, :], decimal=4)
def testMinrealBrute(self): for n, m, p in permutations(range(1,6), 3): s = matlab.rss(n, p, m) sr = s.minreal() if s.states > sr.states: self.nreductions += 1 else: np.testing.assert_array_almost_equal( np.sort(eigvals(s.A)), np.sort(eigvals(sr.A))) for i in range(m): for j in range(p): ht1 = matlab.tf( matlab.ss(s.A, s.B[:,i], s.C[j,:], s.D[j,i])) ht2 = matlab.tf( matlab.ss(sr.A, sr.B[:,i], sr.C[j,:], sr.D[j,i])) try: self.assert_numden_almost_equal( ht1.num[0][0], ht2.num[0][0], ht1.den[0][0], ht2.den[0][0]) except Exception as e: # for larger systems, the tf minreal's # the original rss, but not the balanced one if n < 6: raise e self.assertEqual(self.nreductions, 2)
def test_shape(self): """Test that rss outputs have the right state, input, and output size.""" for states in range(1, self.maxStates): for inputs in range(1, self.maxIO): for outputs in range(1, self.maxIO): sys = matlab.rss(states, outputs, inputs) self.assertEqual(sys.states, states) self.assertEqual(sys.inputs, inputs) self.assertEqual(sys.outputs, outputs)
def testPole(self): """Test that the poles of rss outputs have a negative real part.""" for states in range(1, self.maxStates): for inputs in range(1, self.maxIO): for outputs in range(1, self.maxIO): sys = matlab.rss(states, outputs, inputs) p = sys.pole() for z in p: self.assertTrue(z.real < 0)
def testTF(self, verbose=False): """ Directly tests the functions tb04ad and td04ad through direct comparison of transfer function coefficients. Similar to convert_test, but tests at a lower level. """ from slycot import tb04ad, td04ad for states in range(1, self.maxStates): for inputs in range(1, self.maxI + 1): for outputs in range(1, self.maxO + 1): for testNum in range(self.numTests): ssOriginal = matlab.rss(states, outputs, inputs) if (verbose): print('====== Original SS ==========') print(ssOriginal) print('states=', states) print('inputs=', inputs) print('outputs=', outputs) tfOriginal_Actrb, tfOriginal_Bctrb, tfOriginal_Cctrb, tfOrigingal_nctrb, tfOriginal_index,\ tfOriginal_dcoeff, tfOriginal_ucoeff = tb04ad(states,inputs,outputs,\ ssOriginal.A,ssOriginal.B,ssOriginal.C,ssOriginal.D,tol1=0.0) ssTransformed_nr, ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D\ = td04ad('R',inputs,outputs,tfOriginal_index,tfOriginal_dcoeff,tfOriginal_ucoeff,tol=0.0) tfTransformed_Actrb, tfTransformed_Bctrb, tfTransformed_Cctrb, tfTransformed_nctrb,\ tfTransformed_index, tfTransformed_dcoeff, tfTransformed_ucoeff = tb04ad(ssTransformed_nr,\ inputs,outputs,ssTransformed_A, ssTransformed_B, ssTransformed_C,ssTransformed_D,tol1=0.0) #print 'size(Trans_A)=',ssTransformed_A.shape if (verbose): print('===== Transformed SS ==========') print( matlab.ss(ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D)) # print 'Trans_nr=',ssTransformed_nr # print 'tfOrig_index=',tfOriginal_index # print 'tfOrig_ucoeff=',tfOriginal_ucoeff # print 'tfOrig_dcoeff=',tfOriginal_dcoeff # print 'tfTrans_index=',tfTransformed_index # print 'tfTrans_ucoeff=',tfTransformed_ucoeff # print 'tfTrans_dcoeff=',tfTransformed_dcoeff #Compare the TF directly, must match #numerators np.testing.assert_array_almost_equal( tfOriginal_ucoeff, tfTransformed_ucoeff, decimal=3) #denominators np.testing.assert_array_almost_equal( tfOriginal_dcoeff, tfTransformed_dcoeff, decimal=3)
def dsystem_dt(request): """Test systems for test_discrete""" # SISO state space systems with either fixed or unspecified sampling times sys = rss(3, 1, 1) # MIMO state space systems with either fixed or unspecified sampling times A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]] B = [[1., 4.], [-3., -3.], [-2., 1.]] C = [[4., 2., -3.], [1., 4., 3.]] D = [[-2., 4.], [0., 1.]] dt = request.param systems = {'sssiso': StateSpace(sys.A, sys.B, sys.C, sys.D, dt), 'ssmimo': StateSpace(A, B, C, D, dt), 'tf': TransferFunction([1, 1], [1, 2, 1], dt)} return systems
def testFreqResp(self): """Compare the bode reponses of the SS systems and TF systems to the original SS They generally are different realizations but have same freq resp. Currently this test may only be applied to SISO systems. """ for states in range(1,self.maxStates): for testNum in range(self.numTests): for inputs in range(1,1): for outputs in range(1,1): ssOriginal = matlab.rss(states, inputs, outputs) tfOriginal_Actrb, tfOriginal_Bctrb, tfOriginal_Cctrb, tfOrigingal_nctrb, tfOriginal_index,\ tfOriginal_dcoeff, tfOriginal_ucoeff = tb04ad(states,inputs,outputs,\ ssOriginal.A,ssOriginal.B,ssOriginal.C,ssOriginal.D,tol1=0.0) ssTransformed_nr, ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D\ = td04ad('R',inputs,outputs,tfOriginal_index,tfOriginal_dcoeff,tfOriginal_ucoeff,tol=0.0) tfTransformed_Actrb, tfTransformed_Bctrb, tfTransformed_Cctrb, tfTransformed_nctrb,\ tfTransformed_index, tfTransformed_dcoeff, tfTransformed_ucoeff = tb04ad(\ ssTransformed_nr,inputs,outputs,ssTransformed_A, ssTransformed_B, ssTransformed_C,\ ssTransformed_D,tol1=0.0) numTransformed = np.array(tfTransformed_ucoeff) denTransformed = np.array(tfTransformed_dcoeff) numOriginal = np.array(tfOriginal_ucoeff) denOriginal = np.array(tfOriginal_dcoeff) ssTransformed = matlab.ss(ssTransformed_A,ssTransformed_B,ssTransformed_C,ssTransformed_D) for inputNum in range(inputs): for outputNum in range(outputs): [ssOriginalMag,ssOriginalPhase,freq] = matlab.bode(ssOriginal,Plot=False) [tfOriginalMag,tfOriginalPhase,freq] = matlab.bode(matlab.tf(numOriginal[outputNum][inputNum],denOriginal[outputNum]),Plot=False) [ssTransformedMag,ssTransformedPhase,freq] = matlab.bode(ssTransformed,freq,Plot=False) [tfTransformedMag,tfTransformedPhase,freq] = matlab.bode(matlab.tf(numTransformed[outputNum][inputNum],denTransformed[outputNum]),freq,Plot=False) #print 'numOrig=',numOriginal[outputNum][inputNum] #print 'denOrig=',denOriginal[outputNum] #print 'numTrans=',numTransformed[outputNum][inputNum] #print 'denTrans=',denTransformed[outputNum] np.testing.assert_array_almost_equal(ssOriginalMag,tfOriginalMag,decimal=3) np.testing.assert_array_almost_equal(ssOriginalPhase,tfOriginalPhase,decimal=3) np.testing.assert_array_almost_equal(ssOriginalMag,ssTransformedMag,decimal=3) np.testing.assert_array_almost_equal(ssOriginalPhase,ssTransformedPhase,decimal=3) np.testing.assert_array_almost_equal(tfOriginalMag,tfTransformedMag,decimal=3) np.testing.assert_array_almost_equal(tfOriginalPhase,tfTransformedPhase,decimal=2)
def testTF(self, verbose=False): """ Directly tests the functions tb04ad and td04ad through direct comparison of transfer function coefficients. Similar to convert_test, but tests at a lower level. """ from slycot import tb04ad, td04ad for states in range(1, self.maxStates): for inputs in range(1, self.maxI + 1): for outputs in range(1, self.maxO + 1): for testNum in range(self.numTests): ssOriginal = matlab.rss(states, outputs, inputs) if (verbose): print('====== Original SS ==========') print(ssOriginal) print('states=', states) print('inputs=', inputs) print('outputs=', outputs) tfOriginal_Actrb, tfOriginal_Bctrb, tfOriginal_Cctrb,\ tfOrigingal_nctrb, tfOriginal_index,\ tfOriginal_dcoeff, tfOriginal_ucoeff =\ tb04ad(states, inputs, outputs, ssOriginal.A, ssOriginal.B, ssOriginal.C, ssOriginal.D, tol1=0.0) ssTransformed_nr, ssTransformed_A, ssTransformed_B,\ ssTransformed_C, ssTransformed_D\ = td04ad('R', inputs, outputs, tfOriginal_index, tfOriginal_dcoeff, tfOriginal_ucoeff, tol=0.0) tfTransformed_Actrb, tfTransformed_Bctrb,\ tfTransformed_Cctrb, tfTransformed_nctrb,\ tfTransformed_index, tfTransformed_dcoeff,\ tfTransformed_ucoeff = tb04ad( ssTransformed_nr, inputs, outputs, ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D, tol1=0.0) # print('size(Trans_A)=',ssTransformed_A.shape) if (verbose): print('===== Transformed SS ==========') print( matlab.ss(ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D))
def testTF(self, verbose=False): """ Directly tests the functions tb04ad and td04ad through direct comparison of transfer function coefficients. Similar to convert_test, but tests at a lower level. """ from slycot import tb04ad, td04ad for states in range(1, self.maxStates): for inputs in range(1, self.maxI+1): for outputs in range(1, self.maxO+1): for testNum in range(self.numTests): ssOriginal = matlab.rss(states, inputs, outputs) if (verbose): print('====== Original SS ==========') print(ssOriginal) print('states=', states) print('inputs=', inputs) print('outputs=', outputs) tfOriginal_Actrb, tfOriginal_Bctrb, tfOriginal_Cctrb, tfOrigingal_nctrb, tfOriginal_index,\ tfOriginal_dcoeff, tfOriginal_ucoeff = tb04ad(states,inputs,outputs,\ ssOriginal.A,ssOriginal.B,ssOriginal.C,ssOriginal.D,tol1=0.0) ssTransformed_nr, ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D\ = td04ad('R',inputs,outputs,tfOriginal_index,tfOriginal_dcoeff,tfOriginal_ucoeff,tol=0.0) tfTransformed_Actrb, tfTransformed_Bctrb, tfTransformed_Cctrb, tfTransformed_nctrb,\ tfTransformed_index, tfTransformed_dcoeff, tfTransformed_ucoeff = tb04ad(ssTransformed_nr,\ inputs,outputs,ssTransformed_A, ssTransformed_B, ssTransformed_C,ssTransformed_D,tol1=0.0) #print 'size(Trans_A)=',ssTransformed_A.shape if (verbose): print('===== Transformed SS ==========') print(matlab.ss(ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D)) # print 'Trans_nr=',ssTransformed_nr # print 'tfOrig_index=',tfOriginal_index # print 'tfOrig_ucoeff=',tfOriginal_ucoeff # print 'tfOrig_dcoeff=',tfOriginal_dcoeff # print 'tfTrans_index=',tfTransformed_index # print 'tfTrans_ucoeff=',tfTransformed_ucoeff # print 'tfTrans_dcoeff=',tfTransformed_dcoeff #Compare the TF directly, must match #numerators np.testing.assert_array_almost_equal(tfOriginal_ucoeff,tfTransformed_ucoeff,decimal=3) #denominators np.testing.assert_array_almost_equal(tfOriginal_dcoeff,tfTransformed_dcoeff,decimal=3)
def testTF(self, verbose=False): """ Directly tests the functions tb04ad and td04ad through direct comparison of transfer function coefficients. Similar to convert_test, but tests at a lower level. """ from slycot import tb04ad, td04ad for states in range(1, self.maxStates): for inputs in range(1, self.maxI+1): for outputs in range(1, self.maxO+1): for testNum in range(self.numTests): ssOriginal = matlab.rss(states, outputs, inputs) if (verbose): print('====== Original SS ==========') print(ssOriginal) print('states=', states) print('inputs=', inputs) print('outputs=', outputs) tfOriginal_Actrb, tfOriginal_Bctrb, tfOriginal_Cctrb,\ tfOrigingal_nctrb, tfOriginal_index,\ tfOriginal_dcoeff, tfOriginal_ucoeff =\ tb04ad(states, inputs, outputs, ssOriginal.A, ssOriginal.B, ssOriginal.C, ssOriginal.D, tol1=0.0) ssTransformed_nr, ssTransformed_A, ssTransformed_B,\ ssTransformed_C, ssTransformed_D\ = td04ad('R', inputs, outputs, tfOriginal_index, tfOriginal_dcoeff, tfOriginal_ucoeff, tol=0.0) tfTransformed_Actrb, tfTransformed_Bctrb,\ tfTransformed_Cctrb, tfTransformed_nctrb,\ tfTransformed_index, tfTransformed_dcoeff,\ tfTransformed_ucoeff = tb04ad( ssTransformed_nr, inputs, outputs, ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D, tol1=0.0) # print('size(Trans_A)=',ssTransformed_A.shape) if (verbose): print('===== Transformed SS ==========') print(matlab.ss(ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D))
def testMinrealBrute(self): for n, m, p in permutations(range(1, 6), 3): s = matlab.rss(n, p, m) sr = s.minreal() if s.states > sr.states: self.nreductions += 1 else: # Check to make sure that poles and zeros match # For poles, just look at eigenvalues of A np.testing.assert_array_almost_equal(np.sort(eigvals(s.A)), np.sort(eigvals(sr.A))) # For zeros, need to extract SISO systems for i in range(m): for j in range(p): # Extract SISO dynamixs from input i to output j s1 = matlab.ss(s.A, s.B[:, i], s.C[j, :], s.D[j, i]) s2 = matlab.ss(sr.A, sr.B[:, i], sr.C[j, :], sr.D[j, i]) # Check that the zeros match # Note: sorting doesn't work => have to do the hard way z1 = matlab.zero(s1) z2 = matlab.zero(s2) # Start by making sure we have the same # of zeros self.assertEqual(len(z1), len(z2)) # Make sure all zeros in s1 are in s2 for zero in z1: # Find the closest zero self.assertAlmostEqual(min(abs(z2 - zero)), 0.) # Make sure all zeros in s2 are in s1 for zero in z2: # Find the closest zero self.assertAlmostEqual(min(abs(z1 - zero)), 0.) # Make sure that the number of systems reduced is as expected # (Need to update this number if you change the seed at top of file) self.assertEqual(self.nreductions, 2)
def testMinrealBrute(self): for n, m, p in permutations(range(1,6), 3): s = matlab.rss(n, p, m) sr = s.minreal() if s.states > sr.states: self.nreductions += 1 else: # Check to make sure that poles and zeros match # For poles, just look at eigenvalues of A np.testing.assert_array_almost_equal( np.sort(eigvals(s.A)), np.sort(eigvals(sr.A))) # For zeros, need to extract SISO systems for i in range(m): for j in range(p): # Extract SISO dynamixs from input i to output j s1 = matlab.ss(s.A, s.B[:,i], s.C[j,:], s.D[j,i]) s2 = matlab.ss(sr.A, sr.B[:,i], sr.C[j,:], sr.D[j,i]) # Check that the zeros match # Note: sorting doesn't work => have to do the hard way z1 = matlab.zero(s1) z2 = matlab.zero(s2) # Start by making sure we have the same # of zeros self.assertEqual(len(z1), len(z2)) # Make sure all zeros in s1 are in s2 for zero in z1: # Find the closest zero self.assertAlmostEqual(min(abs(z2 - zero)), 0.) # Make sure all zeros in s2 are in s1 for zero in z2: # Find the closest zero self.assertAlmostEqual(min(abs(z1 - zero)), 0.) # Make sure that the number of systems reduced is as expected # (Need to update this number if you change the seed at top of file) self.assertEqual(self.nreductions, 2)
def testFreqResp(self): """Compare the bode reponses of the SS systems and TF systems to the original SS They generally are different realizations but have same freq resp. Currently this test may only be applied to SISO systems. """ from slycot import tb04ad, td04ad for states in range(1, self.maxStates): for testNum in range(self.numTests): for inputs in range(1, 1): for outputs in range(1, 1): ssOriginal = matlab.rss(states, outputs, inputs) tfOriginal_Actrb, tfOriginal_Bctrb, tfOriginal_Cctrb,\ tfOrigingal_nctrb, tfOriginal_index,\ tfOriginal_dcoeff, tfOriginal_ucoeff = tb04ad( states, inputs, outputs, ssOriginal.A, ssOriginal.B, ssOriginal.C, ssOriginal.D, tol1=0.0) ssTransformed_nr, ssTransformed_A, ssTransformed_B,\ ssTransformed_C, ssTransformed_D\ = td04ad('R', inputs, outputs, tfOriginal_index, tfOriginal_dcoeff, tfOriginal_ucoeff, tol=0.0) tfTransformed_Actrb, tfTransformed_Bctrb,\ tfTransformed_Cctrb, tfTransformed_nctrb,\ tfTransformed_index, tfTransformed_dcoeff,\ tfTransformed_ucoeff = tb04ad( ssTransformed_nr, inputs, outputs, ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D, tol1=0.0) numTransformed = np.array(tfTransformed_ucoeff) denTransformed = np.array(tfTransformed_dcoeff) numOriginal = np.array(tfOriginal_ucoeff) denOriginal = np.array(tfOriginal_dcoeff) ssTransformed = matlab.ss(ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D) for inputNum in range(inputs): for outputNum in range(outputs): [ssOriginalMag, ssOriginalPhase, freq] =\ matlab.bode(ssOriginal, plot=False) [tfOriginalMag, tfOriginalPhase, freq] =\ matlab.bode(matlab.tf( numOriginal[outputNum][inputNum], denOriginal[outputNum]), plot=False) [ssTransformedMag, ssTransformedPhase, freq] =\ matlab.bode(ssTransformed, freq, plot=False) [tfTransformedMag, tfTransformedPhase, freq] =\ matlab.bode(matlab.tf( numTransformed[outputNum][inputNum], denTransformed[outputNum]), freq, plot=False) # print('numOrig=', # numOriginal[outputNum][inputNum]) # print('denOrig=', # denOriginal[outputNum]) # print('numTrans=', # numTransformed[outputNum][inputNum]) # print('denTrans=', # denTransformed[outputNum]) np.testing.assert_array_almost_equal( ssOriginalMag, tfOriginalMag, decimal=3) np.testing.assert_array_almost_equal( ssOriginalPhase, tfOriginalPhase, decimal=3) np.testing.assert_array_almost_equal( ssOriginalMag, ssTransformedMag, decimal=3) np.testing.assert_array_almost_equal( ssOriginalPhase, ssTransformedPhase, decimal=3) np.testing.assert_array_almost_equal( tfOriginalMag, tfTransformedMag, decimal=3) np.testing.assert_array_almost_equal( tfOriginalPhase, tfTransformedPhase, decimal=2)
from control import tf from control.matlab import rss from numpy import logspace from timeit import timeit nstates = 10 sys = rss(nstates) sys_tf = tf(sys) w = logspace(-1, 1, 50) ntimes = 1000 time_ss = timeit("sys.freqresp(w)", setup="from __main__ import sys, w", number=ntimes) time_tf = timeit("sys_tf.freqresp(w)", setup="from __main__ import sys_tf, w", number=ntimes) print("State-space model on %d states: %f" % (nstates, time_ss)) print("Transfer-function model on %d states: %f" % (nstates, time_tf))
def testConvert(self): """Test state space to transfer function conversion.""" verbose = self.debug from control.statesp import _mimo2siso #print __doc__ # Machine precision for floats. eps = np.finfo(float).eps for states in range(1, self.maxStates): for inputs in range(1, self.maxIO): for outputs in range(1, self.maxIO): # start with a random SS system and transform to TF then # back to SS, check that the matrices are the same. ssOriginal = matlab.rss(states, outputs, inputs) if (verbose): self.printSys(ssOriginal, 1) # Make sure the system is not degenerate Cmat = control.ctrb(ssOriginal.A, ssOriginal.B) if (np.linalg.matrix_rank(Cmat) != states): if (verbose): print(" skipping (not reachable)") continue Omat = control.obsv(ssOriginal.A, ssOriginal.C) if (np.linalg.matrix_rank(Omat) != states): if (verbose): print(" skipping (not observable)") continue tfOriginal = matlab.tf(ssOriginal) if (verbose): self.printSys(tfOriginal, 2) ssTransformed = matlab.ss(tfOriginal) if (verbose): self.printSys(ssTransformed, 3) tfTransformed = matlab.tf(ssTransformed) if (verbose): self.printSys(tfTransformed, 4) # Check to see if the state space systems have same dim if (ssOriginal.states != ssTransformed.states): print("WARNING: state space dimension mismatch: " + \ "%d versus %d" % \ (ssOriginal.states, ssTransformed.states)) # Now make sure the frequency responses match # Since bode() only handles SISO, go through each I/O pair # For phase, take sine and cosine to avoid +/- 360 offset for inputNum in range(inputs): for outputNum in range(outputs): if (verbose): print("Checking input %d, output %d" \ % (inputNum, outputNum)) ssorig_mag, ssorig_phase, ssorig_omega = \ control.bode(_mimo2siso(ssOriginal, \ inputNum, outputNum), \ deg=False, Plot=False) ssorig_real = ssorig_mag * np.cos(ssorig_phase) ssorig_imag = ssorig_mag * np.sin(ssorig_phase) # # Make sure TF has same frequency response # num = tfOriginal.num[outputNum][inputNum] den = tfOriginal.den[outputNum][inputNum] tforig = control.tf(num, den) tforig_mag, tforig_phase, tforig_omega = \ control.bode(tforig, ssorig_omega, \ deg=False, Plot=False) tforig_real = tforig_mag * np.cos(tforig_phase) tforig_imag = tforig_mag * np.sin(tforig_phase) np.testing.assert_array_almost_equal( \ ssorig_real, tforig_real) np.testing.assert_array_almost_equal( \ ssorig_imag, tforig_imag) # # Make sure xform'd SS has same frequency response # ssxfrm_mag, ssxfrm_phase, ssxfrm_omega = \ control.bode(_mimo2siso(ssTransformed, \ inputNum, outputNum), \ ssorig_omega, \ deg=False, Plot=False) ssxfrm_real = ssxfrm_mag * np.cos(ssxfrm_phase) ssxfrm_imag = ssxfrm_mag * np.sin(ssxfrm_phase) np.testing.assert_array_almost_equal( \ ssorig_real, ssxfrm_real) np.testing.assert_array_almost_equal( \ ssorig_imag, ssxfrm_imag) # # Make sure xform'd TF has same frequency response # num = tfTransformed.num[outputNum][inputNum] den = tfTransformed.den[outputNum][inputNum] tfxfrm = control.tf(num, den) tfxfrm_mag, tfxfrm_phase, tfxfrm_omega = \ control.bode(tfxfrm, ssorig_omega, \ deg=False, Plot=False) tfxfrm_real = tfxfrm_mag * np.cos(tfxfrm_phase) tfxfrm_imag = tfxfrm_mag * np.sin(tfxfrm_phase) np.testing.assert_array_almost_equal( \ ssorig_real, tfxfrm_real) np.testing.assert_array_almost_equal( \ ssorig_imag, tfxfrm_imag)
def testConvert(self): """Test state space to transfer function conversion.""" verbose = self.debug # print __doc__ # Machine precision for floats. # eps = np.finfo(float).eps for states in range(1, self.maxStates): for inputs in range(1, self.maxIO): for outputs in range(1, self.maxIO): # start with a random SS system and transform to TF then # back to SS, check that the matrices are the same. ssOriginal = matlab.rss(states, outputs, inputs) if (verbose): self.printSys(ssOriginal, 1) # Make sure the system is not degenerate Cmat = ctrb(ssOriginal.A, ssOriginal.B) if (np.linalg.matrix_rank(Cmat) != states): if (verbose): print(" skipping (not reachable)") continue Omat = obsv(ssOriginal.A, ssOriginal.C) if (np.linalg.matrix_rank(Omat) != states): if (verbose): print(" skipping (not observable)") continue tfOriginal = matlab.tf(ssOriginal) if (verbose): self.printSys(tfOriginal, 2) ssTransformed = matlab.ss(tfOriginal) if (verbose): self.printSys(ssTransformed, 3) tfTransformed = matlab.tf(ssTransformed) if (verbose): self.printSys(tfTransformed, 4) # Check to see if the state space systems have same dim if (ssOriginal.states != ssTransformed.states): print("WARNING: state space dimension mismatch: " + \ "%d versus %d" % \ (ssOriginal.states, ssTransformed.states)) # Now make sure the frequency responses match # Since bode() only handles SISO, go through each I/O pair # For phase, take sine and cosine to avoid +/- 360 offset for inputNum in range(inputs): for outputNum in range(outputs): if (verbose): print("Checking input %d, output %d" \ % (inputNum, outputNum)) ssorig_mag, ssorig_phase, ssorig_omega = \ bode(_mimo2siso(ssOriginal, \ inputNum, outputNum), \ deg=False, plot=False) ssorig_real = ssorig_mag * np.cos(ssorig_phase) ssorig_imag = ssorig_mag * np.sin(ssorig_phase) # # Make sure TF has same frequency response # num = tfOriginal.num[outputNum][inputNum] den = tfOriginal.den[outputNum][inputNum] tforig = tf(num, den) tforig_mag, tforig_phase, tforig_omega = \ bode(tforig, ssorig_omega, \ deg=False, plot=False) tforig_real = tforig_mag * np.cos(tforig_phase) tforig_imag = tforig_mag * np.sin(tforig_phase) np.testing.assert_array_almost_equal( \ ssorig_real, tforig_real) np.testing.assert_array_almost_equal( \ ssorig_imag, tforig_imag) # # Make sure xform'd SS has same frequency response # ssxfrm_mag, ssxfrm_phase, ssxfrm_omega = \ bode(_mimo2siso(ssTransformed, \ inputNum, outputNum), \ ssorig_omega, \ deg=False, plot=False) ssxfrm_real = ssxfrm_mag * np.cos(ssxfrm_phase) ssxfrm_imag = ssxfrm_mag * np.sin(ssxfrm_phase) np.testing.assert_array_almost_equal( \ ssorig_real, ssxfrm_real) np.testing.assert_array_almost_equal( \ ssorig_imag, ssxfrm_imag) # # Make sure xform'd TF has same frequency response # num = tfTransformed.num[outputNum][inputNum] den = tfTransformed.den[outputNum][inputNum] tfxfrm = tf(num, den) tfxfrm_mag, tfxfrm_phase, tfxfrm_omega = \ bode(tfxfrm, ssorig_omega, \ deg=False, plot=False) tfxfrm_real = tfxfrm_mag * np.cos(tfxfrm_phase) tfxfrm_imag = tfxfrm_mag * np.sin(tfxfrm_phase) np.testing.assert_array_almost_equal( \ ssorig_real, tfxfrm_real) np.testing.assert_array_almost_equal( \ ssorig_imag, tfxfrm_imag)
C = np.matrix('0.5, 0.6875, 0.7031, 0.5') D = np.matrix('0.') # The full system fsys = StateSpace(A,B,C,D) # The reduced system, truncating the order by 1 ord = 3 rsys = msimp.balred(fsys,ord, method = 'truncate') # Comparison of the step responses of the full and reduced systems plt.figure(1) y, t = mt.step(fsys) yr, tr = mt.step(rsys) plt.plot(t.T, y.T) plt.plot(tr.T, yr.T) # Repeat balanced reduction, now with 100-dimensional random state space sysrand = mt.rss(100, 1, 1) rsysrand = msimp.balred(sysrand,10,method ='truncate') # Comparison of the impulse responses of the full and reduced random systems plt.figure(2) yrand, trand = mt.impulse(sysrand) yrandr, trandr = mt.impulse(rsysrand) plt.plot(trand.T, yrand.T, trandr.T, yrandr.T) if 'PYCONTROL_TEST_EXAMPLES' not in os.environ: plt.show()
B = np.array([[2.], [0.], [0.], [0.]]) C = np.array([[0.5, 0.6875, 0.7031, 0.5]]) D = np.array([[0.]]) # The full system fsys = StateSpace(A, B, C, D) # The reduced system, truncating the order by 1 n = 3 rsys = msimp.balred(fsys, n, method='truncate') # Comparison of the step responses of the full and reduced systems plt.figure(1) y, t = mt.step(fsys) yr, tr = mt.step(rsys) plt.plot(t.T, y.T) plt.plot(tr.T, yr.T) # Repeat balanced reduction, now with 100-dimensional random state space sysrand = mt.rss(100, 1, 1) rsysrand = msimp.balred(sysrand, 10, method='truncate') # Comparison of the impulse responses of the full and reduced random systems plt.figure(2) yrand, trand = mt.impulse(sysrand) yrandr, trandr = mt.impulse(rsysrand) plt.plot(trand.T, yrand.T, trandr.T, yrandr.T) if 'PYCONTROL_TEST_EXAMPLES' not in os.environ: plt.show()
def test_discrete(self): # Test discrete time frequency response # SISO state space systems with either fixed or unspecified sampling times sys = rss(3, 1, 1) siso_ss1d = StateSpace(sys.A, sys.B, sys.C, sys.D, 0.1) siso_ss2d = StateSpace(sys.A, sys.B, sys.C, sys.D, True) # MIMO state space systems with either fixed or unspecified sampling times A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]] B = [[1., 4.], [-3., -3.], [-2., 1.]] C = [[4., 2., -3.], [1., 4., 3.]] D = [[-2., 4.], [0., 1.]] mimo_ss1d = StateSpace(A, B, C, D, 0.1) mimo_ss2d = StateSpace(A, B, C, D, True) # SISO transfer functions siso_tf1d = TransferFunction([1, 1], [1, 2, 1], 0.1) siso_tf2d = TransferFunction([1, 1], [1, 2, 1], True) # Go through each system and call the code, checking return types for sys in (siso_ss1d, siso_ss2d, mimo_ss1d, mimo_ss2d, siso_tf1d, siso_tf2d): # Set frequency range to just below Nyquist freq (for Bode) omega_ok = np.linspace(10e-4, 0.99, 100) * np.pi / sys.dt # Test frequency response ret = sys.freqresp(omega_ok) # Check for warning if frequency is out of range import warnings warnings.simplefilter('always', UserWarning) # don't supress with warnings.catch_warnings(record=True) as w: # Set up warnings filter to only show warnings in control module warnings.filterwarnings("ignore") warnings.filterwarnings("always", module="control") # Look for a warning about sampling above Nyquist frequency omega_bad = np.linspace(10e-4, 1.1, 10) * np.pi / sys.dt ret = sys.freqresp(omega_bad) print("len(w) =", len(w)) self.assertEqual(len(w), 1) self.assertIn("above", str(w[-1].message)) self.assertIn("Nyquist", str(w[-1].message)) # Test bode plots (currently only implemented for SISO) if (sys.inputs == 1 and sys.outputs == 1): # Generic call (frequency range calculated automatically) ret_ss = bode(sys) # Convert to transfer function and test bode again systf = tf(sys) ret_tf = bode(systf) # Make sure we can pass a frequency range bode(sys, omega_ok) else: # Calling bode should generate a not implemented error self.assertRaises(NotImplementedError, bode, (sys, )) def test_options(self): """Test ability to set parameter values""" # Generate a Bode plot of a transfer function sys = ctrl.tf([1000], [1, 25, 100, 0]) fig1 = plt.figure() ctrl.bode_plot(sys, dB=False, deg=True, Hz=False) # Save the parameter values left1, right1 = fig1.axes[0].xaxis.get_data_interval() numpoints1 = len(fig1.axes[0].lines[0].get_data()[0]) # Same transfer function, but add a decade on each end ctrl.config.set_defaults('freqplot', feature_periphery_decades=2) fig2 = plt.figure() ctrl.bode_plot(sys, dB=False, deg=True, Hz=False) left2, right2 = fig2.axes[0].xaxis.get_data_interval() # Make sure we got an extra decade on each end self.assertAlmostEqual(left2, 0.1 * left1) self.assertAlmostEqual(right2, 10 * right1) # Same transfer function, but add more points to the plot ctrl.config.set_defaults('freqplot', feature_periphery_decades=2, number_of_samples=13) fig3 = plt.figure() ctrl.bode_plot(sys, dB=False, deg=True, Hz=False) numpoints3 = len(fig3.axes[0].lines[0].get_data()[0]) # Make sure we got the right number of points self.assertNotEqual(numpoints1, numpoints3) self.assertEqual(numpoints3, 13) # Reset default parameters to avoid contamination ctrl.config.reset_defaults()
def testRss(self): """Call rss()""" rss(1) rss(2) rss(2, 1, 3)
from control import tf from control.matlab import rss from numpy import logspace from timeit import timeit nstates = 10 sys = rss(nstates) sys_tf = tf(sys) w = logspace(-1,1,50) ntimes = 1000 time_ss = timeit("sys.freqresp(w)", setup="from __main__ import sys, w", number=ntimes) time_tf = timeit("sys_tf.freqresp(w)", setup="from __main__ import sys_tf, w", number=ntimes) print("State-space model on %d states: %f" % (nstates, time_ss)) print("Transfer-function model on %d states: %f" % (nstates, time_tf))