blob: d62bc6351a44fb5e2a15055beeeb48ef71acb0b1 [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
Austin Schuh849f0032013-03-03 23:59:53 -08004#include <assert.h>
Austin Schuhdc1c84a2013-02-23 16:33:10 -08005
Brian Silvermanc571e052013-03-13 17:58:56 -07006#include <vector>
Austin Schuhcda86af2014-02-16 16:16:39 -08007#include <iostream>
Brian Silvermanc571e052013-03-13 17:58:56 -07008
Austin Schuhdc1c84a2013-02-23 16:33:10 -08009#include "Eigen/Dense"
10
Austin Schuhcda86af2014-02-16 16:16:39 -080011#include "aos/common/logging/logging.h"
Brian Silverman0a151c92014-05-02 15:28:44 -070012#include "aos/common/macros.h"
13
14// TODO(brians): There are a lot of public member variables in here that should
15// be made private and have (const) accessors instead (and have _s at the end of
16// their names).
Austin Schuhcda86af2014-02-16 16:16:39 -080017
Austin Schuhdc1c84a2013-02-23 16:33:10 -080018template <int number_of_states, int number_of_inputs, int number_of_outputs>
Austin Schuhe3490622013-03-13 01:24:30 -070019class StateFeedbackPlantCoefficients {
Austin Schuhdc1c84a2013-02-23 16:33:10 -080020 public:
21 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
22
23 const Eigen::Matrix<double, number_of_states, number_of_states> A;
24 const Eigen::Matrix<double, number_of_states, number_of_inputs> B;
25 const Eigen::Matrix<double, number_of_outputs, number_of_states> C;
26 const Eigen::Matrix<double, number_of_outputs, number_of_inputs> D;
27 const Eigen::Matrix<double, number_of_inputs, 1> U_min;
28 const Eigen::Matrix<double, number_of_inputs, 1> U_max;
29
Austin Schuhe3490622013-03-13 01:24:30 -070030 StateFeedbackPlantCoefficients(const StateFeedbackPlantCoefficients &other)
Austin Schuhdc1c84a2013-02-23 16:33:10 -080031 : A(other.A),
32 B(other.B),
33 C(other.C),
34 D(other.D),
35 U_min(other.U_min),
36 U_max(other.U_max) {
Austin Schuhdc1c84a2013-02-23 16:33:10 -080037 }
38
Austin Schuhe3490622013-03-13 01:24:30 -070039 StateFeedbackPlantCoefficients(
Austin Schuhdc1c84a2013-02-23 16:33:10 -080040 const Eigen::Matrix<double, number_of_states, number_of_states> &A,
41 const Eigen::Matrix<double, number_of_states, number_of_inputs> &B,
42 const Eigen::Matrix<double, number_of_outputs, number_of_states> &C,
43 const Eigen::Matrix<double, number_of_outputs, number_of_inputs> &D,
44 const Eigen::Matrix<double, number_of_outputs, 1> &U_max,
45 const Eigen::Matrix<double, number_of_outputs, 1> &U_min)
46 : A(A),
47 B(B),
48 C(C),
49 D(D),
50 U_min(U_min),
51 U_max(U_max) {
Austin Schuhe3490622013-03-13 01:24:30 -070052 }
53
54 protected:
55 // these are accessible from non-templated subclasses
56 static const int kNumStates = number_of_states;
57 static const int kNumOutputs = number_of_outputs;
58 static const int kNumInputs = number_of_inputs;
59};
60
61template <int number_of_states, int number_of_inputs, int number_of_outputs>
62class StateFeedbackPlant {
63 public:
64 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
Brian Silverman0a151c92014-05-02 15:28:44 -070065
66 StateFeedbackPlant(
67 const ::std::vector<StateFeedbackPlantCoefficients<
68 number_of_states, number_of_inputs,
69 number_of_outputs> *> &coefficients)
70 : coefficients_(coefficients),
71 plant_index_(0) {
72 Reset();
73 }
74
75 StateFeedbackPlant(StateFeedbackPlant &&other)
76 : plant_index_(other.plant_index_) {
77 ::std::swap(coefficients_, other.coefficients_);
78 X.swap(other.X);
79 Y.swap(other.Y);
80 U.swap(other.U);
81 }
82
83 virtual ~StateFeedbackPlant() {
84 for (auto c : coefficients_) {
85 delete c;
86 }
87 }
88
Austin Schuhe3490622013-03-13 01:24:30 -070089 ::std::vector<StateFeedbackPlantCoefficients<
90 number_of_states, number_of_inputs, number_of_outputs> *> coefficients_;
91
92 const Eigen::Matrix<double, number_of_states, number_of_states> &A() const {
93 return coefficients().A;
94 }
95 double A(int i, int j) const { return A()(i, j); }
96 const Eigen::Matrix<double, number_of_states, number_of_inputs> &B() const {
97 return coefficients().B;
98 }
99 double B(int i, int j) const { return B()(i, j); }
100 const Eigen::Matrix<double, number_of_outputs, number_of_states> &C() const {
101 return coefficients().C;
102 }
103 double C(int i, int j) const { return C()(i, j); }
104 const Eigen::Matrix<double, number_of_outputs, number_of_inputs> &D() const {
105 return coefficients().D;
106 }
107 double D(int i, int j) const { return D()(i, j); }
108 const Eigen::Matrix<double, number_of_inputs, 1> &U_min() const {
109 return coefficients().U_min;
110 }
111 double U_min(int i, int j) const { return U_min()(i, j); }
112 const Eigen::Matrix<double, number_of_inputs, 1> &U_max() const {
113 return coefficients().U_max;
114 }
115 double U_max(int i, int j) const { return U_max()(i, j); }
116
117 const StateFeedbackPlantCoefficients<
118 number_of_states, number_of_inputs, number_of_outputs>
119 &coefficients() const {
120 return *coefficients_[plant_index_];
121 }
122
123 int plant_index() const { return plant_index_; }
124 void set_plant_index(int plant_index) {
125 if (plant_index < 0) {
126 plant_index_ = 0;
127 } else if (plant_index >= static_cast<int>(coefficients_.size())) {
Brian Silvermanb8cd6892013-03-17 23:36:24 -0700128 plant_index_ = static_cast<int>(coefficients_.size()) - 1;
Austin Schuhe3490622013-03-13 01:24:30 -0700129 } else {
130 plant_index_ = plant_index;
131 }
132 }
133
134 void Reset() {
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800135 X.setZero();
136 Y.setZero();
137 U.setZero();
138 }
139
Austin Schuhe3490622013-03-13 01:24:30 -0700140 Eigen::Matrix<double, number_of_states, 1> X;
141 Eigen::Matrix<double, number_of_outputs, 1> Y;
142 Eigen::Matrix<double, number_of_inputs, 1> U;
143
Austin Schuh849f0032013-03-03 23:59:53 -0800144 // Assert that U is within the hardware range.
145 virtual void CheckU() {
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800146 for (int i = 0; i < kNumOutputs; ++i) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800147 assert(U(i, 0) <= U_max(i, 0) + 0.00001);
148 assert(U(i, 0) >= U_min(i, 0) - 0.00001);
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800149 }
150 }
Austin Schuh849f0032013-03-03 23:59:53 -0800151
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800152 // Computes the new X and Y given the control input.
153 void Update() {
Austin Schuh849f0032013-03-03 23:59:53 -0800154 // Powers outside of the range are more likely controller bugs than things
155 // that the plant should deal with.
156 CheckU();
Austin Schuhe3490622013-03-13 01:24:30 -0700157 X = A() * X + B() * U;
158 Y = C() * X + D() * U;
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800159 }
160
161 protected:
162 // these are accessible from non-templated subclasses
Austin Schuhb1cdb382013-03-01 22:53:52 -0800163 static const int kNumStates = number_of_states;
164 static const int kNumOutputs = number_of_outputs;
165 static const int kNumInputs = number_of_inputs;
Austin Schuhe3490622013-03-13 01:24:30 -0700166
167 private:
168 int plant_index_;
Brian Silverman0a151c92014-05-02 15:28:44 -0700169
170 DISALLOW_COPY_AND_ASSIGN(StateFeedbackPlant);
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800171};
172
Austin Schuh9644e1c2013-03-12 00:40:36 -0700173// A Controller is a structure which holds a plant and the K and L matrices.
174// This is designed such that multiple controllers can share one set of state to
175// support gain scheduling easily.
176template <int number_of_states, int number_of_inputs, int number_of_outputs>
177struct StateFeedbackController {
178 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
179 const Eigen::Matrix<double, number_of_states, number_of_outputs> L;
180 const Eigen::Matrix<double, number_of_outputs, number_of_states> K;
Austin Schuhe3490622013-03-13 01:24:30 -0700181 StateFeedbackPlantCoefficients<number_of_states, number_of_inputs,
182 number_of_outputs> plant;
Austin Schuh9644e1c2013-03-12 00:40:36 -0700183
184 StateFeedbackController(
185 const Eigen::Matrix<double, number_of_states, number_of_outputs> &L,
186 const Eigen::Matrix<double, number_of_outputs, number_of_states> &K,
Austin Schuhe3490622013-03-13 01:24:30 -0700187 const StateFeedbackPlantCoefficients<number_of_states, number_of_inputs,
188 number_of_outputs> &plant)
Austin Schuh9644e1c2013-03-12 00:40:36 -0700189 : L(L),
190 K(K),
191 plant(plant) {
192 }
193};
194
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800195template <int number_of_states, int number_of_inputs, int number_of_outputs>
196class StateFeedbackLoop {
197 public:
198 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
199
Brian Silverman0a151c92014-05-02 15:28:44 -0700200 StateFeedbackLoop(const StateFeedbackController<
201 number_of_states, number_of_inputs, number_of_outputs> &controller)
202 : controller_index_(0) {
203 controllers_.push_back(new StateFeedbackController<
204 number_of_states, number_of_inputs, number_of_outputs>(controller));
205 Reset();
206 }
207
208 StateFeedbackLoop(const ::std::vector<StateFeedbackController<
209 number_of_states, number_of_inputs, number_of_outputs> *> &controllers)
210 : controllers_(controllers), controller_index_(0) {
211 Reset();
212 }
213
214 StateFeedbackLoop(StateFeedbackLoop &&other) {
215 X_hat.swap(other.X_hat);
216 R.swap(other.R);
217 U.swap(other.U);
218 U_uncapped.swap(other.U_uncapped);
219 U_ff.swap(other.U_ff);
220 ::std::swap(controllers_, other.controllers_);
221 Y_.swap(other.Y_);
222 new_y_ = other.new_y_;
223 controller_index_ = other.controller_index_;
224 }
225
226 StateFeedbackLoop(
227 const Eigen::Matrix<double, number_of_states, number_of_outputs> &L,
228 const Eigen::Matrix<double, number_of_outputs, number_of_states> &K,
229 const StateFeedbackPlantCoefficients<number_of_states, number_of_inputs,
230 number_of_outputs> &plant)
231 : controller_index_(0) {
232 controllers_.push_back(
233 new StateFeedbackController<number_of_states, number_of_inputs,
234 number_of_outputs>(L, K, plant));
235
236 Reset();
237 }
238
239 virtual ~StateFeedbackLoop() {
240 for (auto c : controllers_) {
241 delete c;
242 }
243 }
244
Austin Schuh9644e1c2013-03-12 00:40:36 -0700245 const Eigen::Matrix<double, number_of_states, number_of_states> &A() const {
246 return controller().plant.A;
247 }
248 double A(int i, int j) const { return A()(i, j); }
249 const Eigen::Matrix<double, number_of_states, number_of_inputs> &B() const {
250 return controller().plant.B;
251 }
252 double B(int i, int j) const { return B()(i, j); }
253 const Eigen::Matrix<double, number_of_outputs, number_of_states> &C() const {
254 return controller().plant.C;
255 }
256 double C(int i, int j) const { return C()(i, j); }
257 const Eigen::Matrix<double, number_of_outputs, number_of_inputs> &D() const {
258 return controller().plant.D;
259 }
260 double D(int i, int j) const { return D()(i, j); }
261 const Eigen::Matrix<double, number_of_outputs, number_of_states> &K() const {
262 return controller().K;
263 }
264 double K(int i, int j) const { return K()(i, j); }
265 const Eigen::Matrix<double, number_of_states, number_of_outputs> &L() const {
266 return controller().L;
267 }
268 double L(int i, int j) const { return L()(i, j); }
269 const Eigen::Matrix<double, number_of_inputs, 1> &U_min() const {
270 return controller().plant.U_min;
271 }
272 double U_min(int i, int j) const { return U_min()(i, j); }
273 const Eigen::Matrix<double, number_of_inputs, 1> &U_max() const {
274 return controller().plant.U_max;
275 }
276 double U_max(int i, int j) const { return U_max()(i, j); }
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800277
Brian Silverman2c590c32013-11-04 18:08:54 -0800278 const StateFeedbackController<number_of_states, number_of_inputs,
279 number_of_outputs> &controller() const {
Austin Schuhe3490622013-03-13 01:24:30 -0700280 return *controllers_[controller_index_];
Austin Schuh9644e1c2013-03-12 00:40:36 -0700281 }
282
Brian Silverman2c590c32013-11-04 18:08:54 -0800283 const StateFeedbackController<number_of_states, number_of_inputs,
284 number_of_outputs> &controller(
285 int index) const {
Austin Schuh2054f5f2013-10-27 14:54:10 -0700286 return *controllers_[index];
287 }
288
Austin Schuh9644e1c2013-03-12 00:40:36 -0700289 void Reset() {
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800290 X_hat.setZero();
291 R.setZero();
292 U.setZero();
Austin Schuh06ee48e2013-03-02 01:47:54 -0800293 U_uncapped.setZero();
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800294 U_ff.setZero();
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800295 }
Austin Schuh9644e1c2013-03-12 00:40:36 -0700296
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800297 virtual void FeedForward() {
298 for (int i = 0; i < number_of_outputs; ++i) {
299 U_ff[i] = 0.0;
300 }
301 }
302
303 // If U is outside the hardware range, limit it before the plant tries to use
304 // it.
305 virtual void CapU() {
306 for (int i = 0; i < kNumOutputs; ++i) {
Austin Schuh9644e1c2013-03-12 00:40:36 -0700307 if (U(i, 0) > U_max(i, 0)) {
308 U(i, 0) = U_max(i, 0);
309 } else if (U(i, 0) < U_min(i, 0)) {
310 U(i, 0) = U_min(i, 0);
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800311 }
312 }
313 }
314
Austin Schuhf9286cd2014-02-11 00:51:09 -0800315 // Corrects X_hat given the observation in Y.
316 void Correct(const Eigen::Matrix<double, number_of_outputs, 1> &Y) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800317 /*
318 auto eye =
319 Eigen::Matrix<double, number_of_states, number_of_states>::Identity();
320 //LOG(DEBUG, "X_hat(2, 0) = %f\n", X_hat(2, 0));
321 ::std::cout << "Identity " << eye << ::std::endl;
322 ::std::cout << "X_hat " << X_hat << ::std::endl;
323 ::std::cout << "LC " << L() * C() << ::std::endl;
324 ::std::cout << "L " << L() << ::std::endl;
325 ::std::cout << "C " << C() << ::std::endl;
326 ::std::cout << "y " << Y << ::std::endl;
327 ::std::cout << "z " << (Y - C() * X_hat) << ::std::endl;
328 ::std::cout << "correction " << L() * (Y - C() * X_hat) << ::std::endl;
329 X_hat = (eye - L() * C()) * X_hat + L() * Y;
330 ::std::cout << "X_hat after " << X_hat << ::std::endl;
331 ::std::cout << ::std::endl;
332 */
333 //LOG(DEBUG, "X_hat(2, 0) = %f\n", X_hat(2, 0));
Austin Schuhf9286cd2014-02-11 00:51:09 -0800334 Y_ = Y;
335 new_y_ = true;
336 }
337
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800338 // stop_motors is whether or not to output all 0s.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800339 void Update(bool stop_motors) {
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800340 if (stop_motors) {
Austin Schuh9644e1c2013-03-12 00:40:36 -0700341 U.setZero();
Austin Schuh30537882014-02-18 01:07:23 -0800342 U_uncapped.setZero();
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800343 } else {
Austin Schuh9644e1c2013-03-12 00:40:36 -0700344 U = U_uncapped = K() * (R - X_hat);
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800345 CapU();
346 }
347
Austin Schuhcda86af2014-02-16 16:16:39 -0800348 //::std::cout << "Predict xhat before " << X_hat;
349 //::std::cout << "Measurement error is " << Y_ - C() * X_hat;
350 //X_hat = A() * X_hat + B() * U;
Ben Fredrickson890c3fe2014-03-02 00:15:16 +0000351 //::std::cout << "Predict xhat after " << X_hat;
352 UpdateObserver();
353 }
354
355 void UpdateObserver() {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800356 if (new_y_) {
357 X_hat = (A() - L() * C()) * X_hat + L() * Y_ + B() * U;
358 new_y_ = false;
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800359 } else {
Austin Schuh9644e1c2013-03-12 00:40:36 -0700360 X_hat = A() * X_hat + B() * U;
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800361 }
362 }
363
Austin Schuh9644e1c2013-03-12 00:40:36 -0700364 // Sets the current controller to be index and verifies that it isn't out of
365 // range.
366 void set_controller_index(int index) {
Austin Schuhe3490622013-03-13 01:24:30 -0700367 if (index < 0) {
368 controller_index_ = 0;
369 } else if (index >= static_cast<int>(controllers_.size())) {
Brian Silvermanb8cd6892013-03-17 23:36:24 -0700370 controller_index_ = static_cast<int>(controllers_.size()) - 1;
Austin Schuhe3490622013-03-13 01:24:30 -0700371 } else {
Austin Schuh9644e1c2013-03-12 00:40:36 -0700372 controller_index_ = index;
373 }
374 }
375
Austin Schuhd34569d2014-02-18 20:26:38 -0800376 int controller_index() const { return controller_index_; }
Austin Schuh9644e1c2013-03-12 00:40:36 -0700377
Brian Silverman0a151c92014-05-02 15:28:44 -0700378 Eigen::Matrix<double, number_of_states, 1> X_hat;
379 Eigen::Matrix<double, number_of_states, 1> R;
380 Eigen::Matrix<double, number_of_inputs, 1> U;
381 Eigen::Matrix<double, number_of_inputs, 1> U_uncapped;
382 Eigen::Matrix<double, number_of_outputs, 1> U_ff;
383
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800384 protected:
Austin Schuh2054f5f2013-10-27 14:54:10 -0700385 ::std::vector<StateFeedbackController<number_of_states, number_of_inputs,
386 number_of_outputs> *> controllers_;
387
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800388 // these are accessible from non-templated subclasses
Austin Schuhb1cdb382013-03-01 22:53:52 -0800389 static const int kNumStates = number_of_states;
390 static const int kNumOutputs = number_of_outputs;
391 static const int kNumInputs = number_of_inputs;
Austin Schuh9644e1c2013-03-12 00:40:36 -0700392
Austin Schuhf9286cd2014-02-11 00:51:09 -0800393 // Temporary storage for a measurement until I can figure out why I can't
394 // correct when the measurement is made.
395 Eigen::Matrix<double, number_of_outputs, 1> Y_;
396 bool new_y_ = false;
397
Austin Schuh9644e1c2013-03-12 00:40:36 -0700398 int controller_index_;
Brian Silverman0a151c92014-05-02 15:28:44 -0700399
400 private:
401 DISALLOW_COPY_AND_ASSIGN(StateFeedbackLoop);
Austin Schuhdc1c84a2013-02-23 16:33:10 -0800402};
403
404#endif // FRC971_CONTROL_LOOPS_STATEFEEDBACKLOOP_H_