Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Allow static gain StateSpace objects to be straightforwardly created.#107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 41 additions & 35 deletionscontrol/statesp.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -52,7 +52,7 @@

import numpy as np
from numpy import all, angle, any, array, asarray, concatenate, cos, delete, \
dot, empty, exp, eye,matrix,ones, pi, poly, poly1d, roots, shape, sin, \
dot, empty, exp, eye, ones, pi, poly, poly1d, roots, shape, sin, \
zeros, squeeze
from numpy.random import rand, randn
from numpy.linalg import solve, eigvals, matrix_rank
Expand All@@ -66,6 +66,19 @@

__all__ = ['StateSpace', 'ss', 'rss', 'drss', 'tf2ss', 'ssdata']


def _matrix(a):
"""_matrix(a) -> numpy.matrix
a - passed to numpy.matrix
Wrapper around numpy.matrix; unlike that function, _matrix([]) will be 0x0
"""
from numpy import matrix
am = matrix(a)
if (1,0) == am.shape:
am.shape = (0,0)
return am


class StateSpace(LTI):
"""A class for representing state-space models

Expand DownExpand Up@@ -122,34 +135,35 @@ def __init__(self, *args):
else:
raise ValueError("Needs 1 or 4 arguments; received %i." % len(args))

# Here we're going to convert inputs to matrices, if the user gave a
# non-matrix type.
#! TODO: [A, B, C, D] = map(matrix, [A, B, C, D])?
matrices = [A, B, C, D]
for i in range(len(matrices)):
# Convert to matrix first, if necessary.
matrices[i] = matrix(matrices[i])
[A, B, C, D] = matrices
A, B, C, D = [_matrix(M) for M in (A, B, C, D)]

LTI.__init__(self, B.shape[1], C.shape[0], dt)
# TODO: use super here?
LTI.__init__(self, inputs=D.shape[1], outputs=D.shape[0], dt=dt)
self.A = A
self.B = B
self.C = C
self.D = D

self.states = A.shape[0]
self.states = A.shape[1]

if 0 == self.states:
# static gain
# matrix's default "empty" shape is 1x0
A.shape = (0,0)
B.shape = (0,self.inputs)
C.shape = (self.outputs,0)

# Check that the matrix sizes are consistent.
if self.states != A.shape[1]:
if self.states != A.shape[0]:
raise ValueError("A must be square.")
if self.states != B.shape[0]:
raise ValueError("B must have the samerow size as A.")
raise ValueError("A andB must have the samenumber of rows.")
if self.states != C.shape[1]:
raise ValueError("C must have the samecolumn size as A.")
if self.inputs !=D.shape[1]:
raise ValueError("D must have the samecolumn size as B.")
if self.outputs !=D.shape[0]:
raise ValueError("D must have the samerow size as C.")
raise ValueError("A andC must have the samenumber of columns.")
if self.inputs !=B.shape[1]:
raise ValueError("B andD must have the samenumber of columns.")
if self.outputs !=C.shape[0]:
raise ValueError("C andD must have the samenumber of rows.")

# Check for states that don't do anything, and remove them.
self._remove_useless_states()
Expand DownExpand Up@@ -179,17 +193,10 @@ def _remove_useless_states(self):
useless.append(i)

# Remove the useless states.
if all(useless == range(self.states)):
# All the states were useless.
self.A = zeros((1, 1))
self.B = zeros((1, self.inputs))
self.C = zeros((self.outputs, 1))
else:
# A more typical scenario.
self.A = delete(self.A, useless, 0)
self.A = delete(self.A, useless, 1)
self.B = delete(self.B, useless, 0)
self.C = delete(self.C, useless, 1)
self.A = delete(self.A, useless, 0)
self.A = delete(self.A, useless, 1)
self.B = delete(self.B, useless, 0)
self.C = delete(self.C, useless, 1)

self.states = self.A.shape[0]
self.inputs = self.B.shape[1]
Expand DownExpand Up@@ -333,8 +340,9 @@ def __rmul__(self, other):
return _convertToStateSpace(other) * self

# try to treat this as a matrix
# TODO: doesn't _convertToStateSpace do this anyway?
try:
X =matrix(other)
X =_matrix(other)
C = X * self.C
D = X * self.D
return StateSpace(self.A, self.B, C, D, self.dt)
Expand DownExpand Up@@ -692,11 +700,9 @@ def _convertToStateSpace(sys, **kw):

# If this is a matrix, try to create a constant feedthrough
try:
D = matrix(sys)
outputs, inputs = D.shape

return StateSpace(0., zeros((1, inputs)), zeros((outputs, 1)), D)
except Exception(e):
D = _matrix(sys)
return StateSpace([], [], [], D)
except Exception as e:
print("Failure to assume argument is matrix-like in" \
" _convertToStateSpace, result %s" % e)

Expand Down
90 changes: 88 additions & 2 deletionscontrol/tests/statesp_test.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,9 +5,10 @@

import unittest
import numpy as np
from scipy.linalg import eigvals
from numpy.linalg import solve
from scipy.linalg import eigvals, block_diag
from control import matlab
from control.statesp import StateSpace, _convertToStateSpace
from control.statesp import StateSpace, _convertToStateSpace,tf2ss
from control.xferfcn import TransferFunction

class TestStateSpace(unittest.TestCase):
Expand DownExpand Up@@ -235,6 +236,91 @@ def test_dcgain(self):
sys3 = StateSpace(0., 1., 1., 0.)
np.testing.assert_equal(sys3.dcgain(), np.nan)


def test_scalarStaticGain(self):
"""Regression: can we create a scalar static gain?"""
g1=StateSpace([],[],[],[2])
g2=StateSpace([],[],[],[3])

# make sure StateSpace internals, specifically ABC matrix
# sizes, are OK for LTI operations
g3 = g1*g2
self.assertEqual(6, g3.D[0,0])
g4 = g1+g2
self.assertEqual(5, g4.D[0,0])
g5 = g1.feedback(g2)
self.assertAlmostEqual(2./7, g5.D[0,0])
g6 = g1.append(g2)
np.testing.assert_array_equal(np.diag([2,3]),g6.D)


def test_matrixStaticGain(self):
"""Regression: can we create matrix static gains?"""
d1 = np.matrix([[1,2,3],[4,5,6]])
d2 = np.matrix([[7,8],[9,10],[11,12]])
g1=StateSpace([],[],[],d1)

# _remove_useless_states was making A = [[0]]
self.assertEqual((0,0), g1.A.shape)

g2=StateSpace([],[],[],d2)
g3=StateSpace([],[],[],d2.T)

h1 = g1*g2
np.testing.assert_array_equal(d1*d2, h1.D)
h2 = g1+g3
np.testing.assert_array_equal(d1+d2.T, h2.D)
h3 = g1.feedback(g2)
np.testing.assert_array_almost_equal(solve(np.eye(2)+d1*d2,d1), h3.D)
h4 = g1.append(g2)
np.testing.assert_array_equal(block_diag(d1,d2),h4.D)


def test_remove_useless_states(self):
"""Regression: _remove_useless_states gives correct ABC sizes"""
g1 = StateSpace(np.zeros((3,3)),
np.zeros((3,4)),
np.zeros((5,3)),
np.zeros((5,4)))
self.assertEqual((0,0), g1.A.shape)
self.assertEqual((0,4), g1.B.shape)
self.assertEqual((5,0), g1.C.shape)
self.assertEqual((5,4), g1.D.shape)


def test_BadEmptyMatrices(self):
"""Mismatched ABCD matrices when some are empty"""
self.assertRaises(ValueError,StateSpace, [1], [], [], [1])
self.assertRaises(ValueError,StateSpace, [1], [1], [], [1])
self.assertRaises(ValueError,StateSpace, [1], [], [1], [1])
self.assertRaises(ValueError,StateSpace, [], [1], [], [1])
self.assertRaises(ValueError,StateSpace, [], [1], [1], [1])
self.assertRaises(ValueError,StateSpace, [], [], [1], [1])
self.assertRaises(ValueError,StateSpace, [1], [1], [1], [])


def test_Empty(self):
"""Regression: can we create an empty StateSpace object?"""
g1=StateSpace([],[],[],[])
self.assertEqual(0,g1.states)
self.assertEqual(0,g1.inputs)
self.assertEqual(0,g1.outputs)


def test_MatrixToStateSpace(self):
"""_convertToStateSpace(matrix) gives ss([],[],[],D)"""
D = np.matrix([[1,2,3],[4,5,6]])
g = _convertToStateSpace(D)
def empty(shape):
m = np.matrix([])
m.shape = shape
return m
np.testing.assert_array_equal(empty((0,0)), g.A)
np.testing.assert_array_equal(empty((0,D.shape[1])), g.B)
np.testing.assert_array_equal(empty((D.shape[0],0)), g.C)
np.testing.assert_array_equal(D,g.D)


class TestRss(unittest.TestCase):
"""These are tests for the proper functionality of statesp.rss."""

Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp