blob: 12ddab720bb911d45857e1098d3f75b32dcce05d [file] [log] [blame]
Austin Schuhdc1c84a2013-02-23 16:33:10 -08001#ifndef FRC971_CONTROL_LOOPS_STATEFEEDBACKLOOP_H_
2#define FRC971_CONTROL_LOOPS_STATEFEEDBACKLOOP_H_
3
4// wikipedia article is <http://en.wikipedia.org/wiki/State_observer>
5
6#include "Eigen/Dense"
7
8template <int number_of_states, int number_of_inputs, int number_of_outputs>
9class StateFeedbackPlant {
10 public:
11 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
12
13 const Eigen::Matrix<double, number_of_states, number_of_states> A;
14 const Eigen::Matrix<double, number_of_states, number_of_inputs> B;
15 const Eigen::Matrix<double, number_of_outputs, number_of_states> C;
16 const Eigen::Matrix<double, number_of_outputs, number_of_inputs> D;
17 const Eigen::Matrix<double, number_of_inputs, 1> U_min;
18 const Eigen::Matrix<double, number_of_inputs, 1> U_max;
19
20 Eigen::Matrix<double, number_of_states, 1> X;
21 Eigen::Matrix<double, number_of_outputs, 1> Y;
22 Eigen::Matrix<double, number_of_inputs, 1> U;
23
24 StateFeedbackPlant(const StateFeedbackPlant &other)
25 : A(other.A),
26 B(other.B),
27 C(other.C),
28 D(other.D),
29 U_min(other.U_min),
30 U_max(other.U_max) {
31 X.setZero();
32 Y.setZero();
33 U.setZero();
34 }
35
36 StateFeedbackPlant(
37 const Eigen::Matrix<double, number_of_states, number_of_states> &A,
38 const Eigen::Matrix<double, number_of_states, number_of_inputs> &B,
39 const Eigen::Matrix<double, number_of_outputs, number_of_states> &C,
40 const Eigen::Matrix<double, number_of_outputs, number_of_inputs> &D,
41 const Eigen::Matrix<double, number_of_outputs, 1> &U_max,
42 const Eigen::Matrix<double, number_of_outputs, 1> &U_min)
43 : A(A),
44 B(B),
45 C(C),
46 D(D),
47 U_min(U_min),
48 U_max(U_max) {
49 X.setZero();
50 Y.setZero();
51 U.setZero();
52 }
53
54 virtual ~StateFeedbackPlant() {}
55
56 // If U is outside the hardware range, limit it before the plant tries to use
57 // it.
58 virtual void CapU() {
59 for (int i = 0; i < kNumOutputs; ++i) {
60 if (U[i] > U_max[i]) {
61 U[i] = U_max[i];
62 } else if (U[i] < U_min[i]) {
63 U[i] = U_min[i];
64 }
65 }
66 }
67 // Computes the new X and Y given the control input.
68 void Update() {
69 CapU();
70 X = A * X + B * U;
71 Y = C * X + D * U;
72 }
73
74 protected:
75 // these are accessible from non-templated subclasses
76 static constexpr int kNumStates = number_of_states;
77 static constexpr int kNumOutputs = number_of_outputs;
78 static constexpr int kNumInputs = number_of_inputs;
79};
80
81template <int number_of_states, int number_of_inputs, int number_of_outputs>
82class StateFeedbackLoop {
83 public:
84 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
85
86 const Eigen::Matrix<double, number_of_states, number_of_outputs> L;
87 const Eigen::Matrix<double, number_of_outputs, number_of_states> K;
88
89 Eigen::Matrix<double, number_of_states, 1> X_hat;
90 Eigen::Matrix<double, number_of_states, 1> R;
91 Eigen::Matrix<double, number_of_inputs, 1> U;
92 Eigen::Matrix<double, number_of_outputs, 1> U_ff;
93 Eigen::Matrix<double, number_of_outputs, 1> Y;
94
95 StateFeedbackPlant<number_of_states, number_of_inputs,
96 number_of_outputs> plant;
97
98 StateFeedbackLoop(
99 const Eigen::Matrix<double, number_of_states, number_of_outputs> &L,
100 const Eigen::Matrix<double, number_of_outputs, number_of_states> &K,
101 const StateFeedbackPlant<number_of_states, number_of_inputs,
102 number_of_outputs> &plant)
103 : L(L),
104 K(K),
105 plant(plant) {
106 X_hat.setZero();
107 R.setZero();
108 U.setZero();
109 U_ff.setZero();
110 Y.setZero();
111 }
112 virtual ~StateFeedbackLoop() {}
113
114 virtual void FeedForward() {
115 for (int i = 0; i < number_of_outputs; ++i) {
116 U_ff[i] = 0.0;
117 }
118 }
119
120 // If U is outside the hardware range, limit it before the plant tries to use
121 // it.
122 virtual void CapU() {
123 for (int i = 0; i < kNumOutputs; ++i) {
124 if (U[i] > plant.U_max[i]) {
125 U[i] = plant.U_max[i];
126 } else if (U[i] < plant.U_min[i]) {
127 U[i] = plant.U_min[i];
128 }
129 }
130 }
131
132 // update_observer is whether or not to use the values in Y.
133 // stop_motors is whether or not to output all 0s.
134 void Update(bool update_observer, bool stop_motors) {
135 if (stop_motors) {
136 for (int i = 0; i < number_of_outputs; ++i) {
137 U[i] = 0.0;
138 }
139 } else {
140 U.noalias() = K * (R - X_hat);
141 CapU();
142 }
143
144 if (update_observer) {
145 X_hat = (plant.A - L * plant.C) * X_hat + L * Y + plant.B * U;
146 } else {
147 X_hat = plant.A * X_hat + plant.B * U;
148 }
149 }
150
151 protected:
152 // these are accessible from non-templated subclasses
153 static constexpr int kNumStates = number_of_states;
154 static constexpr int kNumOutputs = number_of_outputs;
155 static constexpr int kNumInputs = number_of_inputs;
156};
157
158#endif // FRC971_CONTROL_LOOPS_STATEFEEDBACKLOOP_H_