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
|
|
|
|
|
|
2021-08-20 09:02:01 -07:00
|
|
|
#include <wpi/SymbolExports.h>
|
2021-01-16 20:26:17 -08:00
|
|
|
#include <wpi/array.h>
|
2020-08-14 23:40:33 -07:00
|
|
|
|
2022-04-29 22:29:20 -07:00
|
|
|
#include "frc/EigenCore.h"
|
2020-08-14 23:40:33 -07:00
|
|
|
#include "frc/system/LinearSystem.h"
|
|
|
|
|
#include "units/time.h"
|
|
|
|
|
|
|
|
|
|
namespace frc {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Contains the controller coefficients and logic for a linear-quadratic
|
|
|
|
|
* regulator (LQR).
|
|
|
|
|
* LQRs use the control law u = K(r - x).
|
|
|
|
|
*
|
|
|
|
|
* 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 Number of states.
|
|
|
|
|
* @tparam Inputs Number of inputs.
|
2020-08-14 23:40:33 -07:00
|
|
|
*/
|
|
|
|
|
template <int States, int Inputs>
|
2022-04-29 23:29:17 -07:00
|
|
|
class LinearQuadraticRegulator {
|
2020-08-14 23:40:33 -07:00
|
|
|
public:
|
2022-04-29 22:29:20 -07:00
|
|
|
using StateVector = Vectord<States>;
|
|
|
|
|
using InputVector = Vectord<Inputs>;
|
|
|
|
|
|
|
|
|
|
using StateArray = wpi::array<double, States>;
|
|
|
|
|
using InputArray = wpi::array<double, Inputs>;
|
|
|
|
|
|
2020-08-14 23:40:33 -07:00
|
|
|
/**
|
|
|
|
|
* Constructs a controller with the given coefficients and plant.
|
|
|
|
|
*
|
2023-07-31 19:17:44 -07:00
|
|
|
* See
|
|
|
|
|
* https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html#lqr-tuning
|
|
|
|
|
* for how to select the tolerances.
|
|
|
|
|
*
|
2020-08-14 23:40:33 -07:00
|
|
|
* @param plant The plant being controlled.
|
|
|
|
|
* @param Qelems The maximum desired error tolerance for each state.
|
|
|
|
|
* @param Relems The maximum desired control effort for each input.
|
|
|
|
|
* @param dt Discretization timestep.
|
2022-09-17 00:16:40 -07:00
|
|
|
* @throws std::invalid_argument If the system is uncontrollable.
|
2020-08-14 23:40:33 -07:00
|
|
|
*/
|
|
|
|
|
template <int Outputs>
|
2022-04-29 23:29:17 -07:00
|
|
|
LinearQuadraticRegulator(const LinearSystem<States, Inputs, Outputs>& plant,
|
|
|
|
|
const StateArray& Qelems, const InputArray& Relems,
|
|
|
|
|
units::second_t dt);
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructs a controller with the given coefficients and plant.
|
|
|
|
|
*
|
2023-07-31 19:17:44 -07:00
|
|
|
* See
|
|
|
|
|
* https://docs.wpilib.org/en/stable/docs/software/advanced-controls/state-space/state-space-intro.html#lqr-tuning
|
|
|
|
|
* for how to select the tolerances.
|
|
|
|
|
*
|
2020-08-14 23:40:33 -07:00
|
|
|
* @param A Continuous system matrix of the plant being controlled.
|
|
|
|
|
* @param B Continuous input matrix of the plant being controlled.
|
|
|
|
|
* @param Qelems The maximum desired error tolerance for each state.
|
|
|
|
|
* @param Relems The maximum desired control effort for each input.
|
|
|
|
|
* @param dt Discretization timestep.
|
2022-09-17 00:16:40 -07:00
|
|
|
* @throws std::invalid_argument If the system is uncontrollable.
|
2020-08-14 23:40:33 -07:00
|
|
|
*/
|
2022-04-29 23:29:17 -07:00
|
|
|
LinearQuadraticRegulator(const Matrixd<States, States>& A,
|
|
|
|
|
const Matrixd<States, Inputs>& B,
|
|
|
|
|
const StateArray& Qelems, const InputArray& Relems,
|
|
|
|
|
units::second_t dt);
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructs a controller with the given coefficients and plant.
|
|
|
|
|
*
|
2021-02-15 18:17:55 -08:00
|
|
|
* @param A Continuous system matrix of the plant being controlled.
|
|
|
|
|
* @param B Continuous input matrix of the plant being controlled.
|
|
|
|
|
* @param Q The state cost matrix.
|
|
|
|
|
* @param R The input cost matrix.
|
|
|
|
|
* @param dt Discretization timestep.
|
2022-09-17 00:16:40 -07:00
|
|
|
* @throws std::invalid_argument If the system is uncontrollable.
|
2020-08-14 23:40:33 -07:00
|
|
|
*/
|
2022-04-29 23:29:17 -07:00
|
|
|
LinearQuadraticRegulator(const Matrixd<States, States>& A,
|
|
|
|
|
const Matrixd<States, Inputs>& B,
|
|
|
|
|
const Matrixd<States, States>& Q,
|
|
|
|
|
const Matrixd<Inputs, Inputs>& R,
|
|
|
|
|
units::second_t dt);
|
2021-02-15 18:17:55 -08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructs a controller with the given coefficients and plant.
|
|
|
|
|
*
|
|
|
|
|
* @param A Continuous system matrix of the plant being controlled.
|
|
|
|
|
* @param B Continuous input matrix of the plant being controlled.
|
|
|
|
|
* @param Q The state cost matrix.
|
|
|
|
|
* @param R The input cost matrix.
|
|
|
|
|
* @param N The state-input cross-term cost matrix.
|
|
|
|
|
* @param dt Discretization timestep.
|
2022-09-17 00:16:40 -07:00
|
|
|
* @throws std::invalid_argument If the system is uncontrollable.
|
2021-02-15 18:17:55 -08:00
|
|
|
*/
|
2022-04-29 23:29:17 -07:00
|
|
|
LinearQuadraticRegulator(const Matrixd<States, States>& A,
|
|
|
|
|
const Matrixd<States, Inputs>& B,
|
|
|
|
|
const Matrixd<States, States>& Q,
|
|
|
|
|
const Matrixd<Inputs, Inputs>& R,
|
|
|
|
|
const Matrixd<States, Inputs>& N,
|
|
|
|
|
units::second_t dt);
|
2020-08-14 23:40:33 -07:00
|
|
|
|
2022-04-29 23:29:17 -07:00
|
|
|
LinearQuadraticRegulator(LinearQuadraticRegulator&&) = default;
|
|
|
|
|
LinearQuadraticRegulator& operator=(LinearQuadraticRegulator&&) = default;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the controller matrix K.
|
|
|
|
|
*/
|
2022-04-29 22:29:20 -07:00
|
|
|
const Matrixd<Inputs, States>& K() const { return m_K; }
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns an element of the controller matrix K.
|
|
|
|
|
*
|
|
|
|
|
* @param i Row of K.
|
|
|
|
|
* @param j Column of K.
|
|
|
|
|
*/
|
|
|
|
|
double K(int i, int j) const { return m_K(i, j); }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the reference vector r.
|
|
|
|
|
*
|
|
|
|
|
* @return The 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 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
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the control input vector u.
|
|
|
|
|
*
|
|
|
|
|
* @return The control input.
|
|
|
|
|
*/
|
2022-04-29 22:29:20 -07:00
|
|
|
const InputVector& U() const { return m_u; }
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns an element of the control input vector u.
|
|
|
|
|
*
|
|
|
|
|
* @param i Row of u.
|
|
|
|
|
*
|
|
|
|
|
* @return The row of the control input vector.
|
|
|
|
|
*/
|
2022-03-19 20:44:14 -07:00
|
|
|
double U(int i) const { return m_u(i); }
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resets the controller.
|
|
|
|
|
*/
|
|
|
|
|
void Reset() {
|
|
|
|
|
m_r.setZero();
|
|
|
|
|
m_u.setZero();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the next output of the controller.
|
|
|
|
|
*
|
|
|
|
|
* @param x The current state x.
|
|
|
|
|
*/
|
2022-04-29 23:29:17 -07:00
|
|
|
InputVector Calculate(const StateVector& x);
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the next output of the controller.
|
|
|
|
|
*
|
|
|
|
|
* @param x The current state x.
|
|
|
|
|
* @param nextR The next reference vector r.
|
|
|
|
|
*/
|
2022-04-29 23:29:17 -07:00
|
|
|
InputVector Calculate(const StateVector& x, const StateVector& nextR);
|
2020-08-14 23:40:33 -07:00
|
|
|
|
2020-11-20 15:28:00 -08:00
|
|
|
/**
|
|
|
|
|
* Adjusts LQR controller gain to compensate for a pure time delay in the
|
|
|
|
|
* input.
|
|
|
|
|
*
|
|
|
|
|
* Linear-Quadratic regulator controller gains tend to be aggressive. If
|
|
|
|
|
* sensor measurements are time-delayed too long, the LQR may be unstable.
|
|
|
|
|
* However, if we know the amount of delay, we can compute the control based
|
|
|
|
|
* on where the system will be after the time delay.
|
|
|
|
|
*
|
|
|
|
|
* See https://file.tavsys.net/control/controls-engineering-in-frc.pdf
|
|
|
|
|
* appendix C.4 for a derivation.
|
|
|
|
|
*
|
|
|
|
|
* @param plant The plant being controlled.
|
|
|
|
|
* @param dt Discretization timestep.
|
|
|
|
|
* @param inputDelay Input time delay.
|
|
|
|
|
*/
|
|
|
|
|
template <int Outputs>
|
|
|
|
|
void LatencyCompensate(const LinearSystem<States, Inputs, Outputs>& plant,
|
2022-04-29 23:29:17 -07:00
|
|
|
units::second_t dt, units::second_t inputDelay);
|
2020-11-20 15:28:00 -08:00
|
|
|
|
2020-08-14 23:40:33 -07:00
|
|
|
private:
|
|
|
|
|
// Current reference
|
2022-04-29 22:29:20 -07:00
|
|
|
StateVector m_r;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
// Computed controller output
|
2022-04-29 22:29:20 -07:00
|
|
|
InputVector m_u;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
// Controller gain
|
2022-04-29 22:29:20 -07:00
|
|
|
Matrixd<Inputs, States> m_K;
|
2020-08-14 23:40:33 -07:00
|
|
|
};
|
|
|
|
|
|
2022-04-29 23:29:17 -07:00
|
|
|
extern template class EXPORT_TEMPLATE_DECLARE(WPILIB_DLLEXPORT)
|
|
|
|
|
LinearQuadraticRegulator<1, 1>;
|
|
|
|
|
extern template class EXPORT_TEMPLATE_DECLARE(WPILIB_DLLEXPORT)
|
|
|
|
|
LinearQuadraticRegulator<2, 1>;
|
|
|
|
|
extern template class EXPORT_TEMPLATE_DECLARE(WPILIB_DLLEXPORT)
|
|
|
|
|
LinearQuadraticRegulator<2, 2>;
|
2021-06-01 12:26:11 -07:00
|
|
|
|
2020-08-14 23:40:33 -07:00
|
|
|
} // namespace frc
|
2022-04-29 23:29:17 -07:00
|
|
|
|
|
|
|
|
#include "LinearQuadraticRegulator.inc"
|