2020-12-26 14:12:05 -08:00
|
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
2022-04-29 22:29:20 -07:00
|
|
|
|
#include "frc/EigenCore.h"
|
2020-08-14 23:40:33 -07:00
|
|
|
|
#include "units/time.h"
|
2021-09-13 14:31:01 -07:00
|
|
|
|
#include "unsupported/Eigen/MatrixFunctions"
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
|
|
namespace frc {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Discretizes the given continuous A matrix.
|
|
|
|
|
|
*
|
2021-10-29 15:07:05 -07:00
|
|
|
|
* @tparam States Number of states.
|
2020-08-14 23:40:33 -07:00
|
|
|
|
* @param contA Continuous system matrix.
|
|
|
|
|
|
* @param dt Discretization timestep.
|
|
|
|
|
|
* @param discA Storage for discrete system matrix.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template <int States>
|
2022-04-29 22:29:20 -07:00
|
|
|
|
void DiscretizeA(const Matrixd<States, States>& contA, units::second_t dt,
|
|
|
|
|
|
Matrixd<States, States>* discA) {
|
2022-09-04 17:24:38 -07:00
|
|
|
|
// A_d = eᴬᵀ
|
2021-10-25 08:58:12 -07:00
|
|
|
|
*discA = (contA * dt.value()).exp();
|
2020-08-14 23:40:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Discretizes the given continuous A and B matrices.
|
|
|
|
|
|
*
|
2021-10-29 15:07:05 -07:00
|
|
|
|
* @tparam States Number of states.
|
|
|
|
|
|
* @tparam Inputs Number of inputs.
|
2020-08-14 23:40:33 -07:00
|
|
|
|
* @param contA Continuous system matrix.
|
|
|
|
|
|
* @param contB Continuous input matrix.
|
|
|
|
|
|
* @param dt Discretization timestep.
|
|
|
|
|
|
* @param discA Storage for discrete system matrix.
|
|
|
|
|
|
* @param discB Storage for discrete input matrix.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template <int States, int Inputs>
|
2022-04-29 22:29:20 -07:00
|
|
|
|
void DiscretizeAB(const Matrixd<States, States>& contA,
|
|
|
|
|
|
const Matrixd<States, Inputs>& contB, units::second_t dt,
|
|
|
|
|
|
Matrixd<States, States>* discA,
|
|
|
|
|
|
Matrixd<States, Inputs>* discB) {
|
2022-09-04 17:24:38 -07:00
|
|
|
|
// M = [A B]
|
|
|
|
|
|
// [0 0]
|
|
|
|
|
|
Matrixd<States + Inputs, States + Inputs> M;
|
|
|
|
|
|
M.template block<States, States>(0, 0) = contA;
|
|
|
|
|
|
M.template block<States, Inputs>(0, States) = contB;
|
2023-01-23 09:46:12 -08:00
|
|
|
|
M.template block<Inputs, States + Inputs>(States, 0).setZero();
|
2022-09-04 17:24:38 -07:00
|
|
|
|
|
|
|
|
|
|
// ϕ = eᴹᵀ = [A_d B_d]
|
|
|
|
|
|
// [ 0 I ]
|
|
|
|
|
|
Matrixd<States + Inputs, States + Inputs> phi = (M * dt.value()).exp();
|
|
|
|
|
|
|
|
|
|
|
|
*discA = phi.template block<States, States>(0, 0);
|
|
|
|
|
|
*discB = phi.template block<States, Inputs>(0, States);
|
2020-08-14 23:40:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Discretizes the given continuous A and Q matrices.
|
|
|
|
|
|
*
|
2021-10-29 15:07:05 -07:00
|
|
|
|
* @tparam States Number of states.
|
2020-08-14 23:40:33 -07:00
|
|
|
|
* @param contA Continuous system matrix.
|
|
|
|
|
|
* @param contQ Continuous process noise covariance matrix.
|
|
|
|
|
|
* @param dt Discretization timestep.
|
|
|
|
|
|
* @param discA Storage for discrete system matrix.
|
|
|
|
|
|
* @param discQ Storage for discrete process noise covariance matrix.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template <int States>
|
2022-04-29 22:29:20 -07:00
|
|
|
|
void DiscretizeAQ(const Matrixd<States, States>& contA,
|
|
|
|
|
|
const Matrixd<States, States>& contQ, units::second_t dt,
|
|
|
|
|
|
Matrixd<States, States>* discA,
|
|
|
|
|
|
Matrixd<States, States>* discQ) {
|
2020-08-14 23:40:33 -07:00
|
|
|
|
// Make continuous Q symmetric if it isn't already
|
2022-04-29 22:29:20 -07:00
|
|
|
|
Matrixd<States, States> Q = (contQ + contQ.transpose()) / 2.0;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
2022-09-04 17:24:38 -07:00
|
|
|
|
// M = [−A Q ]
|
|
|
|
|
|
// [ 0 Aᵀ]
|
2022-04-29 22:29:20 -07:00
|
|
|
|
Matrixd<2 * States, 2 * States> M;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
M.template block<States, States>(0, 0) = -contA;
|
|
|
|
|
|
M.template block<States, States>(0, States) = Q;
|
|
|
|
|
|
M.template block<States, States>(States, 0).setZero();
|
|
|
|
|
|
M.template block<States, States>(States, States) = contA.transpose();
|
|
|
|
|
|
|
2022-09-04 17:24:38 -07:00
|
|
|
|
// ϕ = eᴹᵀ = [−A_d A_d⁻¹Q_d]
|
|
|
|
|
|
// [ 0 A_dᵀ ]
|
2022-04-29 22:29:20 -07:00
|
|
|
|
Matrixd<2 * States, 2 * States> phi = (M * dt.value()).exp();
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
2022-09-04 17:24:38 -07:00
|
|
|
|
// ϕ₁₂ = A_d⁻¹Q_d
|
2022-04-29 22:29:20 -07:00
|
|
|
|
Matrixd<States, States> phi12 = phi.block(0, States, States, States);
|
2022-09-04 17:24:38 -07:00
|
|
|
|
|
|
|
|
|
|
// ϕ₂₂ = A_dᵀ
|
2022-04-29 22:29:20 -07:00
|
|
|
|
Matrixd<States, States> phi22 = phi.block(States, States, States, States);
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
|
|
*discA = phi22.transpose();
|
|
|
|
|
|
|
|
|
|
|
|
Q = *discA * phi12;
|
|
|
|
|
|
|
|
|
|
|
|
// Make discrete Q symmetric if it isn't already
|
|
|
|
|
|
*discQ = (Q + Q.transpose()) / 2.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Discretizes the given continuous A and Q matrices.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Rather than solving a 2N x 2N matrix exponential like in DiscretizeAQ()
|
|
|
|
|
|
* (which is expensive), we take advantage of the structure of the block matrix
|
|
|
|
|
|
* of A and Q.
|
|
|
|
|
|
*
|
2022-09-04 17:24:38 -07:00
|
|
|
|
* <ul>
|
|
|
|
|
|
* <li>eᴬᵀ, which is only N x N, is relatively cheap.
|
|
|
|
|
|
* <li>The upper-right quarter of the 2N x 2N matrix, which we can approximate
|
|
|
|
|
|
* using a taylor series to several terms and still be substantially
|
|
|
|
|
|
* cheaper than taking the big exponential.
|
|
|
|
|
|
* </ul>
|
2020-08-14 23:40:33 -07:00
|
|
|
|
*
|
2021-10-29 15:07:05 -07:00
|
|
|
|
* @tparam States Number of states.
|
2020-08-14 23:40:33 -07:00
|
|
|
|
* @param contA Continuous system matrix.
|
|
|
|
|
|
* @param contQ Continuous process noise covariance matrix.
|
|
|
|
|
|
* @param dt Discretization timestep.
|
|
|
|
|
|
* @param discA Storage for discrete system matrix.
|
|
|
|
|
|
* @param discQ Storage for discrete process noise covariance matrix.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template <int States>
|
2022-04-29 22:29:20 -07:00
|
|
|
|
void DiscretizeAQTaylor(const Matrixd<States, States>& contA,
|
|
|
|
|
|
const Matrixd<States, States>& contQ,
|
|
|
|
|
|
units::second_t dt, Matrixd<States, States>* discA,
|
|
|
|
|
|
Matrixd<States, States>* discQ) {
|
2022-09-04 17:24:38 -07:00
|
|
|
|
// T
|
|
|
|
|
|
// Q_d = ∫ e^(Aτ) Q e^(Aᵀτ) dτ
|
|
|
|
|
|
// 0
|
|
|
|
|
|
//
|
|
|
|
|
|
// M = [−A Q ]
|
|
|
|
|
|
// [ 0 Aᵀ]
|
|
|
|
|
|
// ϕ = eᴹᵀ
|
|
|
|
|
|
// ϕ₁₂ = A_d⁻¹Q_d
|
|
|
|
|
|
//
|
|
|
|
|
|
// Taylor series of ϕ:
|
|
|
|
|
|
//
|
|
|
|
|
|
// ϕ = eᴹᵀ = I + MT + 1/2 M²T² + 1/6 M³T³ + …
|
|
|
|
|
|
// ϕ = eᴹᵀ = I + MT + 1/2 T²M² + 1/6 T³M³ + …
|
|
|
|
|
|
//
|
|
|
|
|
|
// Taylor series of ϕ expanded for ϕ₁₂:
|
|
|
|
|
|
//
|
|
|
|
|
|
// ϕ₁₂ = 0 + QT + 1/2 T² (−AQ + QAᵀ) + 1/6 T³ (−A lastTerm + Q Aᵀ²) + …
|
|
|
|
|
|
//
|
|
|
|
|
|
// ```
|
|
|
|
|
|
// lastTerm = Q
|
|
|
|
|
|
// lastCoeff = T
|
|
|
|
|
|
// ATn = Aᵀ
|
|
|
|
|
|
// ϕ₁₂ = lastTerm lastCoeff = QT
|
|
|
|
|
|
//
|
|
|
|
|
|
// for i in range(2, 6):
|
|
|
|
|
|
// // i = 2
|
|
|
|
|
|
// lastTerm = −A lastTerm + Q ATn = −AQ + QAᵀ
|
|
|
|
|
|
// lastCoeff *= T/i → lastCoeff *= T/2 = 1/2 T²
|
|
|
|
|
|
// ATn *= Aᵀ = Aᵀ²
|
|
|
|
|
|
//
|
|
|
|
|
|
// // i = 3
|
|
|
|
|
|
// lastTerm = −A lastTerm + Q ATn = −A (−AQ + QAᵀ) + QAᵀ² = …
|
|
|
|
|
|
// …
|
|
|
|
|
|
// ```
|
|
|
|
|
|
|
2020-08-14 23:40:33 -07:00
|
|
|
|
// Make continuous Q symmetric if it isn't already
|
2022-04-29 22:29:20 -07:00
|
|
|
|
Matrixd<States, States> Q = (contQ + contQ.transpose()) / 2.0;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
2022-04-29 22:29:20 -07:00
|
|
|
|
Matrixd<States, States> lastTerm = Q;
|
2021-10-25 08:58:12 -07:00
|
|
|
|
double lastCoeff = dt.value();
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
2021-07-29 22:42:43 -07:00
|
|
|
|
// Aᵀⁿ
|
2022-09-04 17:24:38 -07:00
|
|
|
|
Matrixd<States, States> ATn = contA.transpose();
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
2022-04-29 22:29:20 -07:00
|
|
|
|
Matrixd<States, States> phi12 = lastTerm * lastCoeff;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
|
|
// i = 6 i.e. 5th order should be enough precision
|
|
|
|
|
|
for (int i = 2; i < 6; ++i) {
|
2022-09-04 17:24:38 -07:00
|
|
|
|
lastTerm = -contA * lastTerm + Q * ATn;
|
2021-10-25 08:58:12 -07:00
|
|
|
|
lastCoeff *= dt.value() / static_cast<double>(i);
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
|
|
phi12 += lastTerm * lastCoeff;
|
|
|
|
|
|
|
2022-09-04 17:24:38 -07:00
|
|
|
|
ATn *= contA.transpose();
|
2020-08-14 23:40:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DiscretizeA<States>(contA, dt, discA);
|
|
|
|
|
|
Q = *discA * phi12;
|
|
|
|
|
|
|
|
|
|
|
|
// Make discrete Q symmetric if it isn't already
|
|
|
|
|
|
*discQ = (Q + Q.transpose()) / 2.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Returns a discretized version of the provided continuous measurement noise
|
|
|
|
|
|
* covariance matrix.
|
|
|
|
|
|
*
|
2021-10-29 15:07:05 -07:00
|
|
|
|
* @tparam Outputs Number of outputs.
|
2020-08-14 23:40:33 -07:00
|
|
|
|
* @param R Continuous measurement noise covariance matrix.
|
|
|
|
|
|
* @param dt Discretization timestep.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template <int Outputs>
|
2022-04-29 22:29:20 -07:00
|
|
|
|
Matrixd<Outputs, Outputs> DiscretizeR(const Matrixd<Outputs, Outputs>& R,
|
|
|
|
|
|
units::second_t dt) {
|
2022-09-04 17:24:38 -07:00
|
|
|
|
// R_d = 1/T R
|
2021-10-25 08:58:12 -07:00
|
|
|
|
return R / dt.value();
|
2020-08-14 23:40:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace frc
|