blob: d7af42666b2de1cdaf0f51e8803e91ba4293a8ab [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
6namespace frc971 {
7namespace controls {
8
Austin Schuhcb091712018-02-21 20:01:55 -08009template <int num_states, int num_inputs>
10int Controllability(const ::Eigen::Matrix<double, num_states, num_states> &A,
11 const ::Eigen::Matrix<double, num_states, num_inputs> &B) {
Austin Schuh7be032e2018-12-23 09:22:29 +110012 Eigen::Matrix<double, num_states, num_states * num_inputs> controllability;
13 controllability.block(0, 0, num_states, num_inputs) = B;
Austin Schuhcb091712018-02-21 20:01:55 -080014
Austin Schuh7be032e2018-12-23 09:22:29 +110015 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 Schuhcb091712018-02-21 20:01:55 -080020
Austin Schuh7be032e2018-12-23 09:22:29 +110021 return Eigen::FullPivLU<
22 Eigen::Matrix<double, num_states, num_states * num_inputs>>(
23 controllability)
24 .rank();
Austin Schuhcb091712018-02-21 20:01:55 -080025}
26
Austin Schuh03785132018-02-19 18:29:06 -080027extern "C" {
28// Forward declaration for slicot fortran library.
29void 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 Schuhcb091712018-02-21 20:01:55 -080038// Computes the optimal LQR controller K for the provided system and costs.
Austin Schuh03785132018-02-19 18:29:06 -080039template <int kN, int kM>
Austin Schuhcb091712018-02-21 20:01:55 -080040int dlqr(::Eigen::Matrix<double, kN, kN> A, ::Eigen::Matrix<double, kN, kM> B,
James Kuszmaul9776b392023-01-14 14:08:08 -080041 ::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 Schuh03785132018-02-19 18:29:06 -080044 *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 Schuh8bfe2292018-12-21 11:09:24 +110059 long N = kN;
60 long M = kM;
Austin Schuh03785132018-02-19 18:29:06 -080061 // 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 Kuszmaul9776b392023-01-14 14:08:08 -080072 memset(ALFAR, 0, sizeof(ALFAR));
73 memset(ALFAI, 0, sizeof(ALFAI));
74 memset(BETA, 0, sizeof(BETA));
Austin Schuh03785132018-02-19 18:29:06 -080075
76 long LDS = 2 * kN + kM;
77 ::Eigen::Matrix<double, 2 * kN + kM, 2 *kN + kM> S_schur =
78 ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN + kM>::Zero();
79
80 ::Eigen::Matrix<double, 2 * kN + kM, 2 *kN> T =
81 ::Eigen::Matrix<double, 2 * kN + kM, 2 * kN>::Zero();
82 long LDT = 2 * kN + kM;
83
84 ::Eigen::Matrix<double, 2 * kN, 2 *kN> U =
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 Kuszmaul9776b392023-01-14 14:08:08 -080091 memset(IWORK, 0, sizeof(IWORK));
Austin Schuh03785132018-02-19 18:29:06 -080092
93 long LDWORK = 16 * kN + 3 * kM + 16;
94
95 double DWORK[LDWORK];
James Kuszmaul9776b392023-01-14 14:08:08 -080096 memset(DWORK, 0, sizeof(DWORK));
Austin Schuh03785132018-02-19 18:29:06 -080097
98 long INFO = 0;
99
James Kuszmaul9776b392023-01-14 14:08:08 -0800100 long BWORK[kN * 2];
101 memset(BWORK, 0, sizeof(BWORK));
Austin Schuh03785132018-02-19 18:29:06 -0800102
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 Schuh03785132018-02-19 18:29:06 -0800108
109 *K = (R + B.transpose() * X * B).inverse() * B.transpose() * X * A;
110 if (S != nullptr) {
111 *S = X;
112 }
Austin Schuhcb091712018-02-21 20:01:55 -0800113
114 return INFO;
Austin Schuh03785132018-02-19 18:29:06 -0800115}
116
117} // namespace controls
118} // namespace frc971
119
120#endif // FRC971_CONTROL_LOOPS_DLQR_H_