blob: 50315e6f80e6365830695a8455b39bb59731f8ef [file] [log] [blame]
Austin Schuh9b881ae2019-01-04 07:29:20 +11001#ifndef FRC971_CONTROL_LOOPS_C2D_H_
2#define FRC971_CONTROL_LOOPS_C2D_H_
3
4#include <chrono>
5
6#include <Eigen/Dense>
James Kuszmaul651fc3f2019-05-15 21:14:25 -07007#include "aos/time/time.h"
James Kuszmaul59a5c612019-01-22 07:56:08 -08008// We need to include MatrixFunctions for the matrix exponential.
9#include "unsupported/Eigen/MatrixFunctions"
Austin Schuh9b881ae2019-01-04 07:29:20 +110010
11namespace frc971 {
12namespace controls {
13
14template <typename Scalar, int num_states, int num_inputs>
15void C2D(const ::Eigen::Matrix<Scalar, num_states, num_states> &A_continuous,
16 const ::Eigen::Matrix<Scalar, num_states, num_inputs> &B_continuous,
17 ::std::chrono::nanoseconds dt,
18 ::Eigen::Matrix<Scalar, num_states, num_states> *A,
19 ::Eigen::Matrix<Scalar, num_states, num_inputs> *B) {
20 // Trick from
21 // https://en.wikipedia.org/wiki/Discretization#Discretization_of_linear_state_space_models
22 // to solve for A and B more efficiently.
23 Eigen::Matrix<Scalar, num_states + num_inputs, num_states + num_inputs>
24 M_state_continuous;
25 M_state_continuous.setZero();
26 M_state_continuous.template block<num_states, num_states>(0, 0) =
James Kuszmaul651fc3f2019-05-15 21:14:25 -070027 A_continuous * ::aos::time::TypedDurationInSeconds<Scalar>(dt);
Austin Schuh9b881ae2019-01-04 07:29:20 +110028 M_state_continuous.template block<num_states, num_inputs>(0, num_states) =
James Kuszmaul651fc3f2019-05-15 21:14:25 -070029 B_continuous * ::aos::time::TypedDurationInSeconds<Scalar>(dt);
Austin Schuh9b881ae2019-01-04 07:29:20 +110030
31 Eigen::Matrix<Scalar, num_states + num_inputs, num_states + num_inputs>
32 M_state = M_state_continuous.exp();
33 *A = M_state.template block<num_states, num_states>(0, 0);
34 *B = M_state.template block<num_states, num_inputs>(0, num_states);
35}
36
James Kuszmaul59a5c612019-01-22 07:56:08 -080037template <typename Scalar, int num_states>
38void DiscretizeQ(
39 const Eigen::Matrix<Scalar, num_states, num_states> &Q_continuous,
40 const Eigen::Matrix<Scalar, num_states, num_states> &A_continuous,
41 ::std::chrono::nanoseconds dt,
42 Eigen::Matrix<Scalar, num_states, num_states> *Q_d) {
43 Eigen::Matrix<Scalar, num_states, num_states> Qtemp =
44 (Q_continuous + Q_continuous.transpose()) / static_cast<Scalar>(2.0);
45 Eigen::Matrix<Scalar, 2 * num_states, 2 * num_states> M_gain;
46 M_gain.setZero();
47 // Set up the matrix M = [[-A, Q], [0, A.T]]
48 M_gain.template block<num_states, num_states>(0, 0) = -A_continuous;
49 M_gain.template block<num_states, num_states>(0, num_states) = Qtemp;
50 M_gain.template block<num_states, num_states>(num_states, num_states) =
51 A_continuous.transpose();
52
53 Eigen::Matrix<Scalar, 2 * num_states, 2 *num_states> phi =
James Kuszmaul651fc3f2019-05-15 21:14:25 -070054 (M_gain * ::aos::time::TypedDurationInSeconds<Scalar>(dt)).exp();
James Kuszmaul59a5c612019-01-22 07:56:08 -080055
56 // Phi12 = phi[0:num_states, num_states:2*num_states]
57 // Phi22 = phi[num_states:2*num_states,
58 // num_states:2*num_states]
59 Eigen::Matrix<Scalar, num_states, num_states> phi12 =
60 phi.block(0, num_states, num_states, num_states);
61 Eigen::Matrix<Scalar, num_states, num_states> phi22 =
62 phi.block(num_states, num_states, num_states, num_states);
63
James Kuszmaulb2a2f352019-03-02 16:59:34 -080064 Qtemp = phi22.transpose() * phi12;
65 *Q_d = (Qtemp + Qtemp.transpose()) / static_cast<Scalar>(2.0);
66}
67
68// Does a faster approximation for the discretizing A/Q, for if solving a 2Nx2N
69// matrix exponential is too expensive.
70// Basic reasoning/method:
71// The original algorithm does a matrix exponential on a 2N x 2N matrix (the
72// block matrix made of of A and Q). This is extremely expensive for larg-ish
73// matrices. This function takes advantage of the structure of the matrix
74// we are taking the exponential and notes that we care about two things:
75// 1) The exponential of A*t, which is only NxN and so is relatively cheap.
76// 2) The upper-right quarter of the 2Nx2N matrix, which we can approximate
77// using a taylor series to several terms and still be substantially cheaper
78// than taking the big exponential.
79template <typename Scalar, int num_states>
80void DiscretizeQAFast(
81 const Eigen::Matrix<Scalar, num_states, num_states> &Q_continuous,
82 const Eigen::Matrix<Scalar, num_states, num_states> &A_continuous,
83 ::std::chrono::nanoseconds dt,
84 Eigen::Matrix<Scalar, num_states, num_states> *Q_d,
85 Eigen::Matrix<Scalar, num_states, num_states> *A_d) {
86 Eigen::Matrix<Scalar, num_states, num_states> Qtemp =
87 (Q_continuous + Q_continuous.transpose()) / static_cast<Scalar>(2.0);
James Kuszmaul651fc3f2019-05-15 21:14:25 -070088 Scalar dt_d = ::aos::time::TypedDurationInSeconds<Scalar>(dt);
James Kuszmaulb2a2f352019-03-02 16:59:34 -080089 Eigen::Matrix<Scalar, num_states, num_states> last_term = Qtemp;
90 double last_coeff = dt_d;
91 const Eigen::Matrix<Scalar, num_states, num_states> At =
92 A_continuous.transpose();
93 Eigen::Matrix<Scalar, num_states, num_states> Atn = At;
94 Eigen::Matrix<Scalar, num_states, num_states> phi12 = last_term * last_coeff;
95 Eigen::Matrix<Scalar, num_states, num_states> phi22 =
96 At.Identity() + Atn * last_coeff;
97 // TODO(james): Tune this once we have the robot up; ii < 6 is enough to get
98 // beyond any real numerical issues. 5 should be fine, but just happened to
99 // kick a test over to failing. 4 would probably work.
100 for (int ii = 2; ii < 6; ++ii) {
101 Eigen::Matrix<Scalar, num_states, num_states> next_term =
102 -A_continuous * last_term + Qtemp * Atn;
103 last_coeff *= dt_d / static_cast<Scalar>(ii);
104 phi12 += next_term * last_coeff;
105
106 last_term = next_term;
107
108 Atn *= At;
109 phi22 += last_coeff * Atn;
110 }
James Kuszmaul651fc3f2019-05-15 21:14:25 -0700111 Eigen::Matrix<Scalar, num_states, num_states> phi22t = phi22.transpose();
James Kuszmaulb2a2f352019-03-02 16:59:34 -0800112
113 Qtemp = phi22t * phi12;
114 *Q_d = (Qtemp + Qtemp.transpose()) / static_cast<Scalar>(2.0);
115 *A_d = phi22t;
James Kuszmaul59a5c612019-01-22 07:56:08 -0800116}
117
Austin Schuh9b881ae2019-01-04 07:29:20 +1100118} // namespace controls
119} // namespace frc971
120
121#endif // FRC971_CONTROL_LOOPS_C2D_H_