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-07-24 08:34:30 -07:00
|
|
|
|
|
2020-08-07 09:38:13 -07:00
|
|
|
|
package edu.wpi.first.math;
|
2020-07-24 08:34:30 -07:00
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
|
import org.ejml.simple.SimpleMatrix;
|
2020-08-07 09:38:13 -07:00
|
|
|
|
|
2023-05-14 22:23:00 -07:00
|
|
|
|
public final class DARE {
|
2023-06-09 00:11:26 -04:00
|
|
|
|
private DARE() {
|
|
|
|
|
|
throw new UnsupportedOperationException("This is a utility class!");
|
|
|
|
|
|
}
|
2020-07-24 08:34:30 -07:00
|
|
|
|
|
|
|
|
|
|
/**
|
2023-08-17 13:56:15 -07:00
|
|
|
|
* Computes the unique stabilizing solution X to the discrete-time algebraic Riccati equation.
|
2020-07-24 08:34:30 -07:00
|
|
|
|
*
|
2023-08-17 13:56:15 -07:00
|
|
|
|
* <p>AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
|
|
|
|
|
|
*
|
|
|
|
|
|
* <p>This internal function skips expensive precondition checks for increased performance. The
|
|
|
|
|
|
* solver may hang if any of the following occur:
|
|
|
|
|
|
*
|
|
|
|
|
|
* <ul>
|
|
|
|
|
|
* <li>Q isn't symmetric positive semidefinite
|
|
|
|
|
|
* <li>R isn't symmetric positive definite
|
|
|
|
|
|
* <li>The (A, B) pair isn't stabilizable
|
|
|
|
|
|
* <li>The (A, C) pair where Q = CᵀC isn't detectable
|
|
|
|
|
|
* </ul>
|
|
|
|
|
|
*
|
|
|
|
|
|
* <p>Only use this function if you're sure the preconditions are met.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param <States> Number of states.
|
|
|
|
|
|
* @param <Inputs> Number of inputs.
|
2020-07-24 08:34:30 -07:00
|
|
|
|
* @param A System matrix.
|
|
|
|
|
|
* @param B Input matrix.
|
|
|
|
|
|
* @param Q State cost matrix.
|
|
|
|
|
|
* @param R Input cost matrix.
|
|
|
|
|
|
* @return Solution of DARE.
|
|
|
|
|
|
*/
|
2023-08-17 13:56:15 -07:00
|
|
|
|
public static <States extends Num, Inputs extends Num> Matrix<States, States> dareDetail(
|
|
|
|
|
|
Matrix<States, States> A,
|
|
|
|
|
|
Matrix<States, Inputs> B,
|
|
|
|
|
|
Matrix<States, States> Q,
|
|
|
|
|
|
Matrix<Inputs, Inputs> R) {
|
|
|
|
|
|
var S = new Matrix<States, States>(new SimpleMatrix(A.getNumRows(), A.getNumCols()));
|
|
|
|
|
|
WPIMathJNI.dareDetailABQR(
|
|
|
|
|
|
A.getStorage().getDDRM().getData(),
|
|
|
|
|
|
B.getStorage().getDDRM().getData(),
|
|
|
|
|
|
Q.getStorage().getDDRM().getData(),
|
|
|
|
|
|
R.getStorage().getDDRM().getData(),
|
2023-08-11 23:25:43 -07:00
|
|
|
|
A.getNumCols(),
|
|
|
|
|
|
B.getNumCols(),
|
2023-08-17 13:56:15 -07:00
|
|
|
|
S.getStorage().getDDRM().getData());
|
2020-07-24 08:34:30 -07:00
|
|
|
|
return S;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-08-17 13:56:15 -07:00
|
|
|
|
* Computes the unique stabilizing solution X to the discrete-time algebraic Riccati equation.
|
|
|
|
|
|
*
|
|
|
|
|
|
* <p>AᵀXA − X − (AᵀXB + N)(BᵀXB + R)⁻¹(BᵀXA + Nᵀ) + Q = 0
|
|
|
|
|
|
*
|
|
|
|
|
|
* <p>This overload of the DARE is useful for finding the control law uₖ that minimizes the
|
|
|
|
|
|
* following cost function subject to xₖ₊₁ = Axₖ + Buₖ.
|
|
|
|
|
|
*
|
|
|
|
|
|
* <pre>
|
|
|
|
|
|
* ∞ [xₖ]ᵀ[Q N][xₖ]
|
|
|
|
|
|
* J = Σ [uₖ] [Nᵀ R][uₖ] ΔT
|
|
|
|
|
|
* k=0
|
|
|
|
|
|
* </pre>
|
|
|
|
|
|
*
|
|
|
|
|
|
* <p>This is a more general form of the following. The linear-quadratic regulator is the feedback
|
|
|
|
|
|
* control law uₖ that minimizes the following cost function subject to xₖ₊₁ = Axₖ + Buₖ:
|
|
|
|
|
|
*
|
|
|
|
|
|
* <pre>
|
|
|
|
|
|
* ∞
|
|
|
|
|
|
* J = Σ (xₖᵀQxₖ + uₖᵀRuₖ) ΔT
|
|
|
|
|
|
* k=0
|
|
|
|
|
|
* </pre>
|
|
|
|
|
|
*
|
|
|
|
|
|
* <p>This can be refactored as:
|
|
|
|
|
|
*
|
|
|
|
|
|
* <pre>
|
|
|
|
|
|
* ∞ [xₖ]ᵀ[Q 0][xₖ]
|
|
|
|
|
|
* J = Σ [uₖ] [0 R][uₖ] ΔT
|
|
|
|
|
|
* k=0
|
|
|
|
|
|
* </pre>
|
|
|
|
|
|
*
|
|
|
|
|
|
* <p>This internal function skips expensive precondition checks for increased performance. The
|
|
|
|
|
|
* solver may hang if any of the following occur:
|
|
|
|
|
|
*
|
|
|
|
|
|
* <ul>
|
|
|
|
|
|
* <li>Q − NR⁻¹Nᵀ isn't symmetric positive semidefinite
|
|
|
|
|
|
* <li>R isn't symmetric positive definite
|
|
|
|
|
|
* <li>The (A, B) pair isn't stabilizable
|
|
|
|
|
|
* <li>The (A, C) pair where Q = CᵀC isn't detectable
|
|
|
|
|
|
* </ul>
|
|
|
|
|
|
*
|
|
|
|
|
|
* <p>Only use this function if you're sure the preconditions are met.
|
2020-07-24 08:34:30 -07:00
|
|
|
|
*
|
2021-06-10 20:46:47 -07:00
|
|
|
|
* @param <States> Number of states.
|
|
|
|
|
|
* @param <Inputs> Number of inputs.
|
2020-07-24 08:34:30 -07:00
|
|
|
|
* @param A System matrix.
|
|
|
|
|
|
* @param B Input matrix.
|
|
|
|
|
|
* @param Q State cost matrix.
|
|
|
|
|
|
* @param R Input cost matrix.
|
2023-08-17 13:56:15 -07:00
|
|
|
|
* @param N State-input cross-term cost matrix.
|
2020-07-24 08:34:30 -07:00
|
|
|
|
* @return Solution of DARE.
|
|
|
|
|
|
*/
|
2023-08-17 13:56:15 -07:00
|
|
|
|
public static <States extends Num, Inputs extends Num> Matrix<States, States> dareDetail(
|
2023-05-14 22:23:00 -07:00
|
|
|
|
Matrix<States, States> A,
|
|
|
|
|
|
Matrix<States, Inputs> B,
|
|
|
|
|
|
Matrix<States, States> Q,
|
2023-08-17 13:56:15 -07:00
|
|
|
|
Matrix<Inputs, Inputs> R,
|
|
|
|
|
|
Matrix<States, Inputs> N) {
|
|
|
|
|
|
var S = new Matrix<States, States>(new SimpleMatrix(A.getNumRows(), A.getNumCols()));
|
|
|
|
|
|
WPIMathJNI.dareDetailABQRN(
|
|
|
|
|
|
A.getStorage().getDDRM().getData(),
|
|
|
|
|
|
B.getStorage().getDDRM().getData(),
|
|
|
|
|
|
Q.getStorage().getDDRM().getData(),
|
|
|
|
|
|
R.getStorage().getDDRM().getData(),
|
|
|
|
|
|
N.getStorage().getDDRM().getData(),
|
|
|
|
|
|
A.getNumCols(),
|
|
|
|
|
|
B.getNumCols(),
|
|
|
|
|
|
S.getStorage().getDDRM().getData());
|
|
|
|
|
|
return S;
|
2020-07-24 08:34:30 -07:00
|
|
|
|
}
|
2021-02-15 18:17:55 -08:00
|
|
|
|
|
|
|
|
|
|
/**
|
2023-08-17 13:56:15 -07:00
|
|
|
|
* Computes the unique stabilizing solution X to the discrete-time algebraic Riccati equation.
|
2021-02-15 18:17:55 -08:00
|
|
|
|
*
|
2023-08-17 13:56:15 -07:00
|
|
|
|
* <p>AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param <States> Number of states.
|
|
|
|
|
|
* @param <Inputs> Number of inputs.
|
2021-02-15 18:17:55 -08:00
|
|
|
|
* @param A System matrix.
|
|
|
|
|
|
* @param B Input matrix.
|
|
|
|
|
|
* @param Q State cost matrix.
|
|
|
|
|
|
* @param R Input cost matrix.
|
|
|
|
|
|
* @return Solution of DARE.
|
2023-08-17 13:56:15 -07:00
|
|
|
|
* @throws IllegalArgumentException if Q isn't symmetric positive semidefinite.
|
2023-08-12 19:45:45 -07:00
|
|
|
|
* @throws IllegalArgumentException if R isn't symmetric positive definite.
|
|
|
|
|
|
* @throws IllegalArgumentException if the (A, B) pair isn't stabilizable.
|
|
|
|
|
|
* @throws IllegalArgumentException if the (A, C) pair where Q = CᵀC isn't detectable.
|
2021-02-15 18:17:55 -08:00
|
|
|
|
*/
|
2023-08-17 13:56:15 -07:00
|
|
|
|
public static <States extends Num, Inputs extends Num> Matrix<States, States> dare(
|
|
|
|
|
|
Matrix<States, States> A,
|
|
|
|
|
|
Matrix<States, Inputs> B,
|
|
|
|
|
|
Matrix<States, States> Q,
|
|
|
|
|
|
Matrix<Inputs, Inputs> R) {
|
|
|
|
|
|
var S = new Matrix<States, States>(new SimpleMatrix(A.getNumRows(), A.getNumCols()));
|
|
|
|
|
|
WPIMathJNI.dareABQR(
|
|
|
|
|
|
A.getStorage().getDDRM().getData(),
|
|
|
|
|
|
B.getStorage().getDDRM().getData(),
|
|
|
|
|
|
Q.getStorage().getDDRM().getData(),
|
|
|
|
|
|
R.getStorage().getDDRM().getData(),
|
2023-08-11 23:25:43 -07:00
|
|
|
|
A.getNumCols(),
|
|
|
|
|
|
B.getNumCols(),
|
2023-08-17 13:56:15 -07:00
|
|
|
|
S.getStorage().getDDRM().getData());
|
2021-02-15 18:17:55 -08:00
|
|
|
|
return S;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-08-17 13:56:15 -07:00
|
|
|
|
* Computes the unique stabilizing solution X to the discrete-time algebraic Riccati equation.
|
|
|
|
|
|
*
|
|
|
|
|
|
* <p>AᵀXA − X − (AᵀXB + N)(BᵀXB + R)⁻¹(BᵀXA + Nᵀ) + Q = 0
|
|
|
|
|
|
*
|
|
|
|
|
|
* <p>This overload of the DARE is useful for finding the control law uₖ that minimizes the
|
|
|
|
|
|
* following cost function subject to xₖ₊₁ = Axₖ + Buₖ.
|
|
|
|
|
|
*
|
|
|
|
|
|
* <pre>
|
|
|
|
|
|
* ∞ [xₖ]ᵀ[Q N][xₖ]
|
|
|
|
|
|
* J = Σ [uₖ] [Nᵀ R][uₖ] ΔT
|
|
|
|
|
|
* k=0
|
|
|
|
|
|
* </pre>
|
|
|
|
|
|
*
|
|
|
|
|
|
* <p>This is a more general form of the following. The linear-quadratic regulator is the feedback
|
|
|
|
|
|
* control law uₖ that minimizes the following cost function subject to xₖ₊₁ = Axₖ + Buₖ:
|
|
|
|
|
|
*
|
|
|
|
|
|
* <pre>
|
|
|
|
|
|
* ∞
|
|
|
|
|
|
* J = Σ (xₖᵀQxₖ + uₖᵀRuₖ) ΔT
|
|
|
|
|
|
* k=0
|
|
|
|
|
|
* </pre>
|
|
|
|
|
|
*
|
|
|
|
|
|
* <p>This can be refactored as:
|
|
|
|
|
|
*
|
|
|
|
|
|
* <pre>
|
|
|
|
|
|
* ∞ [xₖ]ᵀ[Q 0][xₖ]
|
|
|
|
|
|
* J = Σ [uₖ] [0 R][uₖ] ΔT
|
|
|
|
|
|
* k=0
|
|
|
|
|
|
* </pre>
|
2021-02-15 18:17:55 -08:00
|
|
|
|
*
|
2021-06-10 20:46:47 -07:00
|
|
|
|
* @param <States> Number of states.
|
|
|
|
|
|
* @param <Inputs> Number of inputs.
|
2021-02-15 18:17:55 -08:00
|
|
|
|
* @param A System matrix.
|
|
|
|
|
|
* @param B Input matrix.
|
|
|
|
|
|
* @param Q State cost matrix.
|
|
|
|
|
|
* @param R Input cost matrix.
|
|
|
|
|
|
* @param N State-input cross-term cost matrix.
|
|
|
|
|
|
* @return Solution of DARE.
|
2023-08-12 19:45:45 -07:00
|
|
|
|
* @throws IllegalArgumentException if Q − NR⁻¹Nᵀ isn't symmetric positive semidefinite.
|
|
|
|
|
|
* @throws IllegalArgumentException if R isn't symmetric positive definite.
|
|
|
|
|
|
* @throws IllegalArgumentException if the (A, B) pair isn't stabilizable.
|
|
|
|
|
|
* @throws IllegalArgumentException if the (A, C) pair where Q = CᵀC isn't detectable.
|
2021-02-15 18:17:55 -08:00
|
|
|
|
*/
|
2023-05-14 22:23:00 -07:00
|
|
|
|
public static <States extends Num, Inputs extends Num> Matrix<States, States> dare(
|
|
|
|
|
|
Matrix<States, States> A,
|
|
|
|
|
|
Matrix<States, Inputs> B,
|
|
|
|
|
|
Matrix<States, States> Q,
|
|
|
|
|
|
Matrix<Inputs, Inputs> R,
|
|
|
|
|
|
Matrix<States, Inputs> N) {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var S = new Matrix<States, States>(new SimpleMatrix(A.getNumRows(), A.getNumCols()));
|
|
|
|
|
|
WPIMathJNI.dareABQRN(
|
|
|
|
|
|
A.getStorage().getDDRM().getData(),
|
|
|
|
|
|
B.getStorage().getDDRM().getData(),
|
|
|
|
|
|
Q.getStorage().getDDRM().getData(),
|
|
|
|
|
|
R.getStorage().getDDRM().getData(),
|
|
|
|
|
|
N.getStorage().getDDRM().getData(),
|
|
|
|
|
|
A.getNumCols(),
|
|
|
|
|
|
B.getNumCols(),
|
|
|
|
|
|
S.getStorage().getDDRM().getData());
|
|
|
|
|
|
return S;
|
2021-02-15 18:17:55 -08:00
|
|
|
|
}
|
2020-07-24 08:34:30 -07:00
|
|
|
|
}
|