def test_interconnect_docstring(): """Test the examples from the interconnect() docstring""" # MIMO interconnection (note: use [C, P] instead of [P, C] for state order) P = ct.LinearIOSystem( ct.rss(2, 2, 2, strictly_proper=True), name='P') C = ct.LinearIOSystem(ct.rss(2, 2, 2), name='C') T = ct.interconnect( [C, P], connections = [ ['P.u[0]', 'C.y[0]'], ['P.u[1]', 'C.y[1]'], ['C.u[0]', '-P.y[0]'], ['C.u[1]', '-P.y[1]']], inplist = ['C.u[0]', 'C.u[1]'], outlist = ['P.y[0]', 'P.y[1]'], ) T_ss = ct.feedback(P * C, ct.ss([], [], [], np.eye(2))) np.testing.assert_almost_equal(T.A, T_ss.A) np.testing.assert_almost_equal(T.B, T_ss.B) np.testing.assert_almost_equal(T.C, T_ss.C) np.testing.assert_almost_equal(T.D, T_ss.D) # Implicit interconnection (note: use [C, P, sumblk] for proper state order) P = ct.tf2io(ct.tf(1, [1, 0]), inputs='u', outputs='y') C = ct.tf2io(ct.tf(10, [1, 1]), inputs='e', outputs='u') sumblk = ct.summing_junction(inputs=['r', '-y'], output='e') T = ct.interconnect([C, P, sumblk], inplist='r', outlist='y') T_ss = ct.feedback(P * C, 1) np.testing.assert_almost_equal(T.A, T_ss.A) np.testing.assert_almost_equal(T.B, T_ss.B) np.testing.assert_almost_equal(T.C, T_ss.C) np.testing.assert_almost_equal(T.D, T_ss.D)
def test_interconnect_exceptions(): # First make sure the docstring example works P = ct.tf2io(ct.tf(1, [1, 0]), input='u', output='y') C = ct.tf2io(ct.tf(10, [1, 1]), input='e', output='u') sumblk = ct.summing_junction(inputs=['r', '-y'], output='e') T = ct.interconnect((P, C, sumblk), input='r', output='y') assert (T.ninputs, T.noutputs, T.nstates) == (1, 1, 2) # Unrecognized arguments # LinearIOSystem with pytest.raises(TypeError, match="unknown parameter"): P = ct.LinearIOSystem(ct.rss(2, 1, 1), output_name='y') # Interconnect with pytest.raises(TypeError, match="unknown parameter"): T = ct.interconnect((P, C, sumblk), input_name='r', output='y') # Interconnected system with pytest.raises(TypeError, match="unknown parameter"): T = ct.InterconnectedSystem((P, C, sumblk), input_name='r', output='y') # NonlinearIOSytem with pytest.raises(TypeError, match="unknown parameter"): nlios = ct.NonlinearIOSystem( None, lambda t, x, u, params: u*u, input_count=1, output_count=1) # Summing junction with pytest.raises(TypeError, match="input specification is required"): sumblk = ct.summing_junction() with pytest.raises(TypeError, match="unknown parameter"): sumblk = ct.summing_junction(input_count=2, output_count=2)
def test_signals_naming_convention(self): """Enforce generic names to be present when systems are created without explicit signal names: input: 'u[i]' state: 'x[i]' output: 'y[i]' """ ct.InputOutputSystem.idCounter = 0 sys = ct.LinearIOSystem(self.mimo_linsys1) for statename in ["x[0]", "x[1]"]: self.assertTrue(statename in sys.state_index) for inputname in ["u[0]", "u[1]"]: self.assertTrue(inputname in sys.input_index) for outputname in ["y[0]", "y[1]"]: self.assertTrue(outputname in sys.output_index) self.assertEqual(len(sys.state_index), sys.nstates) self.assertEqual(len(sys.input_index), sys.ninputs) self.assertEqual(len(sys.output_index), sys.noutputs) namedsys = ios.NonlinearIOSystem( updfcn = lambda t, x, u, params: x, outfcn = lambda t, x, u, params: u, inputs = ('u0'), outputs = ('y0'), states = ('x0'), name = 'namedsys') unnamedsys = ct.NonlinearIOSystem( lambda t,x,u,params: x, inputs=1, outputs=1, states=1 ) self.assertTrue('u0' in namedsys.input_index) self.assertTrue('y0' in namedsys.output_index) self.assertTrue('x0' in namedsys.state_index) # Unnamed/named connections un_series = unnamedsys * namedsys un_parallel = unnamedsys + namedsys un_feedback = unnamedsys.feedback(namedsys) un_dup = unnamedsys * namedsys.copy() un_hierarchical = un_series*unnamedsys u_neg = - unnamedsys self.assertTrue("sys[1].x[0]" in un_series.state_index) self.assertTrue("namedsys.x0" in un_series.state_index) self.assertTrue("sys[1].x[0]" in un_parallel.state_index) self.assertTrue("namedsys.x0" in un_series.state_index) self.assertTrue("sys[1].x[0]" in un_feedback.state_index) self.assertTrue("namedsys.x0" in un_feedback.state_index) self.assertTrue("sys[1].x[0]" in un_dup.state_index) self.assertTrue("copy of namedsys.x0" in un_dup.state_index) self.assertTrue("sys[1].x[0]" in un_hierarchical.state_index) self.assertTrue("sys[2].sys[1].x[0]" in un_hierarchical.state_index) self.assertTrue("sys[1].x[0]" in u_neg.state_index) # Same system conflict with warnings.catch_warnings(record=True) as warnval: same_name_series = unnamedsys * unnamedsys self.assertEquals(len(warnval), 1) self.assertTrue("sys[1].x[0]" in same_name_series.state_index) self.assertTrue("copy of sys[1].x[0]" in same_name_series.state_index)
def test_string_inputoutput(): # regression test for gh-692 P1 = ct.rss(2, 1, 1) P1_iosys = ct.LinearIOSystem(P1, inputs='u1', outputs='y1') P2 = ct.rss(2, 1, 1) P2_iosys = ct.LinearIOSystem(P2, inputs='y1', outputs='y2') P_s1 = ct.interconnect([P1_iosys, P2_iosys], inputs='u1', outputs=['y2']) assert P_s1.input_index == {'u1' : 0} P_s2 = ct.interconnect([P1_iosys, P2_iosys], input='u1', outputs=['y2']) assert P_s2.input_index == {'u1' : 0} P_s1 = ct.interconnect([P1_iosys, P2_iosys], inputs=['u1'], outputs='y2') assert P_s1.output_index == {'y2' : 0} P_s2 = ct.interconnect([P1_iosys, P2_iosys], inputs=['u1'], output='y2') assert P_s2.output_index == {'y2' : 0}
def test_sys_naming_convention(self): """Enforce generic system names 'sys[i]' to be present when systems are created without explicit names.""" ct.InputOutputSystem.idCounter = 0 sys = ct.LinearIOSystem(self.mimo_linsys1) self.assertEquals(sys.name, "sys[0]") self.assertEquals(sys.copy().name, "copy of sys[0]") namedsys = ios.NonlinearIOSystem( updfcn = lambda t, x, u, params: x, outfcn = lambda t, x, u, params: u, inputs = ('u[0]', 'u[1]'), outputs = ('y[0]', 'y[1]'), states = self.mimo_linsys1.states, name = 'namedsys') unnamedsys1 = ct.NonlinearIOSystem( lambda t,x,u,params: x, inputs=2, outputs=2, states=2 ) unnamedsys2 = ct.NonlinearIOSystem( None, lambda t,x,u,params: u, inputs=2, outputs=2 ) self.assertEquals(unnamedsys2.name, "sys[2]") # Unnamed/unnamed connections uu_series = unnamedsys1 * unnamedsys2 uu_parallel = unnamedsys1 + unnamedsys2 u_neg = - unnamedsys1 uu_feedback = unnamedsys2.feedback(unnamedsys1) uu_dup = unnamedsys1 * unnamedsys1.copy() uu_hierarchical = uu_series*unnamedsys1 self.assertEquals(uu_series.name, "sys[3]") self.assertEquals(uu_parallel.name, "sys[4]") self.assertEquals(u_neg.name, "sys[5]") self.assertEquals(uu_feedback.name, "sys[6]") self.assertEquals(uu_dup.name, "sys[7]") self.assertEquals(uu_hierarchical.name, "sys[8]") # Unnamed/named connections un_series = unnamedsys1 * namedsys un_parallel = unnamedsys1 + namedsys un_feedback = unnamedsys2.feedback(namedsys) un_dup = unnamedsys1 * namedsys.copy() un_hierarchical = uu_series*unnamedsys1 self.assertEquals(un_series.name, "sys[9]") self.assertEquals(un_parallel.name, "sys[10]") self.assertEquals(un_feedback.name, "sys[11]") self.assertEquals(un_dup.name, "sys[12]") self.assertEquals(un_hierarchical.name, "sys[13]") # Same system conflict with warnings.catch_warnings(record=True) as warnval: unnamedsys1 * unnamedsys1 self.assertEqual(len(warnval), 1)
def test_duplicates(self): nlios = ios.NonlinearIOSystem(lambda t,x,u,params: x, \ lambda t, x, u, params: u*u, \ inputs=1, outputs=1, states=1, name="sys") # Turn off deprecation warnings warnings.simplefilter("ignore", category=DeprecationWarning) warnings.simplefilter("ignore", category=PendingDeprecationWarning) # Duplicate objects with warnings.catch_warnings(record=True) as warnval: # Trigger a warning ios_series = nlios * nlios # Verify that we got a warning self.assertEqual(len(warnval), 1) self.assertTrue(issubclass(warnval[-1].category, UserWarning)) self.assertTrue("Duplicate object" in str(warnval[-1].message)) # Nonduplicate objects nlios1 = nlios.copy() nlios2 = nlios.copy() with warnings.catch_warnings(record=True) as warnval: ios_series = nlios1 * nlios2 self.assertEquals(len(warnval), 1) # when subsystems have the same name, duplicates are # renamed <subsysname_i> self.assertTrue("copy of sys_1.x[0]" in ios_series.state_index.keys()) self.assertTrue("copy of sys.x[0]" in ios_series.state_index.keys()) # Duplicate names iosys_siso = ct.LinearIOSystem(self.siso_linsys) nlios1 = ios.NonlinearIOSystem(None, \ lambda t, x, u, params: u*u, inputs=1, outputs=1, name="sys") nlios2 = ios.NonlinearIOSystem(None, \ lambda t, x, u, params: u*u, inputs=1, outputs=1, name="sys") with warnings.catch_warnings(record=True) as warnval: # Trigger a warning iosys = ct.InterconnectedSystem( (nlios1, iosys_siso, nlios2), inputs=0, outputs=0, states=0) # Verify that we got a warning self.assertEqual(len(warnval), 1) self.assertTrue(issubclass(warnval[-1].category, UserWarning)) self.assertTrue("Duplicate name" in str(warnval[-1].message)) # Same system, different names => everything should be OK nlios1 = ios.NonlinearIOSystem(None, \ lambda t, x, u, params: u*u, inputs=1, outputs=1, name="nlios1") nlios2 = ios.NonlinearIOSystem(None, \ lambda t, x, u, params: u*u, inputs=1, outputs=1, name="nlios2") with warnings.catch_warnings(record=True) as warnval: iosys = ct.InterconnectedSystem( (nlios1, iosys_siso, nlios2), inputs=0, outputs=0, states=0) self.assertEqual(len(warnval), 0)
def sys_dict(): sdict = {} sdict['ss'] = ct.ss([[-1]], [[1]], [[1]], [[0]]) sdict['tf'] = ct.tf([1], [0.5, 1]) sdict['tfx'] = ct.tf([1, 1], [1]) # non-proper transfer function sdict['frd'] = ct.frd([10 + 0j, 9 + 1j, 8 + 2j], [1, 2, 3]) sdict['lio'] = ct.LinearIOSystem(ct.ss([[-1]], [[5]], [[5]], [[0]])) sdict['ios'] = ct.NonlinearIOSystem(sdict['lio']._rhs, sdict['lio']._out, 1, 1, 1) sdict['arr'] = np.array([[2.0]]) sdict['flt'] = 3. return sdict
def test_lineariosys_statespace(self): """Make sure that a LinearIOSystem is also a StateSpace object""" iosys_siso = ct.LinearIOSystem(self.siso_linsys) self.assertTrue(isinstance(iosys_siso, ct.StateSpace)) # Make sure that state space functions work for LinearIOSystems np.testing.assert_array_equal( iosys_siso.pole(), self.siso_linsys.pole()) omega = np.logspace(.1, 10, 100) mag_io, phase_io, omega_io = iosys_siso.freqresp(omega) mag_ss, phase_ss, omega_ss = self.siso_linsys.freqresp(omega) np.testing.assert_array_equal(mag_io, mag_ss) np.testing.assert_array_equal(phase_io, phase_ss) np.testing.assert_array_equal(omega_io, omega_ss) # LinearIOSystem methods should override StateSpace methods io_mul = iosys_siso * iosys_siso self.assertTrue(isinstance(io_mul, ct.InputOutputSystem)) # But also retain linear structure self.assertTrue(isinstance(io_mul, ct.StateSpace)) # And make sure the systems match ss_series = self.siso_linsys * self.siso_linsys np.testing.assert_array_equal(io_mul.A, ss_series.A) np.testing.assert_array_equal(io_mul.B, ss_series.B) np.testing.assert_array_equal(io_mul.C, ss_series.C) np.testing.assert_array_equal(io_mul.D, ss_series.D) # Make sure that series does the same thing io_series = ct.series(iosys_siso, iosys_siso) self.assertTrue(isinstance(io_series, ct.InputOutputSystem)) self.assertTrue(isinstance(io_series, ct.StateSpace)) np.testing.assert_array_equal(io_series.A, ss_series.A) np.testing.assert_array_equal(io_series.B, ss_series.B) np.testing.assert_array_equal(io_series.C, ss_series.C) np.testing.assert_array_equal(io_series.D, ss_series.D) # Test out feedback as well io_feedback = ct.feedback(iosys_siso, iosys_siso) self.assertTrue(isinstance(io_series, ct.InputOutputSystem)) # But also retain linear structure self.assertTrue(isinstance(io_series, ct.StateSpace)) # And make sure the systems match ss_feedback = ct.feedback(self.siso_linsys, self.siso_linsys) np.testing.assert_array_equal(io_feedback.A, ss_feedback.A) np.testing.assert_array_equal(io_feedback.B, ss_feedback.B) np.testing.assert_array_equal(io_feedback.C, ss_feedback.C) np.testing.assert_array_equal(io_feedback.D, ss_feedback.D)
def sys_dict(): sdict = {} sdict['ss'] = ct.StateSpace([[-1]], [[1]], [[1]], [[0]]) sdict['tf'] = ct.TransferFunction([1], [0.5, 1]) sdict['tfx'] = ct.TransferFunction([1, 1], [1]) # non-proper TF sdict['frd'] = ct.frd([10 + 0j, 9 + 1j, 8 + 2j, 7 + 3j], [1, 2, 3, 4]) sdict['lio'] = ct.LinearIOSystem(ct.ss([[-1]], [[5]], [[5]], [[0]])) sdict['ios'] = ct.NonlinearIOSystem(sdict['lio']._rhs, sdict['lio']._out, inputs=1, outputs=1, states=1) sdict['arr'] = np.array([[2.0]]) sdict['flt'] = 3. return sdict
def test_trdata_labels(): # Create an I/O system with labels sys = ct.rss(4, 3, 2) iosys = ct.LinearIOSystem(sys) T = np.linspace(1, 10, 10) U = [np.sin(T), np.cos(T)] # Create a response response = ct.input_output_response(iosys, T, U) # Make sure the labels got created np.testing.assert_equal( response.output_labels, ["y[%d]" % i for i in range(sys.noutputs)]) np.testing.assert_equal( response.state_labels, ["x[%d]" % i for i in range(sys.nstates)]) np.testing.assert_equal( response.input_labels, ["u[%d]" % i for i in range(sys.ninputs)])
J_HAT_0 = 5 B_HAT_0 = 0.1 # Flag to disable adaptive control IS_ADAPTIVE = 1 # Plant # u_input: u # x_state: x # y_output: x SS_PLANT = control.StateSpace(-B/J, 1/J, 1, 0) IO_DC_MOTOR = control.LinearIOSystem( SS_PLANT, inputs=('u'), outputs=('x'), states=('x'), name='plant' ) def adaptive_pi_state(_t, x_state, u_input, _params): """Internal state of adpative PI controller""" # Controller inputs x_d = u_input[0] x_d_dot = u_input[1] x = u_input[2] # Controller state e_i = x_state[2]
K_0 = 1.1 BETA_DELTA_0 = 0 E_DELTA_0 = 0 # Actuator saturation limit U_MAX = 10 # Gains GAMMA_1 = 0.1 GAMMA_2 = 0.1 GAMMA_3 = 1 # Define plant IO_PLANT = control.LinearIOSystem(control.StateSpace(A_P, B_P, C_P, D_P), inputs=('u'), outputs=('x_p'), states=('x_p'), name='plant') # Define reference model IO_REF_MODEL = control.LinearIOSystem(control.StateSpace(A_M, B_M, C_M, D_M), inputs=('r'), outputs=('x_m'), states=('x_m'), name='ref_model') def adaptive_state(_t, x_state, u_input, _params): # pylint: disable=too-many-locals """Internal state of adpative controller""" # Controller inputs
if u > Pmax: #300Watt heating element u = Pmax dP = u - power return dP # https://python-control.readthedocs.io/en/0.8.3/iosys.html # Heater system heater_IO = ctl.NonlinearIOSystem(hupdate, None, inputs='p', outputs='ph', states='p') ctl_PID_IO = ctl.iosys.tf2io(conpid) # includes Kd plant_IO = ctl.LinearIOSystem(ss_mod) ufb_IO = ctl.iosys.tf2io(ufb) # unity feedback clsysSat = ctl.feedback( ctl.series(ctl_PID_IO, heater_IO, plant_IO), ufb_IO, sign=-1) # negative fb closed loop system w/ Saturation # Saturation test: #input = Texp # simple ramp #for i,iN in enumerate(input): #input[i] = 2*iN - 250 # expose the saturation # saturation only #t4,y4 = ctl.input_output_response(heater_IO, Texp, input) # closed loop non linear response (experimental) #t4,y4 = ctl.input_output_response(clsysSat, Texp, [ Rexp ])
import matplotlib.pyplot as plt # Plant parameters # input: u # state: x_p # output: x_p A_P = np.array([[-2, -1, 0, 0, 0], [1, 0, 0, 0, 0], [0, 0, -4, -2, 0], [0, 0, 2, 0, 0], [0, 0, 0, 0, 3]]) B_P = np.array([[1, 0], [0, 0], [0, 1], [0, 0], [0, 1]]) C_P = np.array([[1, 1, 0, 1, 0], [0, 1, 0, 0, 1]]) D_P = np.array([[0, 0], [0, 0]]) # Define plant IO_PLANT = control.LinearIOSystem(control.StateSpace(A_P, B_P, C_P, D_P), inputs=2, outputs=2, states=5, name='plant') # Stable Hermite form A = 1 # # Reference Model NUM_WM = [[np.array([0]) for i in range(2)] for j in range(2)] DEN_WM = [[np.array([1]) for i in range(2)] for j in range(2)] NUM_WM[0][0] = np.array([A]) DEN_WM[0][0] = np.array([1., A]) NUM_WM[1][1] = np.array([A]) DEN_WM[1][1] = np.array([1., A])
name='sysFullNonLin') sysLin = sysFullNonLin.linearize([0.0, 0.0, 0.0, 0.0], 0.0, params=sysParams) # linearize on up position A = sysLin.A B = sysLin.B BF = np.concatenate([B, Vd, 0.0 * B], axis=1) # augment inputs to include disturbance and noise C_full = sysLin.C C_part = np.array([1, 0, 0, 0], dtype=np.float) ssFull = control.StateSpace(A, BF, C_full, np.zeros( BF.shape)) # system with full state output, disturbance, no noise sysFull = control.LinearIOSystem(ssFull, inputs=('u', 'vd0', 'vd1', 'vd2', 'vd3', 'vn'), outputs=('x', 'xdot', 'theta', 'thetadot'), states=('x', 'xdot', 'theta', 'thetadot'), name='sysFull') ssPart = control.StateSpace(A, BF, C_part, np.array( [0, 0, 0, 0, 0, Vn])) # build big state space system... with single output # Kalman estimator # Kf, P, E = control.lqe(A, Vd, C, Vd, Vn) # design Kalman filter Kf, _, _ = control.lqr( np.transpose(A), np.expand_dims(C_part, axis=1), Vd, Vn) # alternatively, possible to design using "LQR" code Kf = np.transpose(Kf) ssKF = control.StateSpace( A - Kf * C_part, np.concatenate([B, Kf], axis=1), np.eye(4),