981 lines
42 KiB
Python
981 lines
42 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# iosys_test.py - test input/output system oeprations
|
|
# RMM, 17 Apr 2019
|
|
#
|
|
# This test suite checks to make sure that basic input/output class
|
|
# operations are working. It doesn't do exhaustive testing of
|
|
# operations on input/output systems. Separate unit tests should be
|
|
# created for that purpose.
|
|
|
|
from __future__ import print_function
|
|
import unittest
|
|
import warnings
|
|
import numpy as np
|
|
import scipy as sp
|
|
import control as ct
|
|
import control.iosys as ios
|
|
from distutils.version import StrictVersion
|
|
|
|
class TestIOSys(unittest.TestCase):
|
|
def setUp(self):
|
|
# Turn off numpy matrix warnings
|
|
import warnings
|
|
warnings.simplefilter('ignore', category=PendingDeprecationWarning)
|
|
|
|
# Create a single input/single output linear system
|
|
self.siso_linsys = ct.StateSpace(
|
|
[[-1, 1], [0, -2]], [[0], [1]], [[1, 0]], [[0]])
|
|
|
|
# Create a multi input/multi output linear system
|
|
self.mimo_linsys1 = ct.StateSpace(
|
|
[[-1, 1], [0, -2]], [[1, 0], [0, 1]],
|
|
[[1, 0], [0, 1]], np.zeros((2,2)))
|
|
|
|
# Create a multi input/multi output linear system
|
|
self.mimo_linsys2 = ct.StateSpace(
|
|
[[-1, 1], [0, -2]], [[0, 1], [1, 0]],
|
|
[[1, 0], [0, 1]], np.zeros((2,2)))
|
|
|
|
# Create simulation parameters
|
|
self.T = np.linspace(0, 10, 100)
|
|
self.U = np.sin(self.T)
|
|
self.X0 = [0, 0]
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_linear_iosys(self):
|
|
# Create an input/output system from the linear system
|
|
linsys = self.siso_linsys
|
|
iosys = ios.LinearIOSystem(linsys)
|
|
|
|
# Make sure that the right hand side matches linear system
|
|
for x, u in (([0, 0], 0), ([1, 0], 0), ([0, 1], 0), ([0, 0], 1)):
|
|
np.testing.assert_array_almost_equal(
|
|
np.reshape(iosys._rhs(0, x, u), (-1,1)),
|
|
linsys.A * np.reshape(x, (-1, 1)) + linsys.B * u)
|
|
|
|
# Make sure that simulations also line up
|
|
T, U, X0 = self.T, self.U, self.X0
|
|
lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U, X0)
|
|
ios_t, ios_y = ios.input_output_response(iosys, T, U, X0)
|
|
np.testing.assert_array_almost_equal(lti_t, ios_t)
|
|
np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3)
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_tf2io(self):
|
|
# Create a transfer function from the state space system
|
|
linsys = self.siso_linsys
|
|
tfsys = ct.ss2tf(linsys)
|
|
iosys = ct.tf2io(tfsys)
|
|
|
|
# Verify correctness via simulation
|
|
T, U, X0 = self.T, self.U, self.X0
|
|
lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U, X0)
|
|
ios_t, ios_y = ios.input_output_response(iosys, T, U, X0)
|
|
np.testing.assert_array_almost_equal(lti_t, ios_t)
|
|
np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3)
|
|
|
|
def test_ss2io(self):
|
|
# Create an input/output system from the linear system
|
|
linsys = self.siso_linsys
|
|
iosys = ct.ss2io(linsys)
|
|
np.testing.assert_array_equal(linsys.A, iosys.A)
|
|
np.testing.assert_array_equal(linsys.B, iosys.B)
|
|
np.testing.assert_array_equal(linsys.C, iosys.C)
|
|
np.testing.assert_array_equal(linsys.D, iosys.D)
|
|
|
|
# Try adding names to things
|
|
iosys_named = ct.ss2io(linsys, inputs='u', outputs='y',
|
|
states=['x1', 'x2'], name='iosys_named')
|
|
self.assertEqual(iosys_named.find_input('u'), 0)
|
|
self.assertEqual(iosys_named.find_input('x'), None)
|
|
self.assertEqual(iosys_named.find_output('y'), 0)
|
|
self.assertEqual(iosys_named.find_output('u'), None)
|
|
self.assertEqual(iosys_named.find_state('x0'), None)
|
|
self.assertEqual(iosys_named.find_state('x1'), 0)
|
|
self.assertEqual(iosys_named.find_state('x2'), 1)
|
|
np.testing.assert_array_equal(linsys.A, iosys_named.A)
|
|
np.testing.assert_array_equal(linsys.B, iosys_named.B)
|
|
np.testing.assert_array_equal(linsys.C, iosys_named.C)
|
|
np.testing.assert_array_equal(linsys.D, iosys_named.D)
|
|
|
|
# Make sure unspecified inputs/outputs/states are handled properly
|
|
def test_iosys_unspecified(self):
|
|
# System with unspecified inputs and outputs
|
|
sys = ios.NonlinearIOSystem(secord_update, secord_output)
|
|
np.testing.assert_raises(TypeError, sys.__mul__, sys)
|
|
|
|
# Make sure we can print various types of I/O systems
|
|
def test_iosys_print(self):
|
|
# Send the output to /dev/null
|
|
import os
|
|
f = open(os.devnull,"w")
|
|
|
|
# Simple I/O system
|
|
iosys = ct.ss2io(self.siso_linsys)
|
|
print(iosys, file=f)
|
|
|
|
# I/O system without ninputs, noutputs
|
|
ios_unspecified = ios.NonlinearIOSystem(secord_update, secord_output)
|
|
print(ios_unspecified, file=f)
|
|
|
|
# I/O system with derived inputs and outputs
|
|
ios_linearized = ios.linearize(ios_unspecified, [0, 0], [0])
|
|
print(ios_linearized, file=f)
|
|
|
|
f.close()
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_nonlinear_iosys(self):
|
|
# Create a simple nonlinear I/O system
|
|
nlsys = ios.NonlinearIOSystem(predprey)
|
|
T = self.T
|
|
|
|
# Start by simulating from an equilibrium point
|
|
X0 = [0, 0]
|
|
ios_t, ios_y = ios.input_output_response(nlsys, T, 0, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, np.zeros(np.shape(ios_y)))
|
|
|
|
# Now simulate from a nonzero point
|
|
X0 = [0.5, 0.5]
|
|
ios_t, ios_y = ios.input_output_response(nlsys, T, 0, X0)
|
|
|
|
#
|
|
# Simulate a linear function as a nonlinear function and compare
|
|
#
|
|
# Create a single input/single output linear system
|
|
linsys = self.siso_linsys
|
|
|
|
# Create a nonlinear system with the same dynamics
|
|
nlupd = lambda t, x, u, params: \
|
|
np.reshape(linsys.A * np.reshape(x, (-1, 1)) + linsys.B * u, (-1,))
|
|
nlout = lambda t, x, u, params: \
|
|
np.reshape(linsys.C * np.reshape(x, (-1, 1)) + linsys.D * u, (-1,))
|
|
nlsys = ios.NonlinearIOSystem(nlupd, nlout)
|
|
|
|
# Make sure that simulations also line up
|
|
T, U, X0 = self.T, self.U, self.X0
|
|
lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U, X0)
|
|
ios_t, ios_y = ios.input_output_response(nlsys, T, U, X0)
|
|
np.testing.assert_array_almost_equal(lti_t, ios_t)
|
|
np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3)
|
|
|
|
def test_linearize(self):
|
|
# Create a single input/single output linear system
|
|
linsys = self.siso_linsys
|
|
iosys = ios.LinearIOSystem(linsys)
|
|
|
|
# Linearize it and make sure we get back what we started with
|
|
linearized = iosys.linearize([0, 0], 0)
|
|
np.testing.assert_array_almost_equal(linsys.A, linearized.A)
|
|
np.testing.assert_array_almost_equal(linsys.B, linearized.B)
|
|
np.testing.assert_array_almost_equal(linsys.C, linearized.C)
|
|
np.testing.assert_array_almost_equal(linsys.D, linearized.D)
|
|
|
|
# Create a simple nonlinear system to check (kinematic car)
|
|
def kincar_update(t, x, u, params):
|
|
return np.array([np.cos(x[2]) * u[0], np.sin(x[2]) * u[0], u[1]])
|
|
def kincar_output(t, x, u, params):
|
|
return np.array([x[0], x[1]])
|
|
iosys = ios.NonlinearIOSystem(kincar_update, kincar_output)
|
|
linearized = iosys.linearize([0, 0, 0], [0, 0])
|
|
np.testing.assert_array_almost_equal(linearized.A, np.zeros((3,3)))
|
|
np.testing.assert_array_almost_equal(
|
|
linearized.B, [[1, 0], [0, 0], [0, 1]])
|
|
np.testing.assert_array_almost_equal(
|
|
linearized.C, [[1, 0, 0], [0, 1, 0]])
|
|
np.testing.assert_array_almost_equal(linearized.D, np.zeros((2,2)))
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_connect(self):
|
|
# Define a couple of (linear) systems to interconnection
|
|
linsys1 = self.siso_linsys
|
|
iosys1 = ios.LinearIOSystem(linsys1)
|
|
linsys2 = self.siso_linsys
|
|
iosys2 = ios.LinearIOSystem(linsys2)
|
|
|
|
# Connect systems in different ways and compare to StateSpace
|
|
linsys_series = linsys2 * linsys1
|
|
iosys_series = ios.InterconnectedSystem(
|
|
(iosys1, iosys2), # systems
|
|
((1, 0),), # interconnection (series)
|
|
0, # input = first system
|
|
1 # output = second system
|
|
)
|
|
|
|
# Run a simulation and compare to linear response
|
|
T, U = self.T, self.U
|
|
X0 = np.concatenate((self.X0, self.X0))
|
|
ios_t, ios_y, ios_x = ios.input_output_response(
|
|
iosys_series, T, U, X0, return_x=True)
|
|
lti_t, lti_y, lti_x = ct.forced_response(linsys_series, T, U, X0)
|
|
np.testing.assert_array_almost_equal(lti_t, ios_t)
|
|
np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3)
|
|
|
|
# Connect systems with different timebases
|
|
linsys2c = self.siso_linsys
|
|
linsys2c.dt = 0 # Reset the timebase
|
|
iosys2c = ios.LinearIOSystem(linsys2c)
|
|
iosys_series = ios.InterconnectedSystem(
|
|
(iosys1, iosys2c), # systems
|
|
((1, 0),), # interconnection (series)
|
|
0, # input = first system
|
|
1 # output = second system
|
|
)
|
|
self.assertTrue(ct.isctime(iosys_series, strict=True))
|
|
ios_t, ios_y, ios_x = ios.input_output_response(
|
|
iosys_series, T, U, X0, return_x=True)
|
|
lti_t, lti_y, lti_x = ct.forced_response(linsys_series, T, U, X0)
|
|
np.testing.assert_array_almost_equal(lti_t, ios_t)
|
|
np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3)
|
|
|
|
# Feedback interconnection
|
|
linsys_feedback = ct.feedback(linsys1, linsys2)
|
|
iosys_feedback = ios.InterconnectedSystem(
|
|
(iosys1, iosys2), # systems
|
|
((1, 0), # input of sys2 = output of sys1
|
|
(0, (1, 0, -1))), # input of sys1 = -output of sys2
|
|
0, # input = first system
|
|
0 # output = first system
|
|
)
|
|
ios_t, ios_y, ios_x = ios.input_output_response(
|
|
iosys_feedback, T, U, X0, return_x=True)
|
|
lti_t, lti_y, lti_x = ct.forced_response(linsys_feedback, T, U, X0)
|
|
np.testing.assert_array_almost_equal(lti_t, ios_t)
|
|
np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3)
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_static_nonlinearity(self):
|
|
# Linear dynamical system
|
|
linsys = self.siso_linsys
|
|
ioslin = ios.LinearIOSystem(linsys)
|
|
|
|
# Nonlinear saturation
|
|
sat = lambda u: u if abs(u) < 1 else np.sign(u)
|
|
sat_output = lambda t, x, u, params: sat(u)
|
|
nlsat = ios.NonlinearIOSystem(None, sat_output, inputs=1, outputs=1)
|
|
|
|
# Set up parameters for simulation
|
|
T, U, X0 = self.T, 2 * self.U, self.X0
|
|
Usat = np.vectorize(sat)(U)
|
|
|
|
# Make sure saturation works properly by comparing linear system with
|
|
# saturated input to nonlinear system with saturation composition
|
|
lti_t, lti_y, lti_x = ct.forced_response(linsys, T, Usat, X0)
|
|
ios_t, ios_y, ios_x = ios.input_output_response(
|
|
ioslin * nlsat, T, U, X0, return_x=True)
|
|
np.testing.assert_array_almost_equal(lti_t, ios_t)
|
|
np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=2)
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_algebraic_loop(self):
|
|
# Create some linear and nonlinear systems to play with
|
|
linsys = self.siso_linsys
|
|
lnios = ios.LinearIOSystem(linsys)
|
|
nlios = ios.NonlinearIOSystem(None, \
|
|
lambda t, x, u, params: u*u, inputs=1, outputs=1)
|
|
nlios1 = nlios.copy()
|
|
nlios2 = nlios.copy()
|
|
|
|
# Set up parameters for simulation
|
|
T, U, X0 = self.T, self.U, self.X0
|
|
|
|
# Single nonlinear system - no states
|
|
ios_t, ios_y = ios.input_output_response(nlios, T, U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, U*U, decimal=3)
|
|
|
|
# Composed nonlinear system (series)
|
|
ios_t, ios_y = ios.input_output_response(nlios1 * nlios2, T, U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, U**4, decimal=3)
|
|
|
|
# Composed nonlinear system (parallel)
|
|
ios_t, ios_y = ios.input_output_response(nlios1 + nlios2, T, U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, 2*U**2, decimal=3)
|
|
|
|
# Nonlinear system composed with LTI system (series)
|
|
ios_t, ios_y = ios.input_output_response(
|
|
nlios * lnios * nlios, T, U, X0)
|
|
lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U*U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, lti_y*lti_y, decimal=3)
|
|
|
|
# Nonlinear system in feeback loop with LTI system
|
|
iosys = ios.InterconnectedSystem(
|
|
(lnios, nlios), # linear system w/ nonlinear feedback
|
|
((1,), # feedback interconnection (sig to 0)
|
|
(0, (1, 0, -1))),
|
|
0, # input to linear system
|
|
0 # output from linear system
|
|
)
|
|
ios_t, ios_y = ios.input_output_response(iosys, T, U, X0)
|
|
# No easy way to test the result
|
|
|
|
# Algebraic loop from static nonlinear system in feedback
|
|
# (error will be due to no states)
|
|
iosys = ios.InterconnectedSystem(
|
|
(nlios1, nlios2), # two copies of a static nonlinear system
|
|
((0, 1), # feedback interconnection
|
|
(1, (0, 0, -1))),
|
|
0, 0
|
|
)
|
|
args = (iosys, T, U, X0)
|
|
self.assertRaises(RuntimeError, ios.input_output_response, *args)
|
|
|
|
# Algebraic loop due to feedthrough term
|
|
linsys = ct.StateSpace(
|
|
[[-1, 1], [0, -2]], [[0], [1]], [[1, 0]], [[1]])
|
|
lnios = ios.LinearIOSystem(linsys)
|
|
iosys = ios.InterconnectedSystem(
|
|
(nlios, lnios), # linear system w/ nonlinear feedback
|
|
((0, 1), # feedback interconnection
|
|
(1, (0, 0, -1))),
|
|
0, 0
|
|
)
|
|
args = (iosys, T, U, X0)
|
|
# ios_t, ios_y = ios.input_output_response(iosys, T, U, X0)
|
|
self.assertRaises(RuntimeError, ios.input_output_response, *args)
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_summer(self):
|
|
# Construct a MIMO system for testing
|
|
linsys = self.mimo_linsys1
|
|
linio = ios.LinearIOSystem(linsys)
|
|
|
|
linsys_parallel = linsys + linsys
|
|
iosys_parallel = linio + linio
|
|
|
|
# Set up parameters for simulation
|
|
T = self.T
|
|
U = [np.sin(T), np.cos(T)]
|
|
X0 = 0
|
|
|
|
lin_t, lin_y, lin_x = ct.forced_response(linsys_parallel, T, U, X0)
|
|
ios_t, ios_y = ios.input_output_response(iosys_parallel, T, U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_rmul(self):
|
|
# Test right multiplication
|
|
# TODO: replace with better tests when conversions are implemented
|
|
|
|
# Set up parameters for simulation
|
|
T, U, X0 = self.T, self.U, self.X0
|
|
|
|
# Linear system with input and output nonlinearities
|
|
# Also creates a nested interconnected system
|
|
ioslin = ios.LinearIOSystem(self.siso_linsys)
|
|
nlios = ios.NonlinearIOSystem(None, \
|
|
lambda t, x, u, params: u*u, inputs=1, outputs=1)
|
|
sys1 = nlios * ioslin
|
|
sys2 = ios.InputOutputSystem.__rmul__(nlios, sys1)
|
|
|
|
# Make sure we got the right thing (via simulation comparison)
|
|
ios_t, ios_y = ios.input_output_response(sys2, T, U, X0)
|
|
lti_t, lti_y, lti_x = ct.forced_response(ioslin, T, U*U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, lti_y*lti_y, decimal=3)
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_neg(self):
|
|
"""Test negation of a system"""
|
|
|
|
# Set up parameters for simulation
|
|
T, U, X0 = self.T, self.U, self.X0
|
|
|
|
# Static nonlinear system
|
|
nlios = ios.NonlinearIOSystem(None, \
|
|
lambda t, x, u, params: u*u, inputs=1, outputs=1)
|
|
ios_t, ios_y = ios.input_output_response(-nlios, T, U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, -U*U, decimal=3)
|
|
|
|
# Linear system with input nonlinearity
|
|
# Also creates a nested interconnected system
|
|
ioslin = ios.LinearIOSystem(self.siso_linsys)
|
|
sys = (ioslin) * (-nlios)
|
|
|
|
# Make sure we got the right thing (via simulation comparison)
|
|
ios_t, ios_y = ios.input_output_response(sys, T, U, X0)
|
|
lti_t, lti_y, lti_x = ct.forced_response(ioslin, T, U*U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, -lti_y, decimal=3)
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_feedback(self):
|
|
# Set up parameters for simulation
|
|
T, U, X0 = self.T, self.U, self.X0
|
|
|
|
# Linear system with constant feedback (via "nonlinear" mapping)
|
|
ioslin = ios.LinearIOSystem(self.siso_linsys)
|
|
nlios = ios.NonlinearIOSystem(None, \
|
|
lambda t, x, u, params: u, inputs=1, outputs=1)
|
|
iosys = ct.feedback(ioslin, nlios)
|
|
linsys = ct.feedback(self.siso_linsys, 1)
|
|
|
|
ios_t, ios_y = ios.input_output_response(iosys, T, U, X0)
|
|
lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, lti_y, decimal=3)
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_bdalg_functions(self):
|
|
"""Test block diagram functions algebra on I/O systems"""
|
|
# Set up parameters for simulation
|
|
T = self.T
|
|
U = [np.sin(T), np.cos(T)]
|
|
X0 = 0
|
|
|
|
# Set up systems to be composed
|
|
linsys1 = self.mimo_linsys1
|
|
linio1 = ios.LinearIOSystem(linsys1)
|
|
linsys2 = self.mimo_linsys2
|
|
linio2 = ios.LinearIOSystem(linsys2)
|
|
|
|
# Series interconnection
|
|
linsys_series = ct.series(linsys1, linsys2)
|
|
iosys_series = ct.series(linio1, linio2)
|
|
lin_t, lin_y, lin_x = ct.forced_response(linsys_series, T, U, X0)
|
|
ios_t, ios_y = ios.input_output_response(iosys_series, T, U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
|
|
|
|
# Make sure that systems don't commute
|
|
linsys_series = ct.series(linsys2, linsys1)
|
|
lin_t, lin_y, lin_x = ct.forced_response(linsys_series, T, U, X0)
|
|
self.assertFalse((np.abs(lin_y - ios_y) < 1e-3).all())
|
|
|
|
# Parallel interconnection
|
|
linsys_parallel = ct.parallel(linsys1, linsys2)
|
|
iosys_parallel = ct.parallel(linio1, linio2)
|
|
lin_t, lin_y, lin_x = ct.forced_response(linsys_parallel, T, U, X0)
|
|
ios_t, ios_y = ios.input_output_response(iosys_parallel, T, U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
|
|
|
|
# Negation
|
|
linsys_negate = ct.negate(linsys1)
|
|
iosys_negate = ct.negate(linio1)
|
|
lin_t, lin_y, lin_x = ct.forced_response(linsys_negate, T, U, X0)
|
|
ios_t, ios_y = ios.input_output_response(iosys_negate, T, U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
|
|
|
|
# Feedback interconnection
|
|
linsys_feedback = ct.feedback(linsys1, linsys2)
|
|
iosys_feedback = ct.feedback(linio1, linio2)
|
|
lin_t, lin_y, lin_x = ct.forced_response(linsys_feedback, T, U, X0)
|
|
ios_t, ios_y = ios.input_output_response(iosys_feedback, T, U, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_nonsquare_bdalg(self):
|
|
# Set up parameters for simulation
|
|
T = self.T
|
|
U2 = [np.sin(T), np.cos(T)]
|
|
U3 = [np.sin(T), np.cos(T), T]
|
|
X0 = 0
|
|
|
|
# Set up systems to be composed
|
|
linsys_2i3o = ct.StateSpace(
|
|
[[-1, 1, 0], [0, -2, 0], [0, 0, -3]], [[1, 0], [0, 1], [1, 1]],
|
|
[[1, 0, 0], [0, 1, 0], [0, 0, 1]], np.zeros((3, 2)))
|
|
iosys_2i3o = ios.LinearIOSystem(linsys_2i3o)
|
|
|
|
linsys_3i2o = ct.StateSpace(
|
|
[[-1, 1, 0], [0, -2, 0], [0, 0, -3]],
|
|
[[1, 0, 0], [0, 1, 0], [0, 0, 1]],
|
|
[[1, 0, 1], [0, 1, -1]], np.zeros((2, 3)))
|
|
iosys_3i2o = ios.LinearIOSystem(linsys_3i2o)
|
|
|
|
# Multiplication
|
|
linsys_multiply = linsys_3i2o * linsys_2i3o
|
|
iosys_multiply = iosys_3i2o * iosys_2i3o
|
|
lin_t, lin_y, lin_x = ct.forced_response(linsys_multiply, T, U2, X0)
|
|
ios_t, ios_y = ios.input_output_response(iosys_multiply, T, U2, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
|
|
|
|
linsys_multiply = linsys_2i3o * linsys_3i2o
|
|
iosys_multiply = iosys_2i3o * iosys_3i2o
|
|
lin_t, lin_y, lin_x = ct.forced_response(linsys_multiply, T, U3, X0)
|
|
ios_t, ios_y = ios.input_output_response(iosys_multiply, T, U3, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
|
|
|
|
# Right multiplication
|
|
# TODO: add real tests once conversion from other types is supported
|
|
iosys_multiply = ios.InputOutputSystem.__rmul__(iosys_3i2o, iosys_2i3o)
|
|
ios_t, ios_y = ios.input_output_response(iosys_multiply, T, U3, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
|
|
|
|
# Feedback
|
|
linsys_multiply = ct.feedback(linsys_3i2o, linsys_2i3o)
|
|
iosys_multiply = iosys_3i2o.feedback(iosys_2i3o)
|
|
lin_t, lin_y, lin_x = ct.forced_response(linsys_multiply, T, U3, X0)
|
|
ios_t, ios_y = ios.input_output_response(iosys_multiply, T, U3, X0)
|
|
np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
|
|
|
|
# Mismatch should generate exception
|
|
args = (iosys_3i2o, iosys_3i2o)
|
|
self.assertRaises(ValueError, ct.series, *args)
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_discrete(self):
|
|
"""Test discrete time functionality"""
|
|
# Create some linear and nonlinear systems to play with
|
|
linsys = ct.StateSpace(
|
|
[[-1, 1], [0, -2]], [[0], [1]], [[1, 0]], [[0]], True)
|
|
lnios = ios.LinearIOSystem(linsys)
|
|
|
|
# Set up parameters for simulation
|
|
T, U, X0 = self.T, self.U, self.X0
|
|
|
|
# Simulate and compare to LTI output
|
|
ios_t, ios_y = ios.input_output_response(lnios, T, U, X0)
|
|
lin_t, lin_y, lin_x = ct.forced_response(linsys, T, U, X0)
|
|
np.testing.assert_array_almost_equal(ios_t, lin_t, decimal=3)
|
|
np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
|
|
|
|
# Test MIMO system, converted to discrete time
|
|
linsys = ct.StateSpace(self.mimo_linsys1)
|
|
linsys.dt = self.T[1] - self.T[0]
|
|
lnios = ios.LinearIOSystem(linsys)
|
|
|
|
# Set up parameters for simulation
|
|
T = self.T
|
|
U = [np.sin(T), np.cos(T)]
|
|
X0 = 0
|
|
|
|
# Simulate and compare to LTI output
|
|
ios_t, ios_y = ios.input_output_response(lnios, T, U, X0)
|
|
lin_t, lin_y, lin_x = ct.forced_response(linsys, T, U, X0)
|
|
np.testing.assert_array_almost_equal(ios_t, lin_t, decimal=3)
|
|
np.testing.assert_array_almost_equal(ios_y, lin_y, decimal=3)
|
|
|
|
def test_find_eqpts(self):
|
|
"""Test find_eqpt function"""
|
|
# Simple equilibrium point with no inputs
|
|
nlsys = ios.NonlinearIOSystem(predprey)
|
|
xeq, ueq, result = ios.find_eqpt(
|
|
nlsys, [1.6, 1.2], None, return_result=True)
|
|
self.assertTrue(result.success)
|
|
np.testing.assert_array_almost_equal(xeq, [1.64705879, 1.17923874])
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys._rhs(0, xeq, ueq), np.zeros((2,)))
|
|
|
|
# Ducted fan dynamics with output = velocity
|
|
nlsys = ios.NonlinearIOSystem(pvtol, lambda t, x, u, params: x[0:2])
|
|
|
|
# Make sure the origin is a fixed point
|
|
xeq, ueq, result = ios.find_eqpt(
|
|
nlsys, [0, 0, 0, 0], [0, 4*9.8], return_result=True)
|
|
self.assertTrue(result.success)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys._rhs(0, xeq, ueq), np.zeros((4,)))
|
|
np.testing.assert_array_almost_equal(xeq, [0, 0, 0, 0])
|
|
|
|
# Use a small lateral force to cause motion
|
|
xeq, ueq, result = ios.find_eqpt(
|
|
nlsys, [0, 0, 0, 0], [0.01, 4*9.8], return_result=True)
|
|
self.assertTrue(result.success)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys._rhs(0, xeq, ueq), np.zeros((4,)), decimal=5)
|
|
|
|
# Equilibrium point with fixed output
|
|
xeq, ueq, result = ios.find_eqpt(
|
|
nlsys, [0, 0, 0, 0], [0.01, 4*9.8],
|
|
y0=[0.1, 0.1], return_result=True)
|
|
self.assertTrue(result.success)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys._out(0, xeq, ueq), [0.1, 0.1], decimal=5)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys._rhs(0, xeq, ueq), np.zeros((4,)), decimal=5)
|
|
|
|
# Specify outputs to constrain (replicate previous)
|
|
xeq, ueq, result = ios.find_eqpt(
|
|
nlsys, [0, 0, 0, 0], [0.01, 4*9.8], y0=[0.1, 0.1],
|
|
iy = [0, 1], return_result=True)
|
|
self.assertTrue(result.success)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys._out(0, xeq, ueq), [0.1, 0.1], decimal=5)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys._rhs(0, xeq, ueq), np.zeros((4,)), decimal=5)
|
|
|
|
# Specify inputs to constrain (replicate previous), w/ no result
|
|
xeq, ueq = ios.find_eqpt(
|
|
nlsys, [0, 0, 0, 0], [0.01, 4*9.8], y0=[0.1, 0.1], iu = [])
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys._out(0, xeq, ueq), [0.1, 0.1], decimal=5)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys._rhs(0, xeq, ueq), np.zeros((4,)), decimal=5)
|
|
|
|
# Now solve the problem with the original PVTOL variables
|
|
# Constrain the output angle and x velocity
|
|
nlsys_full = ios.NonlinearIOSystem(pvtol_full, None)
|
|
xeq, ueq, result = ios.find_eqpt(
|
|
nlsys_full, [0, 0, 0, 0, 0, 0], [0.01, 4*9.8],
|
|
y0=[0, 0, 0.1, 0.1, 0, 0], iy = [2, 3],
|
|
idx=[2, 3, 4, 5], ix=[0, 1], return_result=True)
|
|
self.assertTrue(result.success)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys_full._out(0, xeq, ueq)[[2, 3]], [0.1, 0.1], decimal=5)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys_full._rhs(0, xeq, ueq)[-4:], np.zeros((4,)), decimal=5)
|
|
|
|
# Fix one input and vary the other
|
|
nlsys_full = ios.NonlinearIOSystem(pvtol_full, None)
|
|
xeq, ueq, result = ios.find_eqpt(
|
|
nlsys_full, [0, 0, 0, 0, 0, 0], [0.01, 4*9.8],
|
|
y0=[0, 0, 0.1, 0.1, 0, 0], iy=[3], iu=[1],
|
|
idx=[2, 3, 4, 5], ix=[0, 1], return_result=True)
|
|
self.assertTrue(result.success)
|
|
np.testing.assert_almost_equal(ueq[1], 4*9.8, decimal=5)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys_full._out(0, xeq, ueq)[[3]], [0.1], decimal=5)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys_full._rhs(0, xeq, ueq)[-4:], np.zeros((4,)), decimal=5)
|
|
|
|
# PVTOL with output = y velocity
|
|
xeq, ueq, result = ios.find_eqpt(
|
|
nlsys_full, [0, 0, 0, 0.1, 0, 0], [0.01, 4*9.8],
|
|
y0=[0, 0, 0, 0.1, 0, 0], iy=[3],
|
|
dx0=[0.1, 0, 0, 0, 0, 0], idx=[1, 2, 3, 4, 5],
|
|
ix=[0, 1], return_result=True)
|
|
self.assertTrue(result.success)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys_full._out(0, xeq, ueq)[-3:], [0.1, 0, 0], decimal=5)
|
|
np.testing.assert_array_almost_equal(
|
|
nlsys_full._rhs(0, xeq, ueq)[-5:], np.zeros((5,)), decimal=5)
|
|
|
|
# Unobservable system
|
|
linsys = ct.StateSpace(
|
|
[[-1, 1], [0, -2]], [[0], [1]], [[0, 0]], [[0]])
|
|
lnios = ios.LinearIOSystem(linsys)
|
|
|
|
# If result is returned, user has to check
|
|
xeq, ueq, result = ios.find_eqpt(
|
|
lnios, [0, 0], [0], y0=[1], return_result=True)
|
|
self.assertFalse(result.success)
|
|
|
|
# If result is not returned, find_eqpt should return None
|
|
xeq, ueq = ios.find_eqpt(lnios, [0, 0], [0], y0=[1])
|
|
self.assertEqual(xeq, None)
|
|
self.assertEqual(ueq, None)
|
|
|
|
@unittest.skipIf(StrictVersion(sp.__version__) < "1.0",
|
|
"requires SciPy 1.0 or greater")
|
|
def test_params(self):
|
|
# Start with the default set of parameters
|
|
ios_secord_default = ios.NonlinearIOSystem(
|
|
secord_update, secord_output, inputs=1, outputs=1, states=2)
|
|
lin_secord_default = ios.linearize(ios_secord_default, [0, 0], [0])
|
|
w_default, v_default = np.linalg.eig(lin_secord_default.A)
|
|
|
|
# New copy, with modified parameters
|
|
ios_secord_update = ios.NonlinearIOSystem(
|
|
secord_update, secord_output, inputs=1, outputs=1, states=2,
|
|
params={'omega0':2, 'zeta':0})
|
|
|
|
# Make sure the default parameters haven't changed
|
|
lin_secord_check = ios.linearize(ios_secord_default, [0, 0], [0])
|
|
w, v = np.linalg.eig(lin_secord_check.A)
|
|
np.testing.assert_array_almost_equal(np.sort(w), np.sort(w_default))
|
|
|
|
# Make sure updated system parameters got set correctly
|
|
lin_secord_update = ios.linearize(ios_secord_update, [0, 0], [0])
|
|
w, v = np.linalg.eig(lin_secord_update.A)
|
|
np.testing.assert_array_almost_equal(np.sort(w), np.sort([2j, -2j]))
|
|
|
|
# Change the parameters of the default sys just for the linearization
|
|
lin_secord_local = ios.linearize(ios_secord_default, [0, 0], [0],
|
|
params={'zeta':0})
|
|
w, v = np.linalg.eig(lin_secord_local.A)
|
|
np.testing.assert_array_almost_equal(np.sort(w), np.sort([1j, -1j]))
|
|
|
|
# Change the parameters of the updated sys just for the linearization
|
|
lin_secord_local = ios.linearize(ios_secord_update, [0, 0], [0],
|
|
params={'zeta':0, 'omega0':3})
|
|
w, v = np.linalg.eig(lin_secord_local.A)
|
|
np.testing.assert_array_almost_equal(np.sort(w), np.sort([3j, -3j]))
|
|
|
|
# Make sure that changes propagate through interconnections
|
|
ios_series_default_local = ios_secord_default * ios_secord_update
|
|
lin_series_default_local = ios.linearize(
|
|
ios_series_default_local, [0, 0, 0, 0], [0])
|
|
w, v = np.linalg.eig(lin_series_default_local.A)
|
|
np.testing.assert_array_almost_equal(
|
|
np.sort(w), np.sort(np.concatenate((w_default, [2j, -2j]))))
|
|
|
|
# Show that we can change the parameters at linearization
|
|
lin_series_override = ios.linearize(
|
|
ios_series_default_local, [0, 0, 0, 0], [0],
|
|
params={'zeta':0, 'omega0':4})
|
|
w, v = np.linalg.eig(lin_series_override.A)
|
|
np.testing.assert_array_almost_equal(w, [4j, -4j, 4j, -4j])
|
|
|
|
# Check for warning if we try to set params for LinearIOSystem
|
|
linsys = self.siso_linsys
|
|
iosys = ios.LinearIOSystem(linsys)
|
|
T, U, X0 = self.T, self.U, self.X0
|
|
lti_t, lti_y, lti_x = ct.forced_response(linsys, T, U, X0)
|
|
with warnings.catch_warnings(record=True) as warnval:
|
|
# Turn off deprecation warnings
|
|
warnings.simplefilter("ignore", category=DeprecationWarning)
|
|
warnings.simplefilter("ignore", category=PendingDeprecationWarning)
|
|
|
|
# Trigger a warning
|
|
ios_t, ios_y = ios.input_output_response(
|
|
iosys, T, U, X0, params={'something':0})
|
|
|
|
# Verify that we got a warning
|
|
self.assertEqual(len(warnval), 1)
|
|
self.assertTrue(issubclass(warnval[-1].category, UserWarning))
|
|
self.assertTrue("LinearIOSystem" in str(warnval[-1].message))
|
|
self.assertTrue("ignored" in str(warnval[-1].message))
|
|
|
|
# Check to make sure results are OK
|
|
np.testing.assert_array_almost_equal(lti_t, ios_t)
|
|
np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=3)
|
|
|
|
def test_named_signals(self):
|
|
sys1 = ios.NonlinearIOSystem(
|
|
updfcn = lambda t, x, u, params: np.array(
|
|
np.dot(self.mimo_linsys1.A, np.reshape(x, (-1, 1))) \
|
|
+ np.dot(self.mimo_linsys1.B, np.reshape(u, (-1, 1)))
|
|
).reshape(-1,),
|
|
outfcn = lambda t, x, u, params: np.array(
|
|
self.mimo_linsys1.C * np.reshape(x, (-1, 1)) \
|
|
+ self.mimo_linsys1.D * np.reshape(u, (-1, 1))
|
|
).reshape(-1,),
|
|
inputs = ('u[0]', 'u[1]'),
|
|
outputs = ('y[0]', 'y[1]'),
|
|
states = self.mimo_linsys1.states,
|
|
name = 'sys1')
|
|
sys2 = ios.LinearIOSystem(self.mimo_linsys2,
|
|
inputs = ('u[0]', 'u[1]'),
|
|
outputs = ('y[0]', 'y[1]'),
|
|
name = 'sys2')
|
|
|
|
# Series interconnection (sys1 * sys2) using __mul__
|
|
ios_mul = sys1 * sys2
|
|
ss_series = self.mimo_linsys1 * self.mimo_linsys2
|
|
lin_series = ct.linearize(ios_mul, 0, 0)
|
|
for M, N in ((ss_series.A, lin_series.A), (ss_series.B, lin_series.B),
|
|
(ss_series.C, lin_series.C), (ss_series.D, lin_series.D)):
|
|
np.testing.assert_array_almost_equal(M, N)
|
|
|
|
# Series interconnection (sys1 * sys2) using series
|
|
ios_series = ct.series(sys2, sys1)
|
|
ss_series = ct.series(self.mimo_linsys2, self.mimo_linsys1)
|
|
lin_series = ct.linearize(ios_series, 0, 0)
|
|
for M, N in ((ss_series.A, lin_series.A), (ss_series.B, lin_series.B),
|
|
(ss_series.C, lin_series.C), (ss_series.D, lin_series.D)):
|
|
np.testing.assert_array_almost_equal(M, N)
|
|
|
|
# Series interconnection (sys1 * sys2) using named + mixed signals
|
|
ios_connect = ios.InterconnectedSystem(
|
|
(sys2, sys1),
|
|
connections=(
|
|
(('sys1', 'u[0]'), 'sys2.y[0]'),
|
|
('sys1.u[1]', 'sys2.y[1]')
|
|
),
|
|
inplist=('sys2.u[0]', ('sys2', 1)),
|
|
outlist=((1, 'y[0]'), 'sys1.y[1]')
|
|
)
|
|
lin_series = ct.linearize(ios_connect, 0, 0)
|
|
for M, N in ((ss_series.A, lin_series.A), (ss_series.B, lin_series.B),
|
|
(ss_series.C, lin_series.C), (ss_series.D, lin_series.D)):
|
|
np.testing.assert_array_almost_equal(M, N)
|
|
|
|
# Make sure that we can use input signal names as system outputs
|
|
ios_connect = ios.InterconnectedSystem(
|
|
(sys1, sys2),
|
|
connections=(
|
|
('sys2.u[0]', 'sys1.y[0]'), ('sys2.u[1]', 'sys1.y[1]'),
|
|
('sys1.u[0]', '-sys2.y[0]'), ('sys1.u[1]', '-sys2.y[1]')
|
|
),
|
|
inplist=('sys1.u[0]', 'sys1.u[1]'),
|
|
outlist=('sys2.u[0]', 'sys2.u[1]') # = sys1.y[0], sys1.y[1]
|
|
)
|
|
ss_feedback = ct.feedback(self.mimo_linsys1, self.mimo_linsys2)
|
|
lin_feedback = ct.linearize(ios_connect, 0, 0)
|
|
np.testing.assert_array_almost_equal(ss_feedback.A, lin_feedback.A)
|
|
np.testing.assert_array_almost_equal(ss_feedback.B, lin_feedback.B)
|
|
np.testing.assert_array_almost_equal(ss_feedback.C, lin_feedback.C)
|
|
np.testing.assert_array_almost_equal(ss_feedback.D, lin_feedback.D)
|
|
|
|
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 test_duplicates(self):
|
|
nlios = ios.NonlinearIOSystem(None, \
|
|
lambda t, x, u, params: u*u, inputs=1, outputs=1)
|
|
|
|
# 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.assertEqual(len(warnval), 0)
|
|
|
|
# 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 suite():
|
|
return unittest.TestLoader().loadTestsFromTestCase(TestTimeresp)
|
|
|
|
|
|
# Predator prey dynamics
|
|
def predprey(t, x, u, params={}):
|
|
r = params.get('r', 2)
|
|
d = params.get('d', 0.7)
|
|
b = params.get('b', 0.3)
|
|
k = params.get('k', 10)
|
|
a = params.get('a', 8)
|
|
c = params.get('c', 4)
|
|
|
|
# Dynamics for the system
|
|
dx0 = r * x[0] * (1 - x[0]/k) - a * x[1] * x[0]/(c + x[0])
|
|
dx1 = b * a * x[1] * x[0] / (c + x[0]) - d * x[1]
|
|
|
|
return np.array([dx0, dx1])
|
|
|
|
|
|
# Reduced planar vertical takeoff and landing dynamics
|
|
def pvtol(t, x, u, params={}):
|
|
from math import sin, cos
|
|
m = params.get('m', 4.) # kg, system mass
|
|
J = params.get('J', 0.0475) # kg m^2, system inertia
|
|
r = params.get('r', 0.25) # m, thrust offset
|
|
g = params.get('g', 9.8) # m/s, gravitational constant
|
|
c = params.get('c', 0.05) # N s/m, rotational damping
|
|
l = params.get('c', 0.1) # m, pivot location
|
|
return np.array([
|
|
x[3],
|
|
-c/m * x[1] + 1/m * cos(x[0]) * u[0] - 1/m * sin(x[0]) * u[1],
|
|
-g - c/m * x[2] + 1/m * sin(x[0]) * u[0] + 1/m * cos(x[0]) * u[1],
|
|
-l/J * sin(x[0]) + r/J * u[0]
|
|
])
|
|
|
|
def pvtol_full(t, x, u, params={}):
|
|
from math import sin, cos
|
|
m = params.get('m', 4.) # kg, system mass
|
|
J = params.get('J', 0.0475) # kg m^2, system inertia
|
|
r = params.get('r', 0.25) # m, thrust offset
|
|
g = params.get('g', 9.8) # m/s, gravitational constant
|
|
c = params.get('c', 0.05) # N s/m, rotational damping
|
|
l = params.get('c', 0.1) # m, pivot location
|
|
return np.array([
|
|
x[3], x[4], x[5],
|
|
-c/m * x[3] + 1/m * cos(x[2]) * u[0] - 1/m * sin(x[2]) * u[1],
|
|
-g - c/m * x[4] + 1/m * sin(x[2]) * u[0] + 1/m * cos(x[2]) * u[1],
|
|
-l/J * sin(x[2]) + r/J * u[0]
|
|
])
|
|
|
|
|
|
# Second order system dynamics
|
|
def secord_update(t, x, u, params={}):
|
|
omega0 = params.get('omega0', 1.)
|
|
zeta = params.get('zeta', 0.5)
|
|
u = np.array(u, ndmin=1)
|
|
return np.array([
|
|
x[1],
|
|
-2 * zeta * omega0 * x[1] - omega0*omega0 * x[0] + u[0]
|
|
])
|
|
def secord_output(t, x, u, params={}):
|
|
return np.array([x[0]])
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|