Brick
Loading...
Searching...
No Matches
dare_solver.h
1#pragma once
2
3#include "core/math/eigen_interface.h"
4
5/**
6 * Copyright (c) FIRST and other WPILib contributors.
7 *
8 * Computes the unique stabilizing solution X to the discrete-time algebraic
9 * Riccati equation:
10 *
11 * AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
12 *
13 * This internal function skips expensive precondition checks for increased
14 * performance. The solver may hang if any of the following occur:
15 * <ul>
16 * <li>Q isn't symmetric positive semidefinite</li>
17 * <li>R isn't symmetric positive definite</li>
18 * <li>The (A, B) pair isn't stabilizable</li>
19 * <li>The (A, C) pair where Q = CᵀC isn't detectable</li>
20 * </ul>
21 * Only use this function if you're sure the preconditions are met.
22 *
23 * This algorithm is taken directly from WPILib. The version that I wrote was
24 * slower, unsurprisingly, so we will use theirs instead.
25 *
26 * This is also the algorithm used by Drake, an advanced robotics library.
27 *
28 * @tparam STATES Number of STATES.
29 * @tparam INPUTS Number of INPUTS.
30 * @param A The system matrix.
31 * @param B The input matrix.
32 * @param Q The state cost matrix.
33 * @param R_llt The LLT decomposition of the input cost matrix.
34 * @return Solution to the DARE.
35 */
36template <int STATES, int INPUTS>
37constexpr EMat<STATES, STATES> DARE(const EMat<STATES, STATES>& A, const EMat<STATES, INPUTS>& B,
38 const EMat<STATES, STATES>& Q, const EMat<INPUTS, INPUTS>& R) {
39 const Eigen::LLT<EMat<INPUTS, INPUTS>> R_llt = R.llt();
40 using StateMatrix = EMat<STATES, STATES>;
41
42 /*
43 * Implements SDA algorithm on p. 5 of [1] (initial A, G, H are from (4)).
44 *
45 * [1] E. K.-W. Chu, H.-Y. Fan, W.-W. Lin & C.-S. Wang "Structure-Preserving
46 * Algorithms for Periodic Discrete-Time Algebraic Riccati Equations",
47 * International Journal of Control, 77:8, 767-788, 2004.
48 * DOI: 10.1080/00207170410001714988
49 *
50 * A₀ = A
51 * G₀ = BR⁻¹Bᵀ
52 * H₀ = Q
53 */
54 StateMatrix A_k = A;
55 StateMatrix G_k = B * R_llt.solve(B.transpose());
56 StateMatrix H_k;
57 StateMatrix H_k1 = Q;
58
59 do {
60 H_k = H_k1;
61
62 // W = I + GₖHₖ
63 StateMatrix W = StateMatrix::Identity(H_k.rows(), H_k.cols()) + G_k * H_k;
64
65 auto W_solver = W.lu();
66
67 // Solve WV₁ = Aₖ for V₁
68 StateMatrix V_1 = W_solver.solve(A_k);
69 /*
70 * Solve V₂Wᵀ = Gₖ for V₂
71 *
72 * We want to put V₂Wᵀ = Gₖ into Ax = b form so we can solve it more
73 * efficiently.
74 *
75 * V₂Wᵀ = Gₖ
76 * (V₂Wᵀ)ᵀ = Gₖᵀ
77 * WV₂ᵀ = Gₖᵀ
78 *
79 * The solution of Ax = b can be found via x = A.solve(b).
80 *
81 * V₂ᵀ = W.solve(Gₖᵀ)
82 * V₂ = W.solve(Gₖᵀ)ᵀ
83 */
84 StateMatrix V_2 = W_solver.solve(G_k.transpose()).transpose();
85 /*
86 * Gₖ₊₁ = Gₖ + AₖV₂Aₖᵀ
87 * Hₖ₊₁ = Hₖ + V₁ᵀHₖAₖ
88 * Aₖ₊₁ = AₖV₁
89 */
90 G_k += A_k * V_2 * A_k.transpose();
91 H_k1 = H_k + V_1.transpose() * H_k * A_k;
92 A_k *= V_1;
93
94 // while |Hₖ₊₁ − Hₖ| > ε |Hₖ₊₁|
95 } while ((H_k1 - H_k).norm() > 1e-10 * H_k1.norm());
96
97 return H_k1;
98}