blob: dca57eb2a66d35e7b60f79e4950013640238ee26 [file] [log] [blame]
Austin Schuh085eab92020-11-26 13:54:51 -08001#!/usr/bin/python3
Austin Schuh0038f8e2016-07-20 19:57:01 -07002
3# This is an initial, hacky implementation of the extended LQR paper. It's just a proof of concept,
4# so don't trust it too much.
5
6import numpy
7import scipy.optimize
8from matplotlib import pylab
9import sys
10
11from frc971.control_loops.python import controls
12
13l = 100
14width = 0.2
15dt = 0.05
16num_states = 3
17num_inputs = 2
18x_hat_initial = numpy.matrix([[0.10], [1.0], [0.0]])
19
Ravago Jones5127ccc2022-07-31 16:32:45 -070020
Austin Schuh0038f8e2016-07-20 19:57:01 -070021def dynamics(X, U):
Ravago Jones5127ccc2022-07-31 16:32:45 -070022 """Calculates the dynamics for a 2 wheeled robot.
Austin Schuh0038f8e2016-07-20 19:57:01 -070023
24 Args:
25 X, numpy.matrix(3, 1), The state. [x, y, theta]
26 U, numpy.matrix(2, 1), The input. [left velocity, right velocity]
27
28 Returns:
29 numpy.matrix(3, 1), The derivative of the dynamics.
30 """
Ravago Jones5127ccc2022-07-31 16:32:45 -070031 #return numpy.matrix([[X[1, 0]],
32 # [X[2, 0]],
33 # [U[0, 0]]])
34 return numpy.matrix([[(U[0, 0] + U[1, 0]) * numpy.cos(X[2, 0]) / 2.0],
35 [(U[0, 0] + U[1, 0]) * numpy.sin(X[2, 0]) / 2.0],
36 [(U[1, 0] - U[0, 0]) / width]])
37
Austin Schuh0038f8e2016-07-20 19:57:01 -070038
39def RungeKutta(f, x, dt):
Ravago Jones5127ccc2022-07-31 16:32:45 -070040 """4th order RungeKutta integration of F starting at X."""
41 a = f(x)
42 b = f(x + dt / 2.0 * a)
43 c = f(x + dt / 2.0 * b)
44 d = f(x + dt * c)
45 return x + dt * (a + 2.0 * b + 2.0 * c + d) / 6.0
46
Austin Schuh0038f8e2016-07-20 19:57:01 -070047
48def discrete_dynamics(X, U):
Ravago Jones5127ccc2022-07-31 16:32:45 -070049 return RungeKutta(lambda startingX: dynamics(startingX, U), X, dt)
50
Austin Schuh0038f8e2016-07-20 19:57:01 -070051
52def inverse_discrete_dynamics(X, U):
Ravago Jones5127ccc2022-07-31 16:32:45 -070053 return RungeKutta(lambda startingX: -dynamics(startingX, U), X, dt)
54
Austin Schuh0038f8e2016-07-20 19:57:01 -070055
56def numerical_jacobian_x(fn, X, U, epsilon=1e-4):
Ravago Jones5127ccc2022-07-31 16:32:45 -070057 """Numerically estimates the jacobian around X, U in X.
Austin Schuh0038f8e2016-07-20 19:57:01 -070058
59 Args:
60 fn: A function of X, U.
61 X: numpy.matrix(num_states, 1), The state vector to take the jacobian
62 around.
63 U: numpy.matrix(num_inputs, 1), The input vector to take the jacobian
64 around.
65
66 Returns:
67 numpy.matrix(num_states, num_states), The jacobian of fn with X as the
68 variable.
69 """
Ravago Jones5127ccc2022-07-31 16:32:45 -070070 num_states = X.shape[0]
71 nominal = fn(X, U)
72 answer = numpy.matrix(numpy.zeros((nominal.shape[0], num_states)))
73 # It's more expensive, but +- epsilon will be more reliable
74 for i in range(0, num_states):
75 dX_plus = X.copy()
76 dX_plus[i] += epsilon
77 dX_minus = X.copy()
78 dX_minus[i] -= epsilon
79 answer[:, i] = (fn(dX_plus, U) - fn(dX_minus, U)) / epsilon / 2.0
80 return answer
81
Austin Schuh0038f8e2016-07-20 19:57:01 -070082
83def numerical_jacobian_u(fn, X, U, epsilon=1e-4):
Ravago Jones5127ccc2022-07-31 16:32:45 -070084 """Numerically estimates the jacobian around X, U in U.
Austin Schuh0038f8e2016-07-20 19:57:01 -070085
86 Args:
87 fn: A function of X, U.
88 X: numpy.matrix(num_states, 1), The state vector to take the jacobian
89 around.
90 U: numpy.matrix(num_inputs, 1), The input vector to take the jacobian
91 around.
92
93 Returns:
94 numpy.matrix(num_states, num_inputs), The jacobian of fn with U as the
95 variable.
96 """
Ravago Jones5127ccc2022-07-31 16:32:45 -070097 num_states = X.shape[0]
98 num_inputs = U.shape[0]
99 nominal = fn(X, U)
100 answer = numpy.matrix(numpy.zeros((nominal.shape[0], num_inputs)))
101 for i in range(0, num_inputs):
102 dU_plus = U.copy()
103 dU_plus[i] += epsilon
104 dU_minus = U.copy()
105 dU_minus[i] -= epsilon
106 answer[:, i] = (fn(X, dU_plus) - fn(X, dU_minus)) / epsilon / 2.0
107 return answer
108
Austin Schuh0038f8e2016-07-20 19:57:01 -0700109
110def numerical_jacobian_x_x(fn, X, U):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700111 return numerical_jacobian_x(
112 lambda X_inner, U_inner: numerical_jacobian_x(fn, X_inner, U_inner).T,
113 X, U)
114
Austin Schuh0038f8e2016-07-20 19:57:01 -0700115
116def numerical_jacobian_x_u(fn, X, U):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700117 return numerical_jacobian_x(
118 lambda X_inner, U_inner: numerical_jacobian_u(fn, X_inner, U_inner).T,
119 X, U)
120
Austin Schuh0038f8e2016-07-20 19:57:01 -0700121
122def numerical_jacobian_u_x(fn, X, U):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700123 return numerical_jacobian_u(
124 lambda X_inner, U_inner: numerical_jacobian_x(fn, X_inner, U_inner).T,
125 X, U)
126
Austin Schuh0038f8e2016-07-20 19:57:01 -0700127
128def numerical_jacobian_u_u(fn, X, U):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700129 return numerical_jacobian_u(
130 lambda X_inner, U_inner: numerical_jacobian_u(fn, X_inner, U_inner).T,
131 X, U)
132
Austin Schuh0038f8e2016-07-20 19:57:01 -0700133
134# Simple implementation for a quadratic cost function.
135class CostFunction:
Austin Schuh0038f8e2016-07-20 19:57:01 -0700136
Ravago Jones5127ccc2022-07-31 16:32:45 -0700137 def __init__(self):
138 self.num_states = num_states
139 self.num_inputs = num_inputs
140 self.dt = dt
141 self.Q = numpy.matrix([[0.1, 0, 0], [0, 0.6, 0], [0, 0, 0.1]
142 ]) / dt / dt
143 self.R = numpy.matrix([[0.40, 0], [0, 0.40]]) / dt / dt
144
145 def estimate_Q_final(self, X_hat):
146 """Returns the quadraticized final Q around X_hat.
Austin Schuh0038f8e2016-07-20 19:57:01 -0700147
148 This is calculated by evaluating partial^2 cost(X_hat) / (partial X * partial X)
149
150 Args:
151 X_hat: numpy.matrix(self.num_states, 1), The state to quadraticize around.
152
153 Result:
154 numpy.matrix(self.num_states, self.num_states)
155 """
Ravago Jones5127ccc2022-07-31 16:32:45 -0700156 zero_U = numpy.matrix(numpy.zeros((self.num_inputs, 1)))
157 return numerical_jacobian_x_x(self.final_cost, X_hat, zero_U)
Austin Schuh0038f8e2016-07-20 19:57:01 -0700158
Ravago Jones5127ccc2022-07-31 16:32:45 -0700159 def estimate_partial_cost_partial_x_final(self, X_hat):
160 """Returns \frac{\partial cost}{\partial X}(X_hat) for the final cost.
Austin Schuh0038f8e2016-07-20 19:57:01 -0700161
162 Args:
163 X_hat: numpy.matrix(self.num_states, 1), The state to quadraticize around.
164
165 Result:
166 numpy.matrix(self.num_states, 1)
167 """
Ravago Jones5127ccc2022-07-31 16:32:45 -0700168 return numerical_jacobian_x(
169 self.final_cost, X_hat,
170 numpy.matrix(numpy.zeros((self.num_inputs, 1)))).T
Austin Schuh0038f8e2016-07-20 19:57:01 -0700171
Ravago Jones5127ccc2022-07-31 16:32:45 -0700172 def estimate_q_final(self, X_hat):
173 """Returns q evaluated at X_hat for the final cost function."""
174 return self.estimate_partial_cost_partial_x_final(
175 X_hat) - self.estimate_Q_final(X_hat) * X_hat
Austin Schuh0038f8e2016-07-20 19:57:01 -0700176
Ravago Jones5127ccc2022-07-31 16:32:45 -0700177 def final_cost(self, X, U):
178 """Computes the final cost of being at X
Austin Schuh0038f8e2016-07-20 19:57:01 -0700179
180 Args:
181 X: numpy.matrix(self.num_states, 1)
182 U: numpy.matrix(self.num_inputs, 1), ignored
183
184 Returns:
185 numpy.matrix(1, 1), The quadratic cost of being at X
186 """
Ravago Jones5127ccc2022-07-31 16:32:45 -0700187 return X.T * self.Q * X * 1000
Austin Schuh0038f8e2016-07-20 19:57:01 -0700188
Ravago Jones5127ccc2022-07-31 16:32:45 -0700189 def cost(self, X, U):
190 """Computes the incremental cost given a position and U.
Austin Schuh0038f8e2016-07-20 19:57:01 -0700191
192 Args:
193 X: numpy.matrix(self.num_states, 1)
194 U: numpy.matrix(self.num_inputs, 1)
195
196 Returns:
197 numpy.matrix(1, 1), The quadratic cost of evaluating U.
198 """
Ravago Jones5127ccc2022-07-31 16:32:45 -0700199 return U.T * self.R * U + X.T * self.Q * X
200
Austin Schuh0038f8e2016-07-20 19:57:01 -0700201
202cost_fn_obj = CostFunction()
203
Ravago Jones5127ccc2022-07-31 16:32:45 -0700204S_bar_t = [
205 numpy.matrix(numpy.zeros((num_states, num_states))) for _ in range(l + 1)
206]
Austin Schuh0038f8e2016-07-20 19:57:01 -0700207s_bar_t = [numpy.matrix(numpy.zeros((num_states, 1))) for _ in range(l + 1)]
208s_scalar_bar_t = [numpy.matrix(numpy.zeros((1, 1))) for _ in range(l + 1)]
209
Ravago Jones5127ccc2022-07-31 16:32:45 -0700210L_t = [
211 numpy.matrix(numpy.zeros((num_inputs, num_states))) for _ in range(l + 1)
212]
Austin Schuh0038f8e2016-07-20 19:57:01 -0700213l_t = [numpy.matrix(numpy.zeros((num_inputs, 1))) for _ in range(l + 1)]
Ravago Jones5127ccc2022-07-31 16:32:45 -0700214L_bar_t = [
215 numpy.matrix(numpy.zeros((num_inputs, num_states))) for _ in range(l + 1)
216]
Austin Schuh0038f8e2016-07-20 19:57:01 -0700217l_bar_t = [numpy.matrix(numpy.zeros((num_inputs, 1))) for _ in range(l + 1)]
218
Ravago Jones5127ccc2022-07-31 16:32:45 -0700219S_t = [
220 numpy.matrix(numpy.zeros((num_states, num_states))) for _ in range(l + 1)
221]
Austin Schuh0038f8e2016-07-20 19:57:01 -0700222s_t = [numpy.matrix(numpy.zeros((num_states, 1))) for _ in range(l + 1)]
223s_scalar_t = [numpy.matrix(numpy.zeros((1, 1))) for _ in range(l + 1)]
224
Ravago Jones5127ccc2022-07-31 16:32:45 -0700225last_x_hat_t = [
226 numpy.matrix(numpy.zeros((num_states, 1))) for _ in range(l + 1)
227]
Austin Schuh0038f8e2016-07-20 19:57:01 -0700228
229for a in range(15):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700230 x_hat = x_hat_initial
231 u_t = L_t[0] * x_hat + l_t[0]
232 S_bar_t[0] = numpy.matrix(numpy.zeros((num_states, num_states)))
233 s_bar_t[0] = numpy.matrix(numpy.zeros((num_states, 1)))
234 s_scalar_bar_t[0] = numpy.matrix([[0]])
Austin Schuh0038f8e2016-07-20 19:57:01 -0700235
Ravago Jones5127ccc2022-07-31 16:32:45 -0700236 last_x_hat_t[0] = x_hat_initial
Austin Schuh0038f8e2016-07-20 19:57:01 -0700237
Ravago Jones5127ccc2022-07-31 16:32:45 -0700238 Q_t = numerical_jacobian_x_x(cost_fn_obj.cost, x_hat_initial, u_t)
239 P_t = numerical_jacobian_x_u(cost_fn_obj.cost, x_hat_initial, u_t)
240 R_t = numerical_jacobian_u_u(cost_fn_obj.cost, x_hat_initial, u_t)
Austin Schuh0038f8e2016-07-20 19:57:01 -0700241
Ravago Jones5127ccc2022-07-31 16:32:45 -0700242 q_t = numerical_jacobian_x(cost_fn_obj.cost, x_hat_initial,
243 u_t).T - Q_t * x_hat_initial - P_t.T * u_t
244 r_t = numerical_jacobian_u(cost_fn_obj.cost, x_hat_initial,
245 u_t).T - P_t * x_hat_initial - R_t * u_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700246
Ravago Jones5127ccc2022-07-31 16:32:45 -0700247 q_scalar_t = cost_fn_obj.cost(
248 x_hat_initial,
249 u_t) - 0.5 * (x_hat_initial.T *
250 (Q_t * x_hat_initial + P_t.T * u_t) + u_t.T *
251 (P_t * x_hat_initial + R_t * u_t)
252 ) - x_hat_initial.T * q_t - u_t.T * r_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700253
Ravago Jones5127ccc2022-07-31 16:32:45 -0700254 start_A_t = numerical_jacobian_x(discrete_dynamics, x_hat_initial, u_t)
255 start_B_t = numerical_jacobian_u(discrete_dynamics, x_hat_initial, u_t)
256 x_hat_next = discrete_dynamics(x_hat_initial, u_t)
257 start_c_t = x_hat_next - start_A_t * x_hat_initial - start_B_t * u_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700258
Ravago Jones5127ccc2022-07-31 16:32:45 -0700259 B_svd_u, B_svd_sigma_diag, B_svd_v = numpy.linalg.svd(start_B_t)
260 B_svd_sigma = numpy.matrix(numpy.zeros(start_B_t.shape))
261 B_svd_sigma[0:B_svd_sigma_diag.shape[0],
262 0:B_svd_sigma_diag.shape[0]] = numpy.diag(B_svd_sigma_diag)
Austin Schuh0038f8e2016-07-20 19:57:01 -0700263
Ravago Jones5127ccc2022-07-31 16:32:45 -0700264 B_svd_sigma_inv = numpy.matrix(numpy.zeros(start_B_t.shape)).T
265 B_svd_sigma_inv[0:B_svd_sigma_diag.shape[0],
266 0:B_svd_sigma_diag.shape[0]] = numpy.linalg.inv(
267 numpy.diag(B_svd_sigma_diag))
268 B_svd_inv = B_svd_v.T * B_svd_sigma_inv * B_svd_u.T
Austin Schuh0038f8e2016-07-20 19:57:01 -0700269
Ravago Jones5127ccc2022-07-31 16:32:45 -0700270 L_bar_t[1] = B_svd_inv
271 l_bar_t[1] = -B_svd_inv * (start_A_t * x_hat_initial + start_c_t)
Austin Schuh0038f8e2016-07-20 19:57:01 -0700272
Ravago Jones5127ccc2022-07-31 16:32:45 -0700273 S_bar_t[1] = L_bar_t[1].T * R_t * L_bar_t[1]
Austin Schuh0038f8e2016-07-20 19:57:01 -0700274
Ravago Jones5127ccc2022-07-31 16:32:45 -0700275 TotalS_1 = start_B_t.T * S_t[1] * start_B_t + R_t
276 Totals_1 = start_B_t.T * S_t[1] * (
277 start_c_t + start_A_t *
278 x_hat_initial) + start_B_t.T * s_t[1] + P_t * x_hat_initial + r_t
279 Totals_scalar_1 = 0.5 * (
280 start_c_t.T + x_hat_initial.T * start_A_t.T
281 ) * S_t[1] * (start_c_t + start_A_t * x_hat_initial) + s_scalar_t[
282 1] + x_hat_initial.T * q_t + q_scalar_t + 0.5 * x_hat_initial.T * Q_t * x_hat_initial + (
283 start_c_t.T + x_hat_initial.T * start_A_t.T) * s_t[1]
Austin Schuh0038f8e2016-07-20 19:57:01 -0700284
Ravago Jones5127ccc2022-07-31 16:32:45 -0700285 optimal_u_1 = -numpy.linalg.solve(TotalS_1, Totals_1)
286 optimal_x_1 = start_A_t * x_hat_initial + start_B_t * optimal_u_1 + start_c_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700287
Ravago Jones5127ccc2022-07-31 16:32:45 -0700288 S_bar_1_eigh_eigenvalues, S_bar_1_eigh_eigenvectors = numpy.linalg.eigh(
289 S_bar_t[1])
290 S_bar_1_eigh = numpy.matrix(numpy.diag(S_bar_1_eigh_eigenvalues))
291 S_bar_1_eigh_eigenvalues_stiff = S_bar_1_eigh_eigenvalues.copy()
292 for i in range(S_bar_1_eigh_eigenvalues_stiff.shape[0]):
293 if abs(S_bar_1_eigh_eigenvalues_stiff[i]) < 1e-8:
294 S_bar_1_eigh_eigenvalues_stiff[i] = max(
295 S_bar_1_eigh_eigenvalues_stiff) * 1.0
Austin Schuh0038f8e2016-07-20 19:57:01 -0700296
Austin Schuhea5f0a72024-09-02 15:02:36 -0700297 #print('eigh eigenvalues of S bar', S_bar_1_eigh_eigenvalues)
298 #print('eigh eigenvectors of S bar', S_bar_1_eigh_eigenvectors.T)
Austin Schuh0038f8e2016-07-20 19:57:01 -0700299
Austin Schuhea5f0a72024-09-02 15:02:36 -0700300 #print('S bar eig recreate', S_bar_1_eigh_eigenvectors * numpy.matrix(numpy.diag(S_bar_1_eigh_eigenvalues)) * S_bar_1_eigh_eigenvectors.T)
301 #print('S bar eig recreate error', (S_bar_1_eigh_eigenvectors * numpy.matrix(numpy.diag(S_bar_1_eigh_eigenvalues)) * S_bar_1_eigh_eigenvectors.T - S_bar_t[1]))
Austin Schuh0038f8e2016-07-20 19:57:01 -0700302
Ravago Jones5127ccc2022-07-31 16:32:45 -0700303 S_bar_stiff = S_bar_1_eigh_eigenvectors * numpy.matrix(
304 numpy.diag(
305 S_bar_1_eigh_eigenvalues_stiff)) * S_bar_1_eigh_eigenvectors.T
Austin Schuh0038f8e2016-07-20 19:57:01 -0700306
Austin Schuhea5f0a72024-09-02 15:02:36 -0700307 print('Min u', -numpy.linalg.solve(TotalS_1, Totals_1))
308 print('Min x_hat', optimal_x_1)
Ravago Jones5127ccc2022-07-31 16:32:45 -0700309 s_bar_t[1] = -s_t[1] - (S_bar_stiff + S_t[1]) * optimal_x_1
310 s_scalar_bar_t[1] = 0.5 * (
311 optimal_u_1.T * TotalS_1 * optimal_u_1 - optimal_x_1.T *
312 (S_bar_stiff + S_t[1]) *
313 optimal_x_1) + optimal_u_1.T * Totals_1 - optimal_x_1.T * (
314 s_bar_t[1] + s_t[1]) - s_scalar_t[1] + Totals_scalar_1
Austin Schuh0038f8e2016-07-20 19:57:01 -0700315
Austin Schuhea5f0a72024-09-02 15:02:36 -0700316 print('optimal_u_1', optimal_u_1)
317 print('TotalS_1', TotalS_1)
318 print('Totals_1', Totals_1)
319 print('Totals_scalar_1', Totals_scalar_1)
320 print(
321 'overall cost 1', 0.5 * (optimal_u_1.T * TotalS_1 * optimal_u_1) +
322 optimal_u_1.T * Totals_1 + Totals_scalar_1)
323 print(
324 'overall cost 0', 0.5 * (x_hat_initial.T * S_t[0] * x_hat_initial) +
325 x_hat_initial.T * s_t[0] + s_scalar_t[0])
Austin Schuh0038f8e2016-07-20 19:57:01 -0700326
Austin Schuhea5f0a72024-09-02 15:02:36 -0700327 print('t forward 0')
328 print('x_hat_initial[ 0]: %s' % (x_hat_initial))
329 print('x_hat[%2d]: %s' % (0, x_hat.T))
330 print('x_hat_next[%2d]: %s' % (0, x_hat_next.T))
331 print('u[%2d]: %s' % (0, u_t.T))
Ravago Jones5127ccc2022-07-31 16:32:45 -0700332 print('L[ 0]: %s' % (L_t[0], )).replace('\n', '\n ')
333 print('l[ 0]: %s' % (l_t[0], )).replace('\n', '\n ')
Austin Schuh0038f8e2016-07-20 19:57:01 -0700334
Ravago Jones5127ccc2022-07-31 16:32:45 -0700335 print('A_t[%2d]: %s' % (0, start_A_t)).replace('\n', '\n ')
336 print('B_t[%2d]: %s' % (0, start_B_t)).replace('\n', '\n ')
337 print('c_t[%2d]: %s' % (0, start_c_t)).replace('\n', '\n ')
Austin Schuh0038f8e2016-07-20 19:57:01 -0700338
Ravago Jones5127ccc2022-07-31 16:32:45 -0700339 # TODO(austin): optimal_x_1 is x_hat
340 x_hat = -numpy.linalg.solve((S_t[1] + S_bar_stiff), (s_t[1] + s_bar_t[1]))
Austin Schuhea5f0a72024-09-02 15:02:36 -0700341 print('new xhat', x_hat)
Austin Schuh0038f8e2016-07-20 19:57:01 -0700342
Ravago Jones5127ccc2022-07-31 16:32:45 -0700343 S_bar_t[1] = S_bar_stiff
Austin Schuh0038f8e2016-07-20 19:57:01 -0700344
Ravago Jones5127ccc2022-07-31 16:32:45 -0700345 last_x_hat_t[1] = x_hat
Austin Schuh0038f8e2016-07-20 19:57:01 -0700346
Ravago Jones5127ccc2022-07-31 16:32:45 -0700347 for t in range(1, l):
Austin Schuhea5f0a72024-09-02 15:02:36 -0700348 print('t forward', t)
Ravago Jones5127ccc2022-07-31 16:32:45 -0700349 u_t = L_t[t] * x_hat + l_t[t]
Austin Schuh0038f8e2016-07-20 19:57:01 -0700350
Ravago Jones5127ccc2022-07-31 16:32:45 -0700351 x_hat_next = discrete_dynamics(x_hat, u_t)
352 A_bar_t = numerical_jacobian_x(inverse_discrete_dynamics, x_hat_next,
353 u_t)
354 B_bar_t = numerical_jacobian_u(inverse_discrete_dynamics, x_hat_next,
355 u_t)
356 c_bar_t = x_hat - A_bar_t * x_hat_next - B_bar_t * u_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700357
Austin Schuhea5f0a72024-09-02 15:02:36 -0700358 print('x_hat[%2d]: %s' % (t, x_hat.T))
359 print('x_hat_next[%2d]: %s' % (t, x_hat_next.T))
Ravago Jones5127ccc2022-07-31 16:32:45 -0700360 print('L[%2d]: %s' % (
361 t,
362 L_t[t],
363 )).replace('\n', '\n ')
364 print('l[%2d]: %s' % (
365 t,
366 l_t[t],
367 )).replace('\n', '\n ')
Austin Schuhea5f0a72024-09-02 15:02:36 -0700368 print('u[%2d]: %s' % (t, u_t.T))
Austin Schuh0038f8e2016-07-20 19:57:01 -0700369
Ravago Jones5127ccc2022-07-31 16:32:45 -0700370 print('A_bar_t[%2d]: %s' % (t, A_bar_t)).replace(
371 '\n', '\n ')
372 print('B_bar_t[%2d]: %s' % (t, B_bar_t)).replace(
373 '\n', '\n ')
374 print('c_bar_t[%2d]: %s' % (t, c_bar_t)).replace(
375 '\n', '\n ')
Austin Schuh0038f8e2016-07-20 19:57:01 -0700376
Ravago Jones5127ccc2022-07-31 16:32:45 -0700377 Q_t = numerical_jacobian_x_x(cost_fn_obj.cost, x_hat, u_t)
378 P_t = numerical_jacobian_x_u(cost_fn_obj.cost, x_hat, u_t)
379 R_t = numerical_jacobian_u_u(cost_fn_obj.cost, x_hat, u_t)
Austin Schuh0038f8e2016-07-20 19:57:01 -0700380
Ravago Jones5127ccc2022-07-31 16:32:45 -0700381 q_t = numerical_jacobian_x(cost_fn_obj.cost, x_hat,
382 u_t).T - Q_t * x_hat - P_t.T * u_t
383 r_t = numerical_jacobian_u(cost_fn_obj.cost, x_hat,
384 u_t).T - P_t * x_hat - R_t * u_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700385
Ravago Jones5127ccc2022-07-31 16:32:45 -0700386 q_scalar_t = cost_fn_obj.cost(x_hat, u_t) - 0.5 * (
387 x_hat.T * (Q_t * x_hat + P_t.T * u_t) + u_t.T *
388 (P_t * x_hat + R_t * u_t)) - x_hat.T * q_t - u_t.T * r_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700389
Ravago Jones5127ccc2022-07-31 16:32:45 -0700390 C_bar_t = B_bar_t.T * (S_bar_t[t] + Q_t) * A_bar_t + P_t * A_bar_t
391 D_bar_t = A_bar_t.T * (S_bar_t[t] + Q_t) * A_bar_t
392 E_bar_t = B_bar_t.T * (
393 S_bar_t[t] +
394 Q_t) * B_bar_t + R_t + P_t * B_bar_t + B_bar_t.T * P_t.T
395 d_bar_t = A_bar_t.T * (s_bar_t[t] + q_t) + A_bar_t.T * (S_bar_t[t] +
396 Q_t) * c_bar_t
397 e_bar_t = r_t + P_t * c_bar_t + B_bar_t.T * s_bar_t[t] + B_bar_t.T * (
398 S_bar_t[t] + Q_t) * c_bar_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700399
Ravago Jones5127ccc2022-07-31 16:32:45 -0700400 L_bar_t[t + 1] = -numpy.linalg.inv(E_bar_t) * C_bar_t
401 l_bar_t[t + 1] = -numpy.linalg.inv(E_bar_t) * e_bar_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700402
Ravago Jones5127ccc2022-07-31 16:32:45 -0700403 S_bar_t[t + 1] = D_bar_t + C_bar_t.T * L_bar_t[t + 1]
404 s_bar_t[t + 1] = d_bar_t + C_bar_t.T * l_bar_t[t + 1]
405 s_scalar_bar_t[t + 1] = -0.5 * e_bar_t.T * numpy.linalg.inv(
406 E_bar_t) * e_bar_t + 0.5 * c_bar_t.T * (
407 S_bar_t[t] + Q_t) * c_bar_t + c_bar_t.T * s_bar_t[
408 t] + c_bar_t.T * q_t + s_scalar_bar_t[t] + q_scalar_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700409
Ravago Jones5127ccc2022-07-31 16:32:45 -0700410 x_hat = -numpy.linalg.solve((S_t[t + 1] + S_bar_t[t + 1]),
411 (s_t[t + 1] + s_bar_t[t + 1]))
Austin Schuh0038f8e2016-07-20 19:57:01 -0700412
Ravago Jones5127ccc2022-07-31 16:32:45 -0700413 S_t[l] = cost_fn_obj.estimate_Q_final(x_hat)
414 s_t[l] = cost_fn_obj.estimate_q_final(x_hat)
415 x_hat = -numpy.linalg.inv(S_t[l] + S_bar_t[l]) * (s_t[l] + s_bar_t[l])
Austin Schuh0038f8e2016-07-20 19:57:01 -0700416
Ravago Jones5127ccc2022-07-31 16:32:45 -0700417 for t in reversed(range(l)):
Austin Schuhea5f0a72024-09-02 15:02:36 -0700418 print('t backward', t)
Ravago Jones5127ccc2022-07-31 16:32:45 -0700419 # TODO(austin): I don't think we can use L_t like this here.
420 # I think we are off by 1 somewhere...
421 u_t = L_bar_t[t + 1] * x_hat + l_bar_t[t + 1]
Austin Schuh0038f8e2016-07-20 19:57:01 -0700422
Ravago Jones5127ccc2022-07-31 16:32:45 -0700423 x_hat_prev = inverse_discrete_dynamics(x_hat, u_t)
Austin Schuhea5f0a72024-09-02 15:02:36 -0700424 print('x_hat[%2d]: %s' % (t, x_hat.T))
425 print('x_hat_prev[%2d]: %s' % (t, x_hat_prev.T))
Ravago Jones5127ccc2022-07-31 16:32:45 -0700426 print('L_bar[%2d]: %s' % (t + 1, L_bar_t[t + 1])).replace(
427 '\n', '\n ')
428 print('l_bar[%2d]: %s' % (t + 1, l_bar_t[t + 1])).replace(
429 '\n', '\n ')
Austin Schuhea5f0a72024-09-02 15:02:36 -0700430 print('u[%2d]: %s' % (t, u_t.T))
Ravago Jones5127ccc2022-07-31 16:32:45 -0700431 # Now compute the linearized A, B, and C
432 # Start by doing it numerically, and then optimize.
433 A_t = numerical_jacobian_x(discrete_dynamics, x_hat_prev, u_t)
434 B_t = numerical_jacobian_u(discrete_dynamics, x_hat_prev, u_t)
435 c_t = x_hat - A_t * x_hat_prev - B_t * u_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700436
Ravago Jones5127ccc2022-07-31 16:32:45 -0700437 print('A_t[%2d]: %s' % (t, A_t)).replace('\n', '\n ')
438 print('B_t[%2d]: %s' % (t, B_t)).replace('\n', '\n ')
439 print('c_t[%2d]: %s' % (t, c_t)).replace('\n', '\n ')
Austin Schuh0038f8e2016-07-20 19:57:01 -0700440
Ravago Jones5127ccc2022-07-31 16:32:45 -0700441 Q_t = numerical_jacobian_x_x(cost_fn_obj.cost, x_hat_prev, u_t)
442 P_t = numerical_jacobian_x_u(cost_fn_obj.cost, x_hat_prev, u_t)
443 P_T_t = numerical_jacobian_u_x(cost_fn_obj.cost, x_hat_prev, u_t)
444 R_t = numerical_jacobian_u_u(cost_fn_obj.cost, x_hat_prev, u_t)
Austin Schuh0038f8e2016-07-20 19:57:01 -0700445
Ravago Jones5127ccc2022-07-31 16:32:45 -0700446 q_t = numerical_jacobian_x(cost_fn_obj.cost, x_hat_prev,
447 u_t).T - Q_t * x_hat_prev - P_T_t * u_t
448 r_t = numerical_jacobian_u(cost_fn_obj.cost, x_hat_prev,
449 u_t).T - P_t * x_hat_prev - R_t * u_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700450
Ravago Jones5127ccc2022-07-31 16:32:45 -0700451 q_scalar_t = cost_fn_obj.cost(x_hat_prev, u_t) - 0.5 * (
452 x_hat_prev.T * (Q_t * x_hat_prev + P_t.T * u_t) + u_t.T *
453 (P_t * x_hat_prev + R_t * u_t)) - x_hat_prev.T * q_t - u_t.T * r_t
Austin Schuh0038f8e2016-07-20 19:57:01 -0700454
Ravago Jones5127ccc2022-07-31 16:32:45 -0700455 C_t = P_t + B_t.T * S_t[t + 1] * A_t
456 D_t = Q_t + A_t.T * S_t[t + 1] * A_t
457 E_t = R_t + B_t.T * S_t[t + 1] * B_t
458 d_t = q_t + A_t.T * s_t[t + 1] + A_t.T * S_t[t + 1] * c_t
459 e_t = r_t + B_t.T * s_t[t + 1] + B_t.T * S_t[t + 1] * c_t
460 L_t[t] = -numpy.linalg.inv(E_t) * C_t
461 l_t[t] = -numpy.linalg.inv(E_t) * e_t
462 s_t[t] = d_t + C_t.T * l_t[t]
463 S_t[t] = D_t + C_t.T * L_t[t]
464 s_scalar_t[t] = q_scalar_t - 0.5 * e_t.T * numpy.linalg.inv(
465 E_t) * e_t + 0.5 * c_t.T * S_t[t + 1] * c_t + c_t.T * s_t[
466 t + 1] + s_scalar_t[t + 1]
Austin Schuh0038f8e2016-07-20 19:57:01 -0700467
Ravago Jones5127ccc2022-07-31 16:32:45 -0700468 x_hat = -numpy.linalg.solve((S_t[t] + S_bar_t[t]),
469 (s_t[t] + s_bar_t[t]))
470 if t == 0:
471 last_x_hat_t[t] = x_hat_initial
472 else:
473 last_x_hat_t[t] = x_hat
Austin Schuh0038f8e2016-07-20 19:57:01 -0700474
Ravago Jones5127ccc2022-07-31 16:32:45 -0700475 x_hat_t = [x_hat_initial]
Austin Schuh0038f8e2016-07-20 19:57:01 -0700476
Ravago Jones5127ccc2022-07-31 16:32:45 -0700477 pylab.figure('states %d' % a)
478 pylab.ion()
479 for dim in range(num_states):
480 pylab.plot(numpy.arange(len(last_x_hat_t)),
481 [x_hat_loop[dim, 0] for x_hat_loop in last_x_hat_t],
482 marker='o',
483 label='Xhat[%d]' % dim)
484 pylab.legend()
485 pylab.draw()
Austin Schuh0038f8e2016-07-20 19:57:01 -0700486
Ravago Jones5127ccc2022-07-31 16:32:45 -0700487 pylab.figure('xy %d' % a)
488 pylab.ion()
489 pylab.plot([x_hat_loop[0, 0] for x_hat_loop in last_x_hat_t],
490 [x_hat_loop[1, 0] for x_hat_loop in last_x_hat_t],
491 marker='o',
492 label='trajectory')
493 pylab.legend()
494 pylab.draw()
Austin Schuh0038f8e2016-07-20 19:57:01 -0700495
496final_u_t = [numpy.matrix(numpy.zeros((num_inputs, 1))) for _ in range(l + 1)]
497cost_to_go = []
498cost_to_come = []
499cost = []
500for t in range(l):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700501 cost_to_go.append((0.5 * last_x_hat_t[t].T * S_t[t] * last_x_hat_t[t] +
502 last_x_hat_t[t].T * s_t[t] + s_scalar_t[t])[0, 0])
503 cost_to_come.append(
504 (0.5 * last_x_hat_t[t].T * S_bar_t[t] * last_x_hat_t[t] +
505 last_x_hat_t[t].T * s_bar_t[t] + s_scalar_bar_t[t])[0, 0])
506 cost.append(cost_to_go[-1] + cost_to_come[-1])
507 final_u_t[t] = L_t[t] * last_x_hat_t[t] + l_t[t]
Austin Schuh0038f8e2016-07-20 19:57:01 -0700508
509for t in range(l):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700510 A_t = numerical_jacobian_x(discrete_dynamics, last_x_hat_t[t],
511 final_u_t[t])
512 B_t = numerical_jacobian_u(discrete_dynamics, last_x_hat_t[t],
513 final_u_t[t])
514 c_t = discrete_dynamics(
515 last_x_hat_t[t],
516 final_u_t[t]) - A_t * last_x_hat_t[t] - B_t * final_u_t[t]
517 print("Infeasability at", t, "is",
518 ((A_t * last_x_hat_t[t] + B_t * final_u_t[t] + c_t) -
519 last_x_hat_t[t + 1]).T)
Austin Schuh0038f8e2016-07-20 19:57:01 -0700520
521pylab.figure('u')
522samples = numpy.arange(len(final_u_t))
523for i in range(num_inputs):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700524 pylab.plot(samples, [u[i, 0] for u in final_u_t],
525 label='u[%d]' % i,
526 marker='o')
527 pylab.legend()
Austin Schuh0038f8e2016-07-20 19:57:01 -0700528
529pylab.figure('cost')
530cost_samples = numpy.arange(len(cost))
531pylab.plot(cost_samples, cost_to_go, label='cost to go', marker='o')
532pylab.plot(cost_samples, cost_to_come, label='cost to come', marker='o')
533pylab.plot(cost_samples, cost, label='cost', marker='o')
534pylab.legend()
535
536pylab.ioff()
537pylab.show()
538
539sys.exit(1)