/*----------------------------------------------------------------------------*/ /* 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 #include #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 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. */ template class LinearPlantInversionFeedforward { public: /** * Constructs a feedforward with the given plant. * * @param plant The plant being controlled. * @param dtSeconds 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 dtSeconds Discretization timestep. */ LinearPlantInversionFeedforward( const Eigen::Matrix& A, const Eigen::Matrix& B, units::second_t dt) : m_dt(dt) { DiscretizeAB(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& 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& 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& 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&). * 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 Calculate( const Eigen::Matrix& 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 Calculate( const Eigen::Matrix& r, const Eigen::Matrix& nextR) { m_uff = m_B.householderQr().solve(nextR - (m_A * r)); m_r = nextR; return m_uff; } private: Eigen::Matrix m_A; Eigen::Matrix m_B; units::second_t m_dt; // Current reference Eigen::Matrix m_r; // Computed feedforward Eigen::Matrix m_uff; }; } // namespace frc