2020-12-26 14:12:05 -08:00
|
|
|
|
// 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.
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
|
|
#include <functional>
|
|
|
|
|
|
|
2023-08-28 15:13:34 -07:00
|
|
|
|
#include <Eigen/QR>
|
|
|
|
|
|
|
2022-04-29 22:29:20 -07:00
|
|
|
|
#include "frc/EigenCore.h"
|
2020-08-14 23:40:33 -07:00
|
|
|
|
#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.
|
2021-10-29 15:07:05 -07:00
|
|
|
|
*
|
|
|
|
|
|
* @tparam States The number of states.
|
|
|
|
|
|
* @tparam Inputs The number of inputs.
|
2020-08-14 23:40:33 -07:00
|
|
|
|
*/
|
|
|
|
|
|
template <int States, int Inputs>
|
|
|
|
|
|
class LinearPlantInversionFeedforward {
|
|
|
|
|
|
public:
|
2022-04-29 22:29:20 -07:00
|
|
|
|
using StateVector = Vectord<States>;
|
|
|
|
|
|
using InputVector = Vectord<Inputs>;
|
|
|
|
|
|
|
2020-08-14 23:40:33 -07:00
|
|
|
|
/**
|
|
|
|
|
|
* Constructs a feedforward with the given plant.
|
|
|
|
|
|
*
|
2021-10-29 15:07:05 -07:00
|
|
|
|
* @tparam Outputs The number of outputs.
|
2021-10-14 18:09:38 -07:00
|
|
|
|
* @param plant The plant being controlled.
|
|
|
|
|
|
* @param dt Discretization timestep.
|
2020-08-14 23:40:33 -07:00
|
|
|
|
*/
|
|
|
|
|
|
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.
|
|
|
|
|
|
*
|
2021-10-14 18:09:38 -07:00
|
|
|
|
* @param A Continuous system matrix of the plant being controlled.
|
|
|
|
|
|
* @param B Continuous input matrix of the plant being controlled.
|
|
|
|
|
|
* @param dt Discretization timestep.
|
2020-08-14 23:40:33 -07:00
|
|
|
|
*/
|
2022-04-29 22:29:20 -07:00
|
|
|
|
LinearPlantInversionFeedforward(const Matrixd<States, States>& A,
|
|
|
|
|
|
const Matrixd<States, Inputs>& B,
|
|
|
|
|
|
units::second_t dt)
|
2020-08-14 23:40:33 -07:00
|
|
|
|
: m_dt(dt) {
|
|
|
|
|
|
DiscretizeAB<States, Inputs>(A, B, dt, &m_A, &m_B);
|
2021-06-09 23:59:31 -07:00
|
|
|
|
Reset();
|
2020-08-14 23:40:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Returns the previously calculated feedforward as an input vector.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return The calculated feedforward.
|
|
|
|
|
|
*/
|
2022-04-29 22:29:20 -07:00
|
|
|
|
const InputVector& Uff() const { return m_uff; }
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Returns an element of the previously calculated feedforward.
|
|
|
|
|
|
*
|
2021-10-14 18:09:38 -07:00
|
|
|
|
* @param i Row of uff.
|
2020-08-14 23:40:33 -07:00
|
|
|
|
*
|
|
|
|
|
|
* @return The row of the calculated feedforward.
|
|
|
|
|
|
*/
|
2022-03-19 20:44:14 -07:00
|
|
|
|
double Uff(int i) const { return m_uff(i); }
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Returns the current reference vector r.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return The current reference vector.
|
|
|
|
|
|
*/
|
2022-04-29 22:29:20 -07:00
|
|
|
|
const StateVector& R() const { return m_r; }
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Returns an element of the reference vector r.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param i Row of r.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return The row of the current reference vector.
|
|
|
|
|
|
*/
|
2022-03-19 20:44:14 -07:00
|
|
|
|
double R(int i) const { return m_r(i); }
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Resets the feedforward with a specified initial state vector.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param initialState The initial state vector.
|
|
|
|
|
|
*/
|
2022-04-29 22:29:20 -07:00
|
|
|
|
void Reset(const StateVector& initialState) {
|
2020-08-14 23:40:33 -07:00
|
|
|
|
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.
|
|
|
|
|
|
*
|
2021-08-19 00:23:48 -07:00
|
|
|
|
* If this method is used the initial state of the system is the one set using
|
2022-04-29 22:29:20 -07:00
|
|
|
|
* Reset(const StateVector&). If the initial state is not
|
2021-08-19 00:23:48 -07:00
|
|
|
|
* set it defaults to a zero vector.
|
2020-08-14 23:40:33 -07:00
|
|
|
|
*
|
|
|
|
|
|
* @param nextR The reference state of the future timestep (k + dt).
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return The calculated feedforward.
|
|
|
|
|
|
*/
|
2022-04-29 22:29:20 -07:00
|
|
|
|
InputVector Calculate(const StateVector& nextR) {
|
2022-04-08 21:31:42 -07:00
|
|
|
|
return Calculate(m_r, nextR);
|
2020-08-14 23:40:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
*/
|
2022-04-29 22:29:20 -07:00
|
|
|
|
InputVector Calculate(const StateVector& r, const StateVector& nextR) {
|
2023-09-08 20:14:24 -07:00
|
|
|
|
// rₖ₊₁ = Arₖ + Buₖ
|
|
|
|
|
|
// Buₖ = rₖ₊₁ − Arₖ
|
|
|
|
|
|
// uₖ = B⁺(rₖ₊₁ − Arₖ)
|
2020-08-14 23:40:33 -07:00
|
|
|
|
m_uff = m_B.householderQr().solve(nextR - (m_A * r));
|
|
|
|
|
|
m_r = nextR;
|
|
|
|
|
|
return m_uff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
2022-04-29 22:29:20 -07:00
|
|
|
|
Matrixd<States, States> m_A;
|
|
|
|
|
|
Matrixd<States, Inputs> m_B;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
|
|
units::second_t m_dt;
|
|
|
|
|
|
|
|
|
|
|
|
// Current reference
|
2022-04-29 22:29:20 -07:00
|
|
|
|
StateVector m_r;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
|
|
// Computed feedforward
|
2022-04-29 22:29:20 -07:00
|
|
|
|
InputVector m_uff;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace frc
|