Files
allwpilib/wpimath/src/main/native/include/frc/controller/LinearPlantInversionFeedforward.h
Tyler Veness 4be809499d [wpimath] Use more specific Eigen includes in headers (#2693)
This helps reduce compilation overhead. I tried slimming down includes
of <Eigen/QR>, but the householderQr() function we use from there
requires including dependency headers from Eigen that don't fit with
lexographic ordering. It didn't seem worth the effort to work around.

This won't affect user code at all since all the Eigen feature usage
here is internal only; users generally only need <Eigen/Core>.
2020-09-04 20:52:03 -07:00

164 lines
4.7 KiB
C++

/*----------------------------------------------------------------------------*/
/* Copyright (c) 2020 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#pragma once
#include <array>
#include <functional>
#include "Eigen/Core"
#include "Eigen/QR"
#include "frc/system/Discretization.h"
#include "frc/system/LinearSystem.h"
#include "units/time.h"
namespace frc {
/**
* Constructs a plant inversion model-based feedforward from a LinearSystem.
*
* The feedforward is calculated as <strong> u_ff = B<sup>+</sup> (r_k+1 - A
* r_k) </strong>, where <strong> B<sup>+</sup> </strong> is the pseudoinverse
* of B.
*
* For more on the underlying math, read
* https://file.tavsys.net/control/controls-engineering-in-frc.pdf.
*/
template <int States, int Inputs>
class LinearPlantInversionFeedforward {
public:
/**
* Constructs a feedforward with the given plant.
*
* @param plant The plant being controlled.
* @param dtSeconds Discretization timestep.
*/
template <int Outputs>
LinearPlantInversionFeedforward(
const LinearSystem<States, Inputs, Outputs>& plant, units::second_t dt)
: LinearPlantInversionFeedforward(plant.A(), plant.B(), dt) {}
/**
* Constructs a feedforward with the given coefficients.
*
* @param A Continuous system matrix of the plant being controlled.
* @param B Continuous input matrix of the plant being controlled.
* @param dtSeconds Discretization timestep.
*/
LinearPlantInversionFeedforward(
const Eigen::Matrix<double, States, States>& A,
const Eigen::Matrix<double, States, Inputs>& B, units::second_t dt)
: m_dt(dt) {
DiscretizeAB<States, Inputs>(A, B, dt, &m_A, &m_B);
m_r.setZero();
Reset(m_r);
}
LinearPlantInversionFeedforward(LinearPlantInversionFeedforward&&) = default;
LinearPlantInversionFeedforward& operator=(
LinearPlantInversionFeedforward&&) = default;
/**
* Returns the previously calculated feedforward as an input vector.
*
* @return The calculated feedforward.
*/
const Eigen::Matrix<double, Inputs, 1>& Uff() const { return m_uff; }
/**
* Returns an element of the previously calculated feedforward.
*
* @param row Row of uff.
*
* @return The row of the calculated feedforward.
*/
double Uff(int i) const { return m_uff(i, 0); }
/**
* Returns the current reference vector r.
*
* @return The current reference vector.
*/
const Eigen::Matrix<double, States, 1>& R() const { return m_r; }
/**
* Returns an element of the reference vector r.
*
* @param i Row of r.
*
* @return The row of the current reference vector.
*/
double R(int i) const { return m_r(i, 0); }
/**
* Resets the feedforward with a specified initial state vector.
*
* @param initialState The initial state vector.
*/
void Reset(const Eigen::Matrix<double, States, 1>& initialState) {
m_r = initialState;
m_uff.setZero();
}
/**
* Resets the feedforward with a zero initial state vector.
*/
void Reset() {
m_r.setZero();
m_uff.setZero();
}
/**
* Calculate the feedforward with only the desired
* future reference. This uses the internally stored "current"
* reference.
*
* If this method is used the initial state of the system is the one
* set using Reset(const Eigen::Matrix<double, States, 1>&).
* If the initial state is not set it defaults to a zero vector.
*
* @param nextR The reference state of the future timestep (k + dt).
*
* @return The calculated feedforward.
*/
Eigen::Matrix<double, Inputs, 1> Calculate(
const Eigen::Matrix<double, States, 1>& nextR) {
return Calculate(m_r, nextR);
}
/**
* Calculate the feedforward with current and future reference vectors.
*
* @param r The reference state of the current timestep (k).
* @param nextR The reference state of the future timestep (k + dt).
*
* @return The calculated feedforward.
*/
Eigen::Matrix<double, Inputs, 1> Calculate(
const Eigen::Matrix<double, States, 1>& r,
const Eigen::Matrix<double, States, 1>& nextR) {
m_uff = m_B.householderQr().solve(nextR - (m_A * r));
m_r = nextR;
return m_uff;
}
private:
Eigen::Matrix<double, States, States> m_A;
Eigen::Matrix<double, States, Inputs> m_B;
units::second_t m_dt;
// Current reference
Eigen::Matrix<double, States, 1> m_r;
// Computed feedforward
Eigen::Matrix<double, Inputs, 1> m_uff;
};
} // namespace frc