blob: 57aa88ba6fb7f434d4a123124d9c65184644ebb2 [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();
44 *S = ::Eigen::Matrix<double, kN, kN>::Zero();
45 // Discrete (not continuous)
46 char DICO = 'D';
47 // B and R are provided instead of G.
48 char JOBB = 'B';
49 // Not factored, Q and R are provide.
50 char FACT = 'N';
51 // Store the upper triangle of Q and R.
52 char UPLO = 'U';
53 // L is zero.
54 char JOBL = 'Z';
55 // Stable eigenvalues first in the sort order
56 char SORT = 'S';
57
Austin Schuh8bfe2292018-12-21 11:09:24 +110058 long N = kN;
59 long M = kM;
Austin Schuh03785132018-02-19 18:29:06 -080060 // Not needed since FACT = N
61 long P = 0;
62
63 long LDL = 1;
64
65 double RCOND = 0.0;
66 ::Eigen::Matrix<double, kN, kN> X = ::Eigen::Matrix<double, kN, kN>::Zero();
67
68 double ALFAR[kN * 2];
69 double ALFAI[kN * 2];
70 double BETA[kN * 2];
James Kuszmaul9776b392023-01-14 14:08:08 -080071 memset(ALFAR, 0, sizeof(ALFAR));
72 memset(ALFAI, 0, sizeof(ALFAI));
73 memset(BETA, 0, sizeof(BETA));
Austin Schuh03785132018-02-19 18:29:06 -080074
75 long LDS = 2 * kN + kM;
Austin Schuh612d95d2023-10-21 00:01:40 -070076 ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN + kM> S_schur =
Austin Schuh03785132018-02-19 18:29:06 -080077 ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN + kM>::Zero();
78
Austin Schuh612d95d2023-10-21 00:01:40 -070079 ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN> T =
Austin Schuh03785132018-02-19 18:29:06 -080080 ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN>::Zero();
81 long LDT = 2 * kN + kM;
82
Austin Schuh612d95d2023-10-21 00:01:40 -070083 ::Eigen::Matrix<double, 2 * kN, 2 * kN> U =
Austin Schuh03785132018-02-19 18:29:06 -080084 ::Eigen::Matrix<double, 2 * kN, 2 * kN>::Zero();
85 long LDU = 2 * kN;
86
87 double TOL = 0.0;
88
89 long IWORK[2 * kN > kM ? 2 * kN : kM];
James Kuszmaul9776b392023-01-14 14:08:08 -080090 memset(IWORK, 0, sizeof(IWORK));
Austin Schuh03785132018-02-19 18:29:06 -080091
92 long LDWORK = 16 * kN + 3 * kM + 16;
93
94 double DWORK[LDWORK];
James Kuszmaul9776b392023-01-14 14:08:08 -080095 memset(DWORK, 0, sizeof(DWORK));
Austin Schuh03785132018-02-19 18:29:06 -080096
97 long INFO = 0;
98
James Kuszmaul9776b392023-01-14 14:08:08 -080099 long BWORK[kN * 2];
100 memset(BWORK, 0, sizeof(BWORK));
Austin Schuh03785132018-02-19 18:29:06 -0800101
102 // TODO(austin): I can't tell if anything here is transposed...
103 sb02od_(&DICO, &JOBB, &FACT, &UPLO, &JOBL, &SORT, &N, &M, &P, A.data(), &N,
104 B.data(), &N, Q.data(), &N, R.data(), &M, nullptr, &LDL, &RCOND,
105 X.data(), &N, ALFAR, ALFAI, BETA, S_schur.data(), &LDS, T.data(),
106 &LDT, U.data(), &LDU, &TOL, IWORK, DWORK, &LDWORK, BWORK, &INFO);
Austin Schuh03785132018-02-19 18:29:06 -0800107
108 *K = (R + B.transpose() * X * B).inverse() * B.transpose() * X * A;
109 if (S != nullptr) {
110 *S = X;
111 }
Austin Schuhcb091712018-02-21 20:01:55 -0800112
113 return INFO;
Austin Schuh03785132018-02-19 18:29:06 -0800114}
115
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -0800116} // namespace frc971::controls
Austin Schuh03785132018-02-19 18:29:06 -0800117
118#endif // FRC971_CONTROL_LOOPS_DLQR_H_