blob: ad37f4932693db24173bbd4a1d22327e8a061d01 [file] [log] [blame]
#ifndef FRC971_CONTROL_LOOPS_STATEFEEDBACKLOOP_H_
#define FRC971_CONTROL_LOOPS_STATEFEEDBACKLOOP_H_
// wikipedia article is <http://en.wikipedia.org/wiki/State_observer>
#include "Eigen/Dense"
template <int number_of_states, int number_of_outputs>
class StateFeedbackPlant {
public:
Eigen::Matrix<double, number_of_states, 1> X;
Eigen::Matrix<double, number_of_outputs, 1> Y;
Eigen::Matrix<double, number_of_outputs, 1> U;
Eigen::Matrix<double, number_of_outputs, 1> U_max;
Eigen::Matrix<double, number_of_outputs, 1> U_min;
Eigen::Matrix<double, number_of_states, number_of_states> A;
Eigen::Matrix<double, number_of_states, number_of_outputs> B;
Eigen::Matrix<double, number_of_outputs, number_of_states> C;
Eigen::Matrix<double, number_of_outputs, number_of_outputs> D;
// TODO(aschuh): These following 2 lines are here because MATRIX_INIT
// assumes that you have a controller as well as a plant.
Eigen::Matrix<double, number_of_states, number_of_outputs> L;
Eigen::Matrix<double, number_of_outputs, number_of_states> K;
StateFeedbackPlant() {
X.setZero();
Y.setZero();
U.setZero();
}
virtual ~StateFeedbackPlant() {}
// If U is outside the hardware range, limit it before the plant tries to use
// it.
virtual void CapU() {
for (int i = 0; i < number_of_outputs; ++i) {
if (U[i] > U_max[i]) {
U[i] = U_max[i];
} else if (U[i] < U_min[i]) {
U[i] = U_min[i];
}
}
}
// Computes the new X and Y given the control input.
void Update() {
CapU();
X = A * X + B * U;
Y = C * X + D * U;
}
protected:
// these are accessible from non-templated subclasses
static const int number_of_states_var = number_of_states;
static const int number_of_outputs_var = number_of_outputs;
};
template <int number_of_states, int number_of_outputs>
class StateFeedbackLoop {
public:
Eigen::Matrix<double, number_of_states, 1> X;
Eigen::Matrix<double, number_of_states, 1> X_hat;
Eigen::Matrix<double, number_of_outputs, 1> Y;
Eigen::Matrix<double, number_of_states, 1> R;
Eigen::Matrix<double, number_of_outputs, 1> U;
Eigen::Matrix<double, number_of_outputs, 1> U_max;
Eigen::Matrix<double, number_of_outputs, 1> U_min;
Eigen::Matrix<double, number_of_outputs, 1> U_ff;
Eigen::Matrix<double, number_of_states, number_of_states> A;
Eigen::Matrix<double, number_of_states, number_of_outputs> B;
// K in wikipedia article
Eigen::Matrix<double, number_of_outputs, number_of_states> C;
Eigen::Matrix<double, number_of_outputs, number_of_outputs> D;
// B in wikipedia article
Eigen::Matrix<double, number_of_states, number_of_outputs> L;
// C in wikipedia article
Eigen::Matrix<double, number_of_outputs, number_of_states> K;
StateFeedbackLoop() {
// You have to initialize all the matrices to 0 or else all their elements
// are undefined.
X.setZero();
X_hat.setZero();
Y.setZero();
R.setZero();
U.setZero();
U_ff.setZero();
}
virtual ~StateFeedbackLoop() {}
virtual void FeedForward() {
for (int i = 0; i < number_of_outputs; ++i) {
U_ff[i] = 0.0;
}
}
// If U is outside the hardware range, limit it before it
// gets added to the observer.
virtual void CapU() {
for (int i = 0; i < number_of_outputs; ++i) {
if (U[i] > U_max[i]) {
U[i] = U_max[i];
} else if (U[i] < U_min[i]) {
U[i] = U_min[i];
}
}
}
// update_observer is whether or not to use the values in Y.
// stop_motors is whether or not to output all 0s.
void Update(bool update_observer, bool stop_motors) {
if (stop_motors) {
for (int i = 0; i < number_of_outputs; ++i) {
U[i] = 0.0;
}
} else {
// new power = constant * (goal - current prediction)
U.noalias() = K * (R - X_hat);
CapU();
}
if (update_observer) {
X_hat = (A - L * C) * X_hat + L * Y + B * U;
} else {
X_hat = A * X_hat + B * U;
}
}
protected:
// these are accessible from non-templated subclasses
static const int number_of_states_var = number_of_states;
static const int number_of_outputs_var = number_of_outputs;
};
#endif // FRC971_CONTROL_LOOPS_STATEFEEDBACKLOOP_H_