blob: b66bd56b8abd5ecdb46d163db1e7564fb61862d2 [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#!/usr/bin/python
2
3"""
4Control loop pole placement library.
5
6This library will grow to support many different pole placement methods.
7Currently it only supports direct pole placement.
8"""
9
10__author__ = 'Austin Schuh (austin.linux@gmail.com)'
11
12import numpy
13import slycot
Austin Schuhc976f492015-02-22 21:28:18 -080014import scipy.signal.cont2discrete
brians343bc112013-02-10 01:53:46 +000015
16class Error (Exception):
17 """Base class for all control loop exceptions."""
18
19
20class PolePlacementError(Error):
21 """Exception raised when pole placement fails."""
22
23
24# TODO(aschuh): dplace should take a control system object.
25# There should also exist a function to manipulate laplace expressions, and
26# something to plot bode plots and all that.
27def dplace(A, B, poles, alpha=1e-6):
28 """Set the poles of (A - BF) to poles.
29
30 Args:
31 A: numpy.matrix(n x n), The A matrix.
32 B: numpy.matrix(n x m), The B matrix.
33 poles: array(imaginary numbers), The poles to use. Complex conjugates poles
34 must be in pairs.
35
36 Raises:
37 ValueError: Arguments were the wrong shape or there were too many poles.
38 PolePlacementError: Pole placement failed.
39
40 Returns:
41 numpy.matrix(m x n), K
42 """
43 # See http://www.icm.tu-bs.de/NICONET/doc/SB01BD.html for a description of the
44 # fortran code that this is cleaning up the interface to.
45 n = A.shape[0]
46 if A.shape[1] != n:
47 raise ValueError("A must be square")
48 if B.shape[0] != n:
49 raise ValueError("B must have the same number of states as A.")
50 m = B.shape[1]
51
52 num_poles = len(poles)
53 if num_poles > n:
54 raise ValueError("Trying to place more poles than states.")
55
56 out = slycot.sb01bd(n=n,
57 m=m,
58 np=num_poles,
59 alpha=alpha,
60 A=A,
61 B=B,
62 w=numpy.array(poles),
63 dico='D')
64
65 A_z = numpy.matrix(out[0])
66 num_too_small_eigenvalues = out[2]
67 num_assigned_eigenvalues = out[3]
68 num_uncontrollable_eigenvalues = out[4]
69 K = numpy.matrix(-out[5])
70 Z = numpy.matrix(out[6])
71
72 if num_too_small_eigenvalues != 0:
73 raise PolePlacementError("Number of eigenvalues that are too small "
74 "and are therefore unmodified is %d." %
75 num_too_small_eigenvalues)
76 if num_assigned_eigenvalues != num_poles:
77 raise PolePlacementError("Did not place all the eigenvalues that were "
78 "requested. Only placed %d eigenvalues." %
79 num_assigned_eigenvalues)
80 if num_uncontrollable_eigenvalues != 0:
81 raise PolePlacementError("Found %d uncontrollable eigenvlaues." %
82 num_uncontrollable_eigenvalues)
83
84 return K
Austin Schuhc8ca2442013-02-23 12:29:33 -080085
86
87def c2d(A, B, dt):
88 """Converts from continuous time state space representation to discrete time.
Austin Schuhc8ca2442013-02-23 12:29:33 -080089 Returns (A, B). C and D are unchanged."""
Austin Schuhc8ca2442013-02-23 12:29:33 -080090
Austin Schuhc976f492015-02-22 21:28:18 -080091 ans_a, ans_b, _, _, _ = scipy.signal.cont2discrete((A, B, None, None), dt)
92 return numpy.matrix(ans_a), numpy.matrix(ans_b)
Austin Schuh7ec34fd2014-02-15 22:27:46 -080093
94def ctrb(A, B):
95 """Returns the controlability matrix.
96
97 This matrix must have full rank for all the states to be controlable.
98 """
99 n = A.shape[0]
100 output = B
101 intermediate = B
102 for i in xrange(0, n):
103 intermediate = A * intermediate
104 output = numpy.concatenate((output, intermediate), axis=1)
105
106 return output
107
108def dlqr(A, B, Q, R):
109 """Solves for the optimal lqr controller.
110
111 x(n+1) = A * x(n) + B * u(n)
112 J = sum(0, inf, x.T * Q * x + u.T * R * u)
113 """
114
115 # P = (A.T * P * A) - (A.T * P * B * numpy.linalg.inv(R + B.T * P *B) * (A.T * P.T * B).T + Q
116
Austin Schuh1a387962015-01-31 16:36:20 -0800117 P, rcond, w, S, T = slycot.sb02od(
118 n=A.shape[0], m=B.shape[1], A=A, B=B, Q=Q, R=R, dico='D')
Austin Schuh7ec34fd2014-02-15 22:27:46 -0800119
120 F = numpy.linalg.inv(R + B.T * P *B) * B.T * P * A
121 return F
Austin Schuhe4a14f22015-03-01 00:12:29 -0800122
123def kalman(A, B, C, Q, R):
124 """Solves for the steady state kalman gain and covariance matricies.
125
126 Args:
127 A, B, C: SS matricies.
128 Q: The model uncertantity
129 R: The measurement uncertainty
130
131 Returns:
132 KalmanGain, Covariance.
133 """
134 P = Q
135
136 I = numpy.matrix(numpy.eye(P.shape[0]))
137 At = A.T
138 Ct = C.T
139 i = 0
140
141 while True:
142 last_P = P
143 P_prior = A * P * At + Q
144 S = C * P_prior * Ct + R
145 K = P_prior * Ct * numpy.linalg.inv(S)
146 P = (I - K * C) * P_prior
147
148 diff = P - last_P
149 i += 1
150 if numpy.linalg.norm(diff) / numpy.linalg.norm(P) < 1e-9:
151 break
152
153 return K, P