Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 1 | #ifndef FRC971_CONTROL_LOOPS_DLQR_H_ |
| 2 | #define FRC971_CONTROL_LOOPS_DLQR_H_ |
| 3 | |
| 4 | #include <Eigen/Dense> |
| 5 | |
| 6 | namespace frc971 { |
| 7 | namespace controls { |
| 8 | |
Austin Schuh | cb09171 | 2018-02-21 20:01:55 -0800 | [diff] [blame] | 9 | template <int num_states, int num_inputs> |
| 10 | int Controllability(const ::Eigen::Matrix<double, num_states, num_states> &A, |
| 11 | const ::Eigen::Matrix<double, num_states, num_inputs> &B) { |
Austin Schuh | 7be032e | 2018-12-23 09:22:29 +1100 | [diff] [blame] | 12 | Eigen::Matrix<double, num_states, num_states * num_inputs> controllability; |
| 13 | controllability.block(0, 0, num_states, num_inputs) = B; |
Austin Schuh | cb09171 | 2018-02-21 20:01:55 -0800 | [diff] [blame] | 14 | |
Austin Schuh | 7be032e | 2018-12-23 09:22:29 +1100 | [diff] [blame] | 15 | for (size_t i = 1; i < num_states; i++) { |
| 16 | controllability.block(0, i * num_inputs, num_states, num_inputs) = |
| 17 | A * |
| 18 | controllability.block(0, (i - 1) * num_inputs, num_states, num_inputs); |
| 19 | } |
Austin Schuh | cb09171 | 2018-02-21 20:01:55 -0800 | [diff] [blame] | 20 | |
Austin Schuh | 7be032e | 2018-12-23 09:22:29 +1100 | [diff] [blame] | 21 | return Eigen::FullPivLU< |
| 22 | Eigen::Matrix<double, num_states, num_states * num_inputs>>( |
| 23 | controllability) |
| 24 | .rank(); |
Austin Schuh | cb09171 | 2018-02-21 20:01:55 -0800 | [diff] [blame] | 25 | } |
| 26 | |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 27 | extern "C" { |
| 28 | // Forward declaration for slicot fortran library. |
| 29 | void sb02od_(char *DICO, char *JOBB, char *FACT, char *UPLO, char *JOBL, |
| 30 | char *SORT, long *N, long *M, long *P, double *A, long *LDA, |
| 31 | double *B, long *LDB, double *Q, long *LDQ, double *R, long *LDR, |
| 32 | double *L, long *LDL, double *RCOND, double *X, long *LDX, |
| 33 | double *ALFAR, double *ALFAI, double *BETA, double *S, long *LDS, |
| 34 | double *T, long *LDT, double *U, long *LDU, double *TOL, |
| 35 | long *IWORK, double *DWORK, long *LDWORK, long *BWORK, long *INFO); |
| 36 | } |
| 37 | |
Austin Schuh | cb09171 | 2018-02-21 20:01:55 -0800 | [diff] [blame] | 38 | // Computes the optimal LQR controller K for the provided system and costs. |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 39 | template <int kN, int kM> |
Austin Schuh | cb09171 | 2018-02-21 20:01:55 -0800 | [diff] [blame] | 40 | int dlqr(::Eigen::Matrix<double, kN, kN> A, ::Eigen::Matrix<double, kN, kM> B, |
James Kuszmaul | 9776b39 | 2023-01-14 14:08:08 -0800 | [diff] [blame] | 41 | ::Eigen::Matrix<double, kN, kN> Q, ::Eigen::Matrix<double, kM, kM> R, |
| 42 | ::Eigen::Matrix<double, kM, kN> *K, |
| 43 | ::Eigen::Matrix<double, kN, kN> *S) { |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 44 | *K = ::Eigen::Matrix<double, kM, kN>::Zero(); |
| 45 | *S = ::Eigen::Matrix<double, kN, kN>::Zero(); |
| 46 | // Discrete (not continuous) |
| 47 | char DICO = 'D'; |
| 48 | // B and R are provided instead of G. |
| 49 | char JOBB = 'B'; |
| 50 | // Not factored, Q and R are provide. |
| 51 | char FACT = 'N'; |
| 52 | // Store the upper triangle of Q and R. |
| 53 | char UPLO = 'U'; |
| 54 | // L is zero. |
| 55 | char JOBL = 'Z'; |
| 56 | // Stable eigenvalues first in the sort order |
| 57 | char SORT = 'S'; |
| 58 | |
Austin Schuh | 8bfe229 | 2018-12-21 11:09:24 +1100 | [diff] [blame] | 59 | long N = kN; |
| 60 | long M = kM; |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 61 | // Not needed since FACT = N |
| 62 | long P = 0; |
| 63 | |
| 64 | long LDL = 1; |
| 65 | |
| 66 | double RCOND = 0.0; |
| 67 | ::Eigen::Matrix<double, kN, kN> X = ::Eigen::Matrix<double, kN, kN>::Zero(); |
| 68 | |
| 69 | double ALFAR[kN * 2]; |
| 70 | double ALFAI[kN * 2]; |
| 71 | double BETA[kN * 2]; |
James Kuszmaul | 9776b39 | 2023-01-14 14:08:08 -0800 | [diff] [blame] | 72 | memset(ALFAR, 0, sizeof(ALFAR)); |
| 73 | memset(ALFAI, 0, sizeof(ALFAI)); |
| 74 | memset(BETA, 0, sizeof(BETA)); |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 75 | |
| 76 | long LDS = 2 * kN + kM; |
Austin Schuh | 612d95d | 2023-10-21 00:01:40 -0700 | [diff] [blame] | 77 | ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN + kM> S_schur = |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 78 | ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN + kM>::Zero(); |
| 79 | |
Austin Schuh | 612d95d | 2023-10-21 00:01:40 -0700 | [diff] [blame] | 80 | ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN> T = |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 81 | ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN>::Zero(); |
| 82 | long LDT = 2 * kN + kM; |
| 83 | |
Austin Schuh | 612d95d | 2023-10-21 00:01:40 -0700 | [diff] [blame] | 84 | ::Eigen::Matrix<double, 2 * kN, 2 * kN> U = |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 85 | ::Eigen::Matrix<double, 2 * kN, 2 * kN>::Zero(); |
| 86 | long LDU = 2 * kN; |
| 87 | |
| 88 | double TOL = 0.0; |
| 89 | |
| 90 | long IWORK[2 * kN > kM ? 2 * kN : kM]; |
James Kuszmaul | 9776b39 | 2023-01-14 14:08:08 -0800 | [diff] [blame] | 91 | memset(IWORK, 0, sizeof(IWORK)); |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 92 | |
| 93 | long LDWORK = 16 * kN + 3 * kM + 16; |
| 94 | |
| 95 | double DWORK[LDWORK]; |
James Kuszmaul | 9776b39 | 2023-01-14 14:08:08 -0800 | [diff] [blame] | 96 | memset(DWORK, 0, sizeof(DWORK)); |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 97 | |
| 98 | long INFO = 0; |
| 99 | |
James Kuszmaul | 9776b39 | 2023-01-14 14:08:08 -0800 | [diff] [blame] | 100 | long BWORK[kN * 2]; |
| 101 | memset(BWORK, 0, sizeof(BWORK)); |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 102 | |
| 103 | // TODO(austin): I can't tell if anything here is transposed... |
| 104 | sb02od_(&DICO, &JOBB, &FACT, &UPLO, &JOBL, &SORT, &N, &M, &P, A.data(), &N, |
| 105 | B.data(), &N, Q.data(), &N, R.data(), &M, nullptr, &LDL, &RCOND, |
| 106 | X.data(), &N, ALFAR, ALFAI, BETA, S_schur.data(), &LDS, T.data(), |
| 107 | &LDT, U.data(), &LDU, &TOL, IWORK, DWORK, &LDWORK, BWORK, &INFO); |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 108 | |
| 109 | *K = (R + B.transpose() * X * B).inverse() * B.transpose() * X * A; |
| 110 | if (S != nullptr) { |
| 111 | *S = X; |
| 112 | } |
Austin Schuh | cb09171 | 2018-02-21 20:01:55 -0800 | [diff] [blame] | 113 | |
| 114 | return INFO; |
Austin Schuh | 0378513 | 2018-02-19 18:29:06 -0800 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | } // namespace controls |
| 118 | } // namespace frc971 |
| 119 | |
| 120 | #endif // FRC971_CONTROL_LOOPS_DLQR_H_ |