|
1 | 1 | import tinympc |
2 | 2 | import numpy as np |
| 3 | +from autograd import jacobian |
| 4 | +import autograd.numpy as anp |
3 | 5 |
|
4 | 6 | # Toggle switch for adaptive rho |
5 | 7 | ENABLE_ADAPTIVE_RHO = True # Set to True to enable adaptive rho |
6 | 8 |
|
7 | 9 | # Quadrotor system matrices (12 states, 4 inputs) |
8 | | -A = np.eye(12) # Identity matrix for simplicity |
9 | | -B = np.zeros((12, 4)) |
10 | | -# Fill in control effectiveness |
11 | | -B[0:3, 0:4] = 0.01 * np.ones((3, 4)) # Position control |
12 | | -B[3:6, 0:4] = 0.05 * np.ones((3, 4)) # Velocity control |
13 | | -B[6:9, 0:4] = 0.02 * np.ones((3, 4)) # Attitude control |
14 | | -B[9:12, 0:4] = 0.1 * np.ones((3, 4)) # Angular velocity control |
15 | | - |
16 | | -# Cost matrices |
17 | | -Q = np.diag([10.0, 10.0, 10.0, 1.0, 1.0, 1.0, 5.0, 5.0, 5.0, 0.1, 0.1, 0.1]) |
18 | | -R = np.diag([0.1, 0.1, 0.1, 0.1]) |
| 10 | +rho_value = 5.0 |
| 11 | +Adyn = np.array([ |
| 12 | + 1.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0245250, 0.0000000, 0.0500000, 0.0000000, 0.0000000, 0.0000000, 0.0002044, 0.0000000, |
| 13 | + 0.0000000, 1.0000000, 0.0000000, -0.0245250, 0.0000000, 0.0000000, 0.0000000, 0.0500000, 0.0000000, -0.0002044, 0.0000000, 0.0000000, |
| 14 | + 0.0000000, 0.0000000, 1.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0500000, 0.0000000, 0.0000000, 0.0000000, |
| 15 | + 0.0000000, 0.0000000, 0.0000000, 1.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0250000, 0.0000000, 0.0000000, |
| 16 | + 0.0000000, 0.0000000, 0.0000000, 0.0000000, 1.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0250000, 0.0000000, |
| 17 | + 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 1.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0250000, |
| 18 | + 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.9810000, 0.0000000, 1.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0122625, 0.0000000, |
| 19 | + 0.0000000, 0.0000000, 0.0000000, -0.9810000, 0.0000000, 0.0000000, 0.0000000, 1.0000000, 0.0000000, -0.0122625, 0.0000000, 0.0000000, |
| 20 | + 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 1.0000000, 0.0000000, 0.0000000, 0.0000000, |
| 21 | + 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 1.0000000, 0.0000000, 0.0000000, |
| 22 | + 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 1.0000000, 0.0000000, |
| 23 | + 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 1.0000000 |
| 24 | +]).reshape(12, 12) |
| 25 | + |
| 26 | +# Input/control matrix |
| 27 | +Bdyn = np.array([ |
| 28 | + -0.0007069, 0.0007773, 0.0007091, -0.0007795, |
| 29 | + 0.0007034, 0.0007747, -0.0007042, -0.0007739, |
| 30 | + 0.0052554, 0.0052554, 0.0052554, 0.0052554, |
| 31 | + -0.1720966, -0.1895213, 0.1722891, 0.1893288, |
| 32 | + -0.1729419, 0.1901740, 0.1734809, -0.1907131, |
| 33 | + 0.0123423, -0.0045148, -0.0174024, 0.0095748, |
| 34 | + -0.0565520, 0.0621869, 0.0567283, -0.0623632, |
| 35 | + 0.0562756, 0.0619735, -0.0563386, -0.0619105, |
| 36 | + 0.2102143, 0.2102143, 0.2102143, 0.2102143, |
| 37 | + -13.7677303, -15.1617018, 13.7831318, 15.1463003, |
| 38 | + -13.8353509, 15.2139209, 13.8784751, -15.2570451, |
| 39 | + 0.9873856, -0.3611820, -1.3921880, 0.7659845 |
| 40 | +]).reshape(12, 4) |
| 41 | + |
| 42 | + |
| 43 | +Q_diag = np.array([100.0000000, 100.0000000, 100.0000000, 4.0000000, 4.0000000, 400.0000000, |
| 44 | + 4.0000000, 4.0000000, 4.0000000, 2.0408163, 2.0408163, 4.0000000]) |
| 45 | +R_diag = np.array([4.0000000, 4.0000000, 4.0000000, 4.0000000]) |
| 46 | +Q = np.diag(Q_diag) |
| 47 | +R = np.diag(R_diag) |
19 | 48 |
|
20 | 49 | N = 20 |
21 | 50 |
|
|
25 | 54 | u_max = np.ones(4) * 2.0 |
26 | 55 |
|
27 | 56 | # Setup with adaptive rho based on toggle |
28 | | -prob.setup(A, B, Q, R, N, rho=1.0, max_iter=100, u_min=u_min, u_max=u_max, |
| 57 | +prob.setup(Adyn, Bdyn, Q, R, N, rho=rho_value, max_iter=100, u_min=u_min, u_max=u_max, |
29 | 58 | adaptive_rho=1 if ENABLE_ADAPTIVE_RHO else 0) |
30 | 59 |
|
31 | 60 | if ENABLE_ADAPTIVE_RHO: |
|
34 | 63 | # First compute the cache terms (this will compute K, P, etc.) |
35 | 64 | Kinf, Pinf, Quu_inv, AmBKt = prob.compute_cache_terms() |
36 | 65 |
|
37 | | - # Compute sensitivity matrices using autograd |
38 | | - # For now, we'll use small perturbations to approximate derivatives |
39 | | - eps = 1e-4 |
40 | | - rho = 1.0 |
41 | | - |
42 | | - # Compute perturbed cache terms |
43 | | - prob.setup(A, B, Q, R, N, rho=rho + eps, max_iter=100, u_min=u_min, u_max=u_max, adaptive_rho=1) |
44 | | - Kinf_p, Pinf_p, Quu_inv_p, AmBKt_p = prob.compute_cache_terms() |
45 | | - |
46 | | - # Compute derivatives with respect to rho using finite differences |
47 | | - dK = (Kinf_p - Kinf) / eps |
48 | | - dP = (Pinf_p - Pinf) / eps |
49 | | - dC1 = (Quu_inv_p - Quu_inv) / eps # dC1 is derivative of Quu_inv |
50 | | - dC2 = (AmBKt_p - AmBKt) / eps # dC2 is derivative of AmBKt |
| 66 | + # Compute derivatives with respect to rho via Autograd's Jacobian |
| 67 | + def lqr_direct(rho): |
| 68 | + R_rho = anp.array(R) + rho * anp.eye(4) |
| 69 | + Q_rho = anp.array(Q) + rho * anp.eye(12) |
| 70 | + P = Q_rho |
| 71 | + for _ in range(5000): |
| 72 | + K = anp.linalg.solve( |
| 73 | + R_rho + Bdyn.T @ P @ Bdyn + 1e-8*anp.eye(4), |
| 74 | + Bdyn.T @ P @ Adyn |
| 75 | + ) |
| 76 | + P = Q_rho + Adyn.T @ P @ (Adyn - Bdyn @ K) |
| 77 | + # Final gain and cache matrices |
| 78 | + K = anp.linalg.solve( |
| 79 | + R_rho + Bdyn.T @ P @ Bdyn + 1e-8*anp.eye(4), |
| 80 | + Bdyn.T @ P @ Adyn |
| 81 | + ) |
| 82 | + C1 = anp.linalg.inv(R_rho + Bdyn.T @ P @ Bdyn) |
| 83 | + C2 = (Adyn - Bdyn @ K).T |
| 84 | + return anp.concatenate([K.flatten(), P.flatten(), C1.flatten(), C2.flatten()]) |
| 85 | + |
| 86 | + derivs = jacobian(lqr_direct)(rho_value) |
| 87 | + # split into four blocks and reshape |
| 88 | + sizes = [4*12, 12*12, 4*4, 12*12] |
| 89 | + parts = np.split(np.array(derivs), np.cumsum(sizes)[:-1]) |
| 90 | + dK = parts[0].reshape(4, 12) |
| 91 | + dP = parts[1].reshape(12, 12) |
| 92 | + dC1 = parts[2].reshape(4, 4) |
| 93 | + dC2 = parts[3].reshape(12, 12) |
51 | 94 |
|
52 | 95 | # Generate code with sensitivity matrices |
53 | 96 | prob.codegen_with_sensitivity("out", dK, dP, dC1, dC2, verbose=1) |
|
0 commit comments