blob: 6a5083a7d846ea24af46f9d63f17e383e8ae93bd [file] [log] [blame]
#ifndef FRC971_CONTROL_LOOPS_DLQR_H_
#define FRC971_CONTROL_LOOPS_DLQR_H_
#include <Eigen/Dense>
namespace frc971::controls {
template <typename Scalar, int num_states, int num_inputs>
int Controllability(const ::Eigen::Matrix<Scalar, num_states, num_states> &A,
const ::Eigen::Matrix<Scalar, num_states, num_inputs> &B) {
Eigen::Matrix<Scalar, num_states, num_states * num_inputs> controllability;
controllability.block(0, 0, num_states, num_inputs) = B;
for (size_t i = 1; i < num_states; i++) {
controllability.block(0, i * num_inputs, num_states, num_inputs) =
A *
controllability.block(0, (i - 1) * num_inputs, num_states, num_inputs);
}
return Eigen::FullPivLU<
Eigen::Matrix<Scalar, num_states, num_states * num_inputs>>(
controllability)
.rank();
}
extern "C" {
// Forward declaration for slicot fortran library.
void sb02od_(char *DICO, char *JOBB, char *FACT, char *UPLO, char *JOBL,
char *SORT, long *N, long *M, long *P, double *A, long *LDA,
double *B, long *LDB, double *Q, long *LDQ, double *R, long *LDR,
double *L, long *LDL, double *RCOND, double *X, long *LDX,
double *ALFAR, double *ALFAI, double *BETA, double *S, long *LDS,
double *T, long *LDT, double *U, long *LDU, double *TOL,
long *IWORK, double *DWORK, long *LDWORK, long *BWORK, long *INFO);
}
// Computes the optimal LQR controller K for the provided system and costs.
template <int kN, int kM>
int dlqr(::Eigen::Matrix<double, kN, kN> A, ::Eigen::Matrix<double, kN, kM> B,
::Eigen::Matrix<double, kN, kN> Q, ::Eigen::Matrix<double, kM, kM> R,
::Eigen::Matrix<double, kM, kN> *K,
::Eigen::Matrix<double, kN, kN> *S) {
*K = ::Eigen::Matrix<double, kM, kN>::Zero();
if (S != nullptr) {
*S = ::Eigen::Matrix<double, kN, kN>::Zero();
}
// Discrete (not continuous)
char DICO = 'D';
// B and R are provided instead of G.
char JOBB = 'B';
// Not factored, Q and R are provide.
char FACT = 'N';
// Store the upper triangle of Q and R.
char UPLO = 'U';
// L is zero.
char JOBL = 'Z';
// Stable eigenvalues first in the sort order
char SORT = 'S';
long N = kN;
long M = kM;
// Not needed since FACT = N
long P = 0;
long LDL = 1;
double RCOND = 0.0;
::Eigen::Matrix<double, kN, kN> X = ::Eigen::Matrix<double, kN, kN>::Zero();
double ALFAR[kN * 2];
double ALFAI[kN * 2];
double BETA[kN * 2];
memset(ALFAR, 0, sizeof(ALFAR));
memset(ALFAI, 0, sizeof(ALFAI));
memset(BETA, 0, sizeof(BETA));
long LDS = 2 * kN + kM;
::Eigen::Matrix<double, 2 * kN + kM, 2 * kN + kM> S_schur =
::Eigen::Matrix<double, 2 * kN + kM, 2 * kN + kM>::Zero();
::Eigen::Matrix<double, 2 * kN + kM, 2 * kN> T =
::Eigen::Matrix<double, 2 * kN + kM, 2 * kN>::Zero();
long LDT = 2 * kN + kM;
::Eigen::Matrix<double, 2 * kN, 2 * kN> U =
::Eigen::Matrix<double, 2 * kN, 2 * kN>::Zero();
long LDU = 2 * kN;
double TOL = 0.0;
long IWORK[2 * kN > kM ? 2 * kN : kM];
memset(IWORK, 0, sizeof(IWORK));
long LDWORK = 16 * kN + 3 * kM + 16;
double DWORK[LDWORK];
memset(DWORK, 0, sizeof(DWORK));
long INFO = 0;
long BWORK[kN * 2];
memset(BWORK, 0, sizeof(BWORK));
// TODO(austin): I can't tell if anything here is transposed...
sb02od_(&DICO, &JOBB, &FACT, &UPLO, &JOBL, &SORT, &N, &M, &P, A.data(), &N,
B.data(), &N, Q.data(), &N, R.data(), &M, nullptr, &LDL, &RCOND,
X.data(), &N, ALFAR, ALFAI, BETA, S_schur.data(), &LDS, T.data(),
&LDT, U.data(), &LDU, &TOL, IWORK, DWORK, &LDWORK, BWORK, &INFO);
*K = (R + B.transpose() * X * B).inverse() * B.transpose() * X * A;
if (S != nullptr) {
*S = X;
}
return INFO;
}
} // namespace frc971::controls
#endif // FRC971_CONTROL_LOOPS_DLQR_H_