// Copyright (c) FIRST and other WPILib contributors. // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. #pragma once #include #include #include #include "frc/EigenCore.h" #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 u_ff = B+ (r_k+1 - A * r_k) , where B+ is the pseudoinverse * of B. * * For more on the underlying math, read * https://file.tavsys.net/control/controls-engineering-in-frc.pdf. * * @tparam States The number of states. * @tparam Inputs The number of inputs. */ template class LinearPlantInversionFeedforward { public: using StateVector = Vectord; using InputVector = Vectord; /** * Constructs a feedforward with the given plant. * * @tparam Outputs The number of outputs. * @param plant The plant being controlled. * @param dt Discretization timestep. */ template LinearPlantInversionFeedforward( const LinearSystem& 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 dt Discretization timestep. */ LinearPlantInversionFeedforward(const Matrixd& A, const Matrixd& B, units::second_t dt) : m_dt(dt) { DiscretizeAB(A, B, dt, &m_A, &m_B); Reset(); } /** * Returns the previously calculated feedforward as an input vector. * * @return The calculated feedforward. */ const InputVector& Uff() const { return m_uff; } /** * Returns an element of the previously calculated feedforward. * * @param i Row of uff. * * @return The row of the calculated feedforward. */ double Uff(int i) const { return m_uff(i); } /** * Returns the current reference vector r. * * @return The current reference vector. */ const StateVector& 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); } /** * Resets the feedforward with a specified initial state vector. * * @param initialState The initial state vector. */ void Reset(const StateVector& 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 StateVector&). 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. */ InputVector Calculate(const StateVector& 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. */ InputVector Calculate(const StateVector& r, const StateVector& nextR) { // rₖ₊₁ = Arₖ + Buₖ // Buₖ = rₖ₊₁ − Arₖ // uₖ = B⁺(rₖ₊₁ − Arₖ) m_uff = m_B.householderQr().solve(nextR - (m_A * r)); m_r = nextR; return m_uff; } private: Matrixd m_A; Matrixd m_B; units::second_t m_dt; // Current reference StateVector m_r; // Computed feedforward InputVector m_uff; }; } // namespace frc