Austin Schuh | 9b881ae | 2019-01-04 07:29:20 +1100 | [diff] [blame] | 1 | #ifndef FRC971_CONTROL_LOOPS_C2D_H_ |
| 2 | #define FRC971_CONTROL_LOOPS_C2D_H_ |
| 3 | |
| 4 | #include <chrono> |
| 5 | |
| 6 | #include <Eigen/Dense> |
James Kuszmaul | 59a5c61 | 2019-01-22 07:56:08 -0800 | [diff] [blame] | 7 | // We need to include MatrixFunctions for the matrix exponential. |
| 8 | #include "unsupported/Eigen/MatrixFunctions" |
Austin Schuh | 9b881ae | 2019-01-04 07:29:20 +1100 | [diff] [blame] | 9 | |
| 10 | namespace frc971 { |
| 11 | namespace controls { |
| 12 | |
| 13 | template <typename Scalar, int num_states, int num_inputs> |
| 14 | void C2D(const ::Eigen::Matrix<Scalar, num_states, num_states> &A_continuous, |
| 15 | const ::Eigen::Matrix<Scalar, num_states, num_inputs> &B_continuous, |
| 16 | ::std::chrono::nanoseconds dt, |
| 17 | ::Eigen::Matrix<Scalar, num_states, num_states> *A, |
| 18 | ::Eigen::Matrix<Scalar, num_states, num_inputs> *B) { |
| 19 | // Trick from |
| 20 | // https://en.wikipedia.org/wiki/Discretization#Discretization_of_linear_state_space_models |
| 21 | // to solve for A and B more efficiently. |
| 22 | Eigen::Matrix<Scalar, num_states + num_inputs, num_states + num_inputs> |
| 23 | M_state_continuous; |
| 24 | M_state_continuous.setZero(); |
| 25 | M_state_continuous.template block<num_states, num_states>(0, 0) = |
| 26 | A_continuous * |
| 27 | ::std::chrono::duration_cast<::std::chrono::duration<Scalar>>(dt).count(); |
| 28 | M_state_continuous.template block<num_states, num_inputs>(0, num_states) = |
| 29 | B_continuous * |
| 30 | ::std::chrono::duration_cast<::std::chrono::duration<Scalar>>(dt).count(); |
| 31 | |
| 32 | Eigen::Matrix<Scalar, num_states + num_inputs, num_states + num_inputs> |
| 33 | M_state = M_state_continuous.exp(); |
| 34 | *A = M_state.template block<num_states, num_states>(0, 0); |
| 35 | *B = M_state.template block<num_states, num_inputs>(0, num_states); |
| 36 | } |
| 37 | |
James Kuszmaul | 59a5c61 | 2019-01-22 07:56:08 -0800 | [diff] [blame] | 38 | template <typename Scalar, int num_states> |
| 39 | void DiscretizeQ( |
| 40 | const Eigen::Matrix<Scalar, num_states, num_states> &Q_continuous, |
| 41 | const Eigen::Matrix<Scalar, num_states, num_states> &A_continuous, |
| 42 | ::std::chrono::nanoseconds dt, |
| 43 | Eigen::Matrix<Scalar, num_states, num_states> *Q_d) { |
| 44 | Eigen::Matrix<Scalar, num_states, num_states> Qtemp = |
| 45 | (Q_continuous + Q_continuous.transpose()) / static_cast<Scalar>(2.0); |
| 46 | Eigen::Matrix<Scalar, 2 * num_states, 2 * num_states> M_gain; |
| 47 | M_gain.setZero(); |
| 48 | // Set up the matrix M = [[-A, Q], [0, A.T]] |
| 49 | M_gain.template block<num_states, num_states>(0, 0) = -A_continuous; |
| 50 | M_gain.template block<num_states, num_states>(0, num_states) = Qtemp; |
| 51 | M_gain.template block<num_states, num_states>(num_states, num_states) = |
| 52 | A_continuous.transpose(); |
| 53 | |
| 54 | Eigen::Matrix<Scalar, 2 * num_states, 2 *num_states> phi = |
| 55 | (M_gain * |
| 56 | ::std::chrono::duration_cast<::std::chrono::duration<Scalar>>(dt) |
| 57 | .count()).exp(); |
| 58 | |
| 59 | // Phi12 = phi[0:num_states, num_states:2*num_states] |
| 60 | // Phi22 = phi[num_states:2*num_states, |
| 61 | // num_states:2*num_states] |
| 62 | Eigen::Matrix<Scalar, num_states, num_states> phi12 = |
| 63 | phi.block(0, num_states, num_states, num_states); |
| 64 | Eigen::Matrix<Scalar, num_states, num_states> phi22 = |
| 65 | phi.block(num_states, num_states, num_states, num_states); |
| 66 | |
| 67 | *Q_d = phi22.transpose() * phi12; |
| 68 | *Q_d = (*Q_d + Q_d->transpose()) / static_cast<Scalar>(2.0); |
| 69 | } |
| 70 | |
Austin Schuh | 9b881ae | 2019-01-04 07:29:20 +1100 | [diff] [blame] | 71 | } // namespace controls |
| 72 | } // namespace frc971 |
| 73 | |
| 74 | #endif // FRC971_CONTROL_LOOPS_C2D_H_ |