-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontrols.py
More file actions
172 lines (132 loc) · 5.19 KB
/
Copy pathcontrols.py
File metadata and controls
172 lines (132 loc) · 5.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#!/usr/bin/python
"""
Control loop pole placement library.
This library will grow to support many different pole placement methods.
Currently it only supports direct pole placement.
"""
__author__ = 'Austin Schuh (austin.linux@gmail.com)'
import numpy
import slycot
import scipy.signal
#import glog
class Error (Exception):
"""Base class for all control loop exceptions."""
class PolePlacementError(Error):
"""Exception raised when pole placement fails."""
# TODO(aschuh): dplace should take a control system object.
# There should also exist a function to manipulate laplace expressions, and
# something to plot bode plots and all that.
def dplace(A, B, poles, alpha=1e-6):
"""Set the poles of (A - BF) to poles.
Args:
A: numpy.matrix(n x n), The A matrix.
B: numpy.matrix(n x m), The B matrix.
poles: array(imaginary numbers), The poles to use. Complex conjugates poles
must be in pairs.
Raises:
ValueError: Arguments were the wrong shape or there were too many poles.
PolePlacementError: Pole placement failed.
Returns:
numpy.matrix(m x n), K
"""
# See http://www.icm.tu-bs.de/NICONET/doc/SB01BD.html for a description of the
# fortran code that this is cleaning up the interface to.
n = A.shape[0]
if A.shape[1] != n:
raise ValueError("A must be square")
if B.shape[0] != n:
raise ValueError("B must have the same number of states as A.")
m = B.shape[1]
num_poles = len(poles)
if num_poles > n:
raise ValueError("Trying to place more poles than states.")
out = slycot.sb01bd(n=n,
m=m,
np=num_poles,
alpha=alpha,
A=A,
B=B,
w=numpy.array(poles),
dico='D')
A_z = numpy.matrix(out[0])
num_too_small_eigenvalues = out[2]
num_assigned_eigenvalues = out[3]
num_uncontrollable_eigenvalues = out[4]
K = numpy.matrix(-out[5])
Z = numpy.matrix(out[6])
if num_too_small_eigenvalues != 0:
raise PolePlacementError("Number of eigenvalues that are too small "
"and are therefore unmodified is %d." %
num_too_small_eigenvalues)
if num_assigned_eigenvalues != num_poles:
raise PolePlacementError("Did not place all the eigenvalues that were "
"requested. Only placed %d eigenvalues." %
num_assigned_eigenvalues)
if num_uncontrollable_eigenvalues != 0:
raise PolePlacementError("Found %d uncontrollable eigenvlaues." %
num_uncontrollable_eigenvalues)
return K
def c2d(A, B, dt):
"""Converts from continuous time state space representation to discrete time.
Returns (A, B). C and D are unchanged."""
ans_a, ans_b, _, _, _ = scipy.signal.cont2discrete((A, B, None, None), dt)
return numpy.matrix(ans_a), numpy.matrix(ans_b)
def ctrb(A, B):
"""Returns the controllability matrix.
This matrix must have full rank for all the states to be controllable.
"""
n = A.shape[0]
output = B
intermediate = B
for i in xrange(0, n):
intermediate = A * intermediate
output = numpy.concatenate((output, intermediate), axis=1)
return output
def dlqr(A, B, Q, R):
"""Solves for the optimal lqr controller.
x(n+1) = A * x(n) + B * u(n)
J = sum(0, inf, x.T * Q * x + u.T * R * u)
"""
# P = (A.T * P * A) - (A.T * P * B * numpy.linalg.inv(R + B.T * P *B) * (A.T * P.T * B).T + Q
P, rcond, w, S, T = slycot.sb02od(n=A.shape[0],m=B.shape[1],A=A,B=B,Q=Q,R=R,dico='D')
F = numpy.linalg.inv(R + B.T * P *B) * B.T * P * A
return F
def kalman(A, B, C, Q, R):
"""Solves for the steady state kalman gain and covariance matricies.
Args:
A, B, C: SS matricies.
Q: The model uncertantity
R: The measurement uncertainty
Returns:
KalmanGain, Covariance.
"""
I = numpy.matrix(numpy.eye(Q.shape[0]))
Z = numpy.matrix(numpy.zeros(Q.shape[0]))
n = A.shape[0]
m = C.shape[0]
controllability_rank = numpy.linalg.matrix_rank(ctrb(A.T, C.T))
#if controllability_rank != n:
# glog.warning('Observability of %d != %d, unobservable state',
# controllability_rank, n)
# Compute the steady state covariance matrix.
P_prior, rcond, w, S, T = slycot.sb02od(n=n, m=m, A=A.T, B=C.T, Q=Q, R=R, dico='D')
S = C * P_prior * C.T + R
K = numpy.linalg.lstsq(S.T, (P_prior * C.T).T)[0].T
P = (I - K * C) * P_prior
return K, P
def TwoStateFeedForwards(B, Q):
"""Computes the feed forwards constant for a 2 state controller.
This will take the form U = Kff * (R(n + 1) - A * R(n)), where Kff is the
feed-forwards constant. It is important that Kff is *only* computed off
the goal and not the feed back terms.
Args:
B: numpy.Matrix[num_states, num_inputs] The B matrix.
Q: numpy.Matrix[num_states, num_states] The Q (cost) matrix.
Returns:
numpy.Matrix[num_inputs, num_states]
"""
# We want to find the optimal U such that we minimize the tracking cost.
# This means that we want to minimize
# (B * U - (R(n+1) - A R(n)))^T * Q * (B * U - (R(n+1) - A R(n)))
# TODO(austin): This doesn't take into account the cost of U
return numpy.linalg.inv(B.T * Q * B) * B.T * Q.T