blob: 811c875e50735380452e35d1265fa0af7cb988a2 [file] [log] [blame]
#ifndef FRC971_CONTROL_LOOPS_STATEFEEDBACKLOOP_H_
#define FRC971_CONTROL_LOOPS_STATEFEEDBACKLOOP_H_
#include <assert.h>
#include <vector>
// Stupid vxworks system headers define it which blows up Eigen...
#undef m_data
#include "Eigen/Dense"
template <int number_of_states, int number_of_inputs, int number_of_outputs>
class StateFeedbackPlantCoefficients {
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
const Eigen::Matrix<double, number_of_states, number_of_states> A;
const Eigen::Matrix<double, number_of_states, number_of_inputs> B;
const Eigen::Matrix<double, number_of_outputs, number_of_states> C;
const Eigen::Matrix<double, number_of_outputs, number_of_inputs> D;
const Eigen::Matrix<double, number_of_inputs, 1> U_min;
const Eigen::Matrix<double, number_of_inputs, 1> U_max;
StateFeedbackPlantCoefficients(const StateFeedbackPlantCoefficients &other)
: A(other.A),
B(other.B),
C(other.C),
D(other.D),
U_min(other.U_min),
U_max(other.U_max) {
}
StateFeedbackPlantCoefficients(
const Eigen::Matrix<double, number_of_states, number_of_states> &A,
const Eigen::Matrix<double, number_of_states, number_of_inputs> &B,
const Eigen::Matrix<double, number_of_outputs, number_of_states> &C,
const Eigen::Matrix<double, number_of_outputs, number_of_inputs> &D,
const Eigen::Matrix<double, number_of_outputs, 1> &U_max,
const Eigen::Matrix<double, number_of_outputs, 1> &U_min)
: A(A),
B(B),
C(C),
D(D),
U_min(U_min),
U_max(U_max) {
}
protected:
// these are accessible from non-templated subclasses
static const int kNumStates = number_of_states;
static const int kNumOutputs = number_of_outputs;
static const int kNumInputs = number_of_inputs;
};
template <int number_of_states, int number_of_inputs, int number_of_outputs>
class StateFeedbackPlant {
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
::std::vector<StateFeedbackPlantCoefficients<
number_of_states, number_of_inputs, number_of_outputs> *> coefficients_;
const Eigen::Matrix<double, number_of_states, number_of_states> &A() const {
return coefficients().A;
}
double A(int i, int j) const { return A()(i, j); }
const Eigen::Matrix<double, number_of_states, number_of_inputs> &B() const {
return coefficients().B;
}
double B(int i, int j) const { return B()(i, j); }
const Eigen::Matrix<double, number_of_outputs, number_of_states> &C() const {
return coefficients().C;
}
double C(int i, int j) const { return C()(i, j); }
const Eigen::Matrix<double, number_of_outputs, number_of_inputs> &D() const {
return coefficients().D;
}
double D(int i, int j) const { return D()(i, j); }
const Eigen::Matrix<double, number_of_inputs, 1> &U_min() const {
return coefficients().U_min;
}
double U_min(int i, int j) const { return U_min()(i, j); }
const Eigen::Matrix<double, number_of_inputs, 1> &U_max() const {
return coefficients().U_max;
}
double U_max(int i, int j) const { return U_max()(i, j); }
const StateFeedbackPlantCoefficients<
number_of_states, number_of_inputs, number_of_outputs>
&coefficients() const {
return *coefficients_[plant_index_];
}
int plant_index() const { return plant_index_; }
void set_plant_index(int plant_index) {
if (plant_index < 0) {
plant_index_ = 0;
} else if (plant_index >= static_cast<int>(coefficients_.size())) {
plant_index_ = static_cast<int>(coefficients_.size());
} else {
plant_index_ = plant_index;
}
}
void Reset() {
X.setZero();
Y.setZero();
U.setZero();
}
Eigen::Matrix<double, number_of_states, 1> X;
Eigen::Matrix<double, number_of_outputs, 1> Y;
Eigen::Matrix<double, number_of_inputs, 1> U;
StateFeedbackPlant(
const ::std::vector<StateFeedbackPlantCoefficients<
number_of_states, number_of_inputs,
number_of_outputs> *> &coefficients)
: coefficients_(coefficients),
plant_index_(0) {
Reset();
}
StateFeedbackPlant(StateFeedbackPlant &&other)
: plant_index_(0) {
Reset();
::std::swap(coefficients_, other.coefficients_);
}
virtual ~StateFeedbackPlant() {}
// Assert that U is within the hardware range.
virtual void CheckU() {
for (int i = 0; i < kNumOutputs; ++i) {
assert(U(i, 0) <= U_max(i, 0));
assert(U(i, 0) >= U_min(i, 0));
}
}
// Computes the new X and Y given the control input.
void Update() {
// Powers outside of the range are more likely controller bugs than things
// that the plant should deal with.
CheckU();
X = A() * X + B() * U;
Y = C() * X + D() * U;
}
protected:
// these are accessible from non-templated subclasses
static const int kNumStates = number_of_states;
static const int kNumOutputs = number_of_outputs;
static const int kNumInputs = number_of_inputs;
private:
int plant_index_;
};
// A Controller is a structure which holds a plant and the K and L matrices.
// This is designed such that multiple controllers can share one set of state to
// support gain scheduling easily.
template <int number_of_states, int number_of_inputs, int number_of_outputs>
struct StateFeedbackController {
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
const Eigen::Matrix<double, number_of_states, number_of_outputs> L;
const Eigen::Matrix<double, number_of_outputs, number_of_states> K;
StateFeedbackPlantCoefficients<number_of_states, number_of_inputs,
number_of_outputs> plant;
StateFeedbackController(
const Eigen::Matrix<double, number_of_states, number_of_outputs> &L,
const Eigen::Matrix<double, number_of_outputs, number_of_states> &K,
const StateFeedbackPlantCoefficients<number_of_states, number_of_inputs,
number_of_outputs> &plant)
: L(L),
K(K),
plant(plant) {
}
};
template <int number_of_states, int number_of_inputs, int number_of_outputs>
class StateFeedbackLoop {
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
const Eigen::Matrix<double, number_of_states, number_of_states> &A() const {
return controller().plant.A;
}
double A(int i, int j) const { return A()(i, j); }
const Eigen::Matrix<double, number_of_states, number_of_inputs> &B() const {
return controller().plant.B;
}
double B(int i, int j) const { return B()(i, j); }
const Eigen::Matrix<double, number_of_outputs, number_of_states> &C() const {
return controller().plant.C;
}
double C(int i, int j) const { return C()(i, j); }
const Eigen::Matrix<double, number_of_outputs, number_of_inputs> &D() const {
return controller().plant.D;
}
double D(int i, int j) const { return D()(i, j); }
const Eigen::Matrix<double, number_of_outputs, number_of_states> &K() const {
return controller().K;
}
double K(int i, int j) const { return K()(i, j); }
const Eigen::Matrix<double, number_of_states, number_of_outputs> &L() const {
return controller().L;
}
double L(int i, int j) const { return L()(i, j); }
const Eigen::Matrix<double, number_of_inputs, 1> &U_min() const {
return controller().plant.U_min;
}
double U_min(int i, int j) const { return U_min()(i, j); }
const Eigen::Matrix<double, number_of_inputs, 1> &U_max() const {
return controller().plant.U_max;
}
double U_max(int i, int j) const { return U_max()(i, j); }
Eigen::Matrix<double, number_of_states, 1> X_hat;
Eigen::Matrix<double, number_of_states, 1> R;
Eigen::Matrix<double, number_of_inputs, 1> U;
Eigen::Matrix<double, number_of_inputs, 1> U_uncapped;
Eigen::Matrix<double, number_of_outputs, 1> U_ff;
Eigen::Matrix<double, number_of_outputs, 1> Y;
::std::vector<StateFeedbackController<number_of_states, number_of_inputs,
number_of_outputs> *> controllers_;
const StateFeedbackController<
number_of_states, number_of_inputs, number_of_outputs>
&controller() const {
return *controllers_[controller_index_];
}
void Reset() {
X_hat.setZero();
R.setZero();
U.setZero();
U_uncapped.setZero();
U_ff.setZero();
Y.setZero();
}
StateFeedbackLoop(
const StateFeedbackController<number_of_states, number_of_inputs,
number_of_outputs> &controller)
: controller_index_(0) {
controllers_.push_back(
new StateFeedbackController<number_of_states, number_of_inputs,
number_of_outputs>(controller));
Reset();
}
StateFeedbackLoop(
const ::std::vector<StateFeedbackController<
number_of_states, number_of_inputs,
number_of_outputs> *> &controllers)
: controllers_(controllers),
controller_index_(0) {
Reset();
}
StateFeedbackLoop(
const Eigen::Matrix<double, number_of_states, number_of_outputs> &L,
const Eigen::Matrix<double, number_of_outputs, number_of_states> &K,
const StateFeedbackPlantCoefficients<number_of_states, number_of_inputs,
number_of_outputs> &plant)
: controller_index_(0) {
controllers_.push_back(
new StateFeedbackController<number_of_states, number_of_inputs,
number_of_outputs>(L, K, plant));
Reset();
}
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 the plant tries to use
// it.
virtual void CapU() {
for (int i = 0; i < kNumOutputs; ++i) {
if (U(i, 0) > U_max(i, 0)) {
U(i, 0) = U_max(i, 0);
} else if (U(i, 0) < U_min(i, 0)) {
U(i, 0) = U_min(i, 0);
}
}
}
// 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) {
U.setZero();
} else {
U = U_uncapped = 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;
}
}
// Sets the current controller to be index and verifies that it isn't out of
// range.
void set_controller_index(int index) {
if (index < 0) {
controller_index_ = 0;
} else if (index >= static_cast<int>(controllers_.size())) {
controller_index_ = static_cast<int>(controllers_.size());
} else {
controller_index_ = index;
}
}
void controller_index() const { return controller_index_; }
protected:
// these are accessible from non-templated subclasses
static const int kNumStates = number_of_states;
static const int kNumOutputs = number_of_outputs;
static const int kNumInputs = number_of_inputs;
int controller_index_;
};
#endif // FRC971_CONTROL_LOOPS_STATEFEEDBACKLOOP_H_