blob: d31f492abc72e8f95a00ba921c0d457aae56b138 [file] [log] [blame]
Austin Schuh03785132018-02-19 18:29:06 -08001#ifndef FRC971_CONTROL_LOOPS_DLQR_H_
2#define FRC971_CONTROL_LOOPS_DLQR_H_
3
4#include <Eigen/Dense>
5
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -08006namespace frc971::controls {
Austin Schuh03785132018-02-19 18:29:06 -08007
Austin Schuhcb091712018-02-21 20:01:55 -08008template <int num_states, int num_inputs>
9int Controllability(const ::Eigen::Matrix<double, num_states, num_states> &A,
10 const ::Eigen::Matrix<double, num_states, num_inputs> &B) {
Austin Schuh7be032e2018-12-23 09:22:29 +110011 Eigen::Matrix<double, num_states, num_states * num_inputs> controllability;
12 controllability.block(0, 0, num_states, num_inputs) = B;
Austin Schuhcb091712018-02-21 20:01:55 -080013
Austin Schuh7be032e2018-12-23 09:22:29 +110014 for (size_t i = 1; i < num_states; i++) {
15 controllability.block(0, i * num_inputs, num_states, num_inputs) =
16 A *
17 controllability.block(0, (i - 1) * num_inputs, num_states, num_inputs);
18 }
Austin Schuhcb091712018-02-21 20:01:55 -080019
Austin Schuh7be032e2018-12-23 09:22:29 +110020 return Eigen::FullPivLU<
21 Eigen::Matrix<double, num_states, num_states * num_inputs>>(
22 controllability)
23 .rank();
Austin Schuhcb091712018-02-21 20:01:55 -080024}
25
Austin Schuh03785132018-02-19 18:29:06 -080026extern "C" {
27// Forward declaration for slicot fortran library.
28void sb02od_(char *DICO, char *JOBB, char *FACT, char *UPLO, char *JOBL,
29 char *SORT, long *N, long *M, long *P, double *A, long *LDA,
30 double *B, long *LDB, double *Q, long *LDQ, double *R, long *LDR,
31 double *L, long *LDL, double *RCOND, double *X, long *LDX,
32 double *ALFAR, double *ALFAI, double *BETA, double *S, long *LDS,
33 double *T, long *LDT, double *U, long *LDU, double *TOL,
34 long *IWORK, double *DWORK, long *LDWORK, long *BWORK, long *INFO);
35}
36
Austin Schuhcb091712018-02-21 20:01:55 -080037// Computes the optimal LQR controller K for the provided system and costs.
Austin Schuh03785132018-02-19 18:29:06 -080038template <int kN, int kM>
Austin Schuhcb091712018-02-21 20:01:55 -080039int dlqr(::Eigen::Matrix<double, kN, kN> A, ::Eigen::Matrix<double, kN, kM> B,
James Kuszmaul9776b392023-01-14 14:08:08 -080040 ::Eigen::Matrix<double, kN, kN> Q, ::Eigen::Matrix<double, kM, kM> R,
41 ::Eigen::Matrix<double, kM, kN> *K,
42 ::Eigen::Matrix<double, kN, kN> *S) {
Austin Schuh03785132018-02-19 18:29:06 -080043 *K = ::Eigen::Matrix<double, kM, kN>::Zero();
James Kuszmaul601b23a2024-10-08 23:32:33 -070044 if (S != nullptr) {
45 *S = ::Eigen::Matrix<double, kN, kN>::Zero();
46 }
Austin Schuh03785132018-02-19 18:29:06 -080047 // Discrete (not continuous)
48 char DICO = 'D';
49 // B and R are provided instead of G.
50 char JOBB = 'B';
51 // Not factored, Q and R are provide.
52 char FACT = 'N';
53 // Store the upper triangle of Q and R.
54 char UPLO = 'U';
55 // L is zero.
56 char JOBL = 'Z';
57 // Stable eigenvalues first in the sort order
58 char SORT = 'S';
59
Austin Schuh8bfe2292018-12-21 11:09:24 +110060 long N = kN;
61 long M = kM;
Austin Schuh03785132018-02-19 18:29:06 -080062 // Not needed since FACT = N
63 long P = 0;
64
65 long LDL = 1;
66
67 double RCOND = 0.0;
68 ::Eigen::Matrix<double, kN, kN> X = ::Eigen::Matrix<double, kN, kN>::Zero();
69
70 double ALFAR[kN * 2];
71 double ALFAI[kN * 2];
72 double BETA[kN * 2];
James Kuszmaul9776b392023-01-14 14:08:08 -080073 memset(ALFAR, 0, sizeof(ALFAR));
74 memset(ALFAI, 0, sizeof(ALFAI));
75 memset(BETA, 0, sizeof(BETA));
Austin Schuh03785132018-02-19 18:29:06 -080076
77 long LDS = 2 * kN + kM;
Austin Schuh612d95d2023-10-21 00:01:40 -070078 ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN + kM> S_schur =
Austin Schuh03785132018-02-19 18:29:06 -080079 ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN + kM>::Zero();
80
Austin Schuh612d95d2023-10-21 00:01:40 -070081 ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN> T =
Austin Schuh03785132018-02-19 18:29:06 -080082 ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN>::Zero();
83 long LDT = 2 * kN + kM;
84
Austin Schuh612d95d2023-10-21 00:01:40 -070085 ::Eigen::Matrix<double, 2 * kN, 2 * kN> U =
Austin Schuh03785132018-02-19 18:29:06 -080086 ::Eigen::Matrix<double, 2 * kN, 2 * kN>::Zero();
87 long LDU = 2 * kN;
88
89 double TOL = 0.0;
90
91 long IWORK[2 * kN > kM ? 2 * kN : kM];
James Kuszmaul9776b392023-01-14 14:08:08 -080092 memset(IWORK, 0, sizeof(IWORK));
Austin Schuh03785132018-02-19 18:29:06 -080093
94 long LDWORK = 16 * kN + 3 * kM + 16;
95
96 double DWORK[LDWORK];
James Kuszmaul9776b392023-01-14 14:08:08 -080097 memset(DWORK, 0, sizeof(DWORK));
Austin Schuh03785132018-02-19 18:29:06 -080098
99 long INFO = 0;
100
James Kuszmaul9776b392023-01-14 14:08:08 -0800101 long BWORK[kN * 2];
102 memset(BWORK, 0, sizeof(BWORK));
Austin Schuh03785132018-02-19 18:29:06 -0800103
104 // TODO(austin): I can't tell if anything here is transposed...
105 sb02od_(&DICO, &JOBB, &FACT, &UPLO, &JOBL, &SORT, &N, &M, &P, A.data(), &N,
106 B.data(), &N, Q.data(), &N, R.data(), &M, nullptr, &LDL, &RCOND,
107 X.data(), &N, ALFAR, ALFAI, BETA, S_schur.data(), &LDS, T.data(),
108 &LDT, U.data(), &LDU, &TOL, IWORK, DWORK, &LDWORK, BWORK, &INFO);
Austin Schuh03785132018-02-19 18:29:06 -0800109
110 *K = (R + B.transpose() * X * B).inverse() * B.transpose() * X * A;
111 if (S != nullptr) {
112 *S = X;
113 }
Austin Schuhcb091712018-02-21 20:01:55 -0800114
115 return INFO;
Austin Schuh03785132018-02-19 18:29:06 -0800116}
117
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -0800118} // namespace frc971::controls
Austin Schuh03785132018-02-19 18:29:06 -0800119
120#endif // FRC971_CONTROL_LOOPS_DLQR_H_