Skip to content

Commit af7fa06

Browse files
committed
Small changes to fix reported bugs
* Fixes problems in genswitch (legend call) and lti (NoneType) * Small documentation updates
1 parent 7344b1e commit af7fa06

File tree

6 files changed

+56
-28
lines changed

6 files changed

+56
-28
lines changed

ChangeLog

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
2012-10-27 Richard Murray <murray@altura.local>
2+
3+
* src/statefbk.py (acker): small updates to docstring
4+
5+
* src/bdalg.py (feedback): fixed up docstring formatting (for sphinx)
6+
7+
* tests/statefbk_test.py (TestStatefbk.testAcker): skip
8+
ill-conditioned systems and loosened tolerances on acker() testing
9+
10+
* src/lti.py (timebaseEqual): replaced "type(sys.dt) == NoneType"
11+
with "sys.dt is None", based on feedback from Luke Peterson that
12+
NoneType is deprecated in python 3.
13+
14+
2012-10-22 Richard Murray <murray@altura.local>
15+
16+
* examples/genswitch.py (genswitch): updated calls to legend, fixing
17+
problem pointed out by Scott Livingston
18+
119
2012-10-19 Richard Murray <murray@dn0a1594a2.sunet>
220

321
* src/matlab.py (rlocus): reverted change in default argument

examples/genswitch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def genswitch(y, t, mu=4, n=2):
4040
mpl.figure(1); mpl.clf();
4141
mpl.axis([0, 5, 0, 5]); # box on;
4242
mpl.plot(u, f, '-', f, u, '--') # 'LineWidth', AM_data_linewidth);
43-
mpl.legend('z1, f(z1)', 'z2, f(z2)') # legend(lgh, 'boxoff');
43+
mpl.legend(('z1, f(z1)', 'z2, f(z2)')) # legend(lgh, 'boxoff');
4444
mpl.plot([0, 3], [0, 3], 'k-') # 'LineWidth', AM_ref_linewidth);
4545
mpl.plot(eqpt[0], eqpt[1], 'k.', eqpt[1], eqpt[0], 'k.',
4646
eqpt[2], eqpt[2], 'k.') # 'MarkerSize', AM_data_markersize*3);
@@ -62,7 +62,7 @@ def genswitch(y, t, mu=4, n=2):
6262

6363
mpl.xlabel('Time {\itt} [scaled]');
6464
mpl.ylabel('Protein concentrations [scaled]');
65-
mpl.legend('z1 (A)', 'z2 (B)') # 'Orientation', 'horizontal');
65+
mpl.legend(('z1 (A)', 'z2 (B)')) # 'Orientation', 'horizontal');
6666
# legend(legh, 'boxoff');
6767

6868
# Phase portrait

src/bdalg.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,13 @@ def feedback(sys1, sys2, sign=-1):
177177
Parameters
178178
----------
179179
sys1: scalar, StateSpace, or TransferFunction
180-
The primary plant.
180+
The primary plant.
181181
sys2: scalar, StateSpace, or TransferFunction
182-
The feedback plant (often a feedback controller).
183-
sign: scalar
184-
The sign of feedback. `sign` = -1 indicates negative feedback, and
185-
`sign` = 1 indicates positive feedback. `sign` is an optional argument; it
186-
assumes a value of -1 if not specified.
182+
The feedback plant (often a feedback controller).
183+
sign: scalar
184+
The sign of feedback. `sign` = -1 indicates negative feedback, and
185+
`sign` = 1 indicates positive feedback. `sign` is an optional
186+
argument; it assumes a value of -1 if not specified.
187187
188188
Returns
189189
-------

src/lti.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
timebaseEqual()
1313
"""
1414

15-
from types import NoneType
16-
1715
class Lti:
1816

1917
"""Lti is a parent class to linear time invariant control (LTI) objects.
@@ -96,7 +94,7 @@ def timebaseEqual(sys1, sys2):
9694
if (type(sys1.dt) == bool or type(sys2.dt) == bool):
9795
# Make sure both are unspecified discrete timebases
9896
return type(sys1.dt) == type(sys2.dt) and sys1.dt == sys2.dt
99-
elif (type(sys1.dt) == NoneType or type(sys2.dt) == NoneType):
97+
elif (sys1.dt is None or sys2.dt is None):
10098
# One or the other is unspecified => the other can be anything
10199
return True
102100
else:

