Skip to content

Commit 99302cd

Browse files
committed
Modify statefbk.place to use the YT algorithm implemented in scipy.signals.place_poles
1 parent 33bebc1 commit 99302cd

2 files changed

Lines changed: 53 additions & 28 deletions

File tree

control/statefbk.py

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747

4848
__all__ = ['ctrb', 'obsv', 'gram', 'place', 'lqr', 'acker']
4949

50+
5051
# Pole placement
5152
def place(A, B, p):
5253
"""Place closed loop eigenvalues
@@ -63,43 +64,48 @@ def place(A, B, p):
6364
Returns
6465
-------
6566
K : 2-d array
66-
Gains such that A - B K has given eigenvalues
67+
Gains such that A - B K has eigenvalues given in p
68+
69+
Algorithm
70+
---------
71+
This is a wrapper function for scipy.signal.place_poles, which
72+
implements the Tits and Yang algorithm [1]. It will handle SISO,
73+
MISO, and MIMO systems. If you want more control over the algorithm,
74+
use scipy.signal.place_poles directly.
75+
76+
[1] A.L. Tits and Y. Yang, "Globally convergent algorithms for robust
77+
pole assignment by state feedback, IEEE Transactions on Automatic
78+
Control, Vol. 41, pp. 1432-1452, 1996.
79+
80+
Limitations
81+
-----------
82+
The algorithm will not place poles at the same location more
83+
than rank(B) times.
6784
6885
Examples
6986
--------
7087
>>> A = [[-1, -1], [0, 1]]
7188
>>> B = [[0], [1]]
7289
>>> K = place(A, B, [-2, -5])
7390
"""
74-
75-
# Make sure that SLICOT is installed
76-
try:
77-
from slycot import sb01bd
78-
except ImportError:
79-
raise ControlSlycot("can't find slycot module 'sb01bd'")
91+
from scipy.signal import place_poles
8092

8193
# Convert the system inputs to NumPy arrays
82-
A_mat = np.array(A);
83-
B_mat = np.array(B);
84-
if (A_mat.shape[0] != A_mat.shape[1] or
85-
A_mat.shape[0] != B_mat.shape[0]):
86-
raise ControlDimension("matrix dimensions are incorrect")
87-
88-
# Compute the system eigenvalues and convert poles to numpy array
89-
system_eigs = np.linalg.eig(A_mat)[0]
90-
placed_eigs = np.array(p);
91-
92-
# SB01BD sets eigenvalues with real part less than alpha
93-
# We want to place all poles of the system => set alpha to minimum
94-
alpha = min(system_eigs.real);
95-
96-
# Call SLICOT routine to place the eigenvalues
97-
A_z,w,nfp,nap,nup,F,Z = \
98-
sb01bd(B_mat.shape[0], B_mat.shape[1], len(placed_eigs), alpha,
99-
A_mat, B_mat, placed_eigs, 'C');
100-
101-
# Return the gain matrix, with MATLAB gain convention
102-
return -F
94+
A_mat = np.array(A)
95+
B_mat = np.array(B)
96+
if (A_mat.shape[0] != A_mat.shape[1]):
97+
raise ControlDimension("A must be a square matrix")
98+
99+
if (A_mat.shape[0] != B_mat.shape[0]):
100+
err_str = "The number of rows of A must equal the number of rows in B"
101+
raise ControlDimension(err_str)
102+
103+
# Convert desired poles to numpy array
104+
placed_eigs = np.array(p)
105+
106+
result = place_poles(A_mat, B_mat, placed_eigs, method='YT')
107+
K = result.gain_matrix
108+
return K
103109

104110
# Contributed by Roberto Bucher <roberto.bucher@supsi.ch>
105111
def acker(A, B, poles):

control/tests/statefbk_test.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,25 @@ def testAcker(self):
157157
np.testing.assert_array_almost_equal(np.sort(poles),
158158
np.sort(placed), decimal=4)
159159

160+
def testPlace(self):
161+
# Matrices shamelessly stolen from scipy example code.
162+
A = np.array([[1.380, -0.2077, 6.715, -5.676],
163+
[-0.5814, -4.290, 0, 0.6750],
164+
[1.067, 4.273, -6.654, 5.893],
165+
[0.0480, 4.273, 1.343, -2.104]])
166+
167+
B = np.array([[0, 5.679],
168+
[1.136, 1.136],
169+
[0, 0,],
170+
[-3.146, 0]])
171+
P = np.array([-0.2, -0.5, -5.0566, -8.6659])
172+
K = place(A, B, P)
173+
P_placed = np.linalg.eigvals(A - B.dot(K))
174+
# No guarantee of the ordering, so sort them
175+
P.sort()
176+
P_placed.sort()
177+
np.testing.assert_array_almost_equal(P, P_placed)
178+
160179
def check_LQR(self, K, S, poles, Q, R):
161180
S_expected = np.array(np.sqrt(Q * R))
162181
K_expected = S_expected / R

0 commit comments

Comments
 (0)