src/statefbk.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -101,42 +101,46 @@ def place(A, B, p):
101101
return -F
102102

103103
# Contributed by Roberto Bucher <roberto.bucher@supsi.ch>
104-
def acker(A,B,poles):
105-
"""Pole placemenmt using Ackermann method
104+
def acker(A, B, poles):
105+
"""Pole placement using Ackermann method
106106
107107
Call:
108-
k=acker(A,B,poles)
108+
K = acker(A, B, poles)
109109
110110
Parameters
111111
----------
112-
A, B : State and input matrix of the system
113-
poles: desired poles
112+
A, B : 2-d arrays
113+
State and input matrix of the system
114+
poles: 1-d list
115+
Desired eigenvalue locations
114116
115117
Returns
116118
-------
117-
k: matrix
118-
State feedback gains
119+
K: matrix
120+
Gains such that A - B K has given eigenvalues
119121
120122
"""
121123
# Convert the inputs to matrices
122124
a = np.mat(A)
123125
b = np.mat(B)
124126

125127
# Make sure the system is controllable
126-
p = np.real(np.poly(poles))
127-
ct = ctrb(A,B)
128+
ct = ctrb(A, B)
128129
if sp.linalg.det(ct) == 0:
129130
raise ValueError, "System not reachable; pole placement invalid"
130131

132+
# Compute the desired characteristic polynomial
133+
p = np.real(np.poly(poles))
134+
131135
# Place the poles using Ackermann's method
132136
n = np.size(p)
133137
pmat = p[n-1]*a**0
134138
for i in np.arange(1,n):
135-
pmat = pmat+p[n-i-1]*a**i
136-
k = sp.linalg.inv(ct)*pmat
137-
k = k[-1][:]
139+
pmat = pmat + p[n-i-1]*a**i
140+
K = sp.linalg.inv(ct) * pmat
138141

139-
return k
142+
K = K[-1][:] # Extract the last row
143+
return K
140144

141145
def lqr(*args, **keywords):
142146
"""Linear quadratic regulator design

tests/statefbk_test.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def setUp(self):
1717
# Maximum number of inputs and outputs to test + 1
1818
self.maxTries = 4
1919
# Set to True to print systems to the output.
20-
self.debug = False
20+
self.debug = True
2121

2222
def testCtrbSISO(self):
2323
A = np.matrix("1. 2.; 3. 4.")
@@ -104,9 +104,10 @@ def testAcker(self):
104104

105105
# Make sure the system is not degenerate
106106
Cmat = ctrb(sys.A, sys.B)
107-
if (np.linalg.matrix_rank(Cmat) != states):
107+
if (np.linalg.matrix_rank(Cmat) != states or
108+
abs(np.linalg.det(Cmat)) < 1e-5):
108109
if (self.debug):
109-
print " skipping (not reachable)"
110+
print " skipping (not reachable or ill conditioned)"
110111
continue
111112

112113
# Place the poles at random locations
@@ -118,8 +119,15 @@ def testAcker(self):
118119
new = ss(sys.A - sys.B * K, sys.B, sys.C, sys.D)
119120
placed = pole(new)
120121

122+
# Debugging code
123+
# diff = np.sort(poles) - np.sort(placed)
124+
# if not all(diff < 0.001):
125+
# print "Found a problem:"
126+
# print sys
127+
# print "desired = ", poles
128+
121129
np.testing.assert_array_almost_equal(np.sort(poles),
122-
np.sort(placed))
130+
np.sort(placed), decimal=4)
123131

124132
def suite():
125133
return unittest.TestLoader().loadTestsFromTestCase(TestStatefbk)

0 commit comments

Comments
 (0)