001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.math.system; 006 007import edu.wpi.first.math.Matrix; 008import edu.wpi.first.math.Num; 009import edu.wpi.first.math.numbers.N1; 010 011@SuppressWarnings("ClassTypeParameterName") 012public class LinearSystem<States extends Num, Inputs extends Num, Outputs extends Num> { 013 /** Continuous system matrix. */ 014 @SuppressWarnings("MemberName") 015 private final Matrix<States, States> m_A; 016 017 /** Continuous input matrix. */ 018 @SuppressWarnings("MemberName") 019 private final Matrix<States, Inputs> m_B; 020 021 /** Output matrix. */ 022 @SuppressWarnings("MemberName") 023 private final Matrix<Outputs, States> m_C; 024 025 /** Feedthrough matrix. */ 026 @SuppressWarnings("MemberName") 027 private final Matrix<Outputs, Inputs> m_D; 028 029 /** 030 * Construct a new LinearSystem from the four system matrices. 031 * 032 * @param a The system matrix A. 033 * @param b The input matrix B. 034 * @param c The output matrix C. 035 * @param d The feedthrough matrix D. 036 * @throws IllegalArgumentException if any matrix element isn't finite. 037 */ 038 @SuppressWarnings("ParameterName") 039 public LinearSystem( 040 Matrix<States, States> a, 041 Matrix<States, Inputs> b, 042 Matrix<Outputs, States> c, 043 Matrix<Outputs, Inputs> d) { 044 for (int row = 0; row < a.getNumRows(); ++row) { 045 for (int col = 0; col < a.getNumCols(); ++col) { 046 if (!Double.isFinite(a.get(row, col))) { 047 throw new IllegalArgumentException( 048 "Elements of A aren't finite. This is usually due to model implementation errors."); 049 } 050 } 051 } 052 for (int row = 0; row < b.getNumRows(); ++row) { 053 for (int col = 0; col < b.getNumCols(); ++col) { 054 if (!Double.isFinite(b.get(row, col))) { 055 throw new IllegalArgumentException( 056 "Elements of B aren't finite. This is usually due to model implementation errors."); 057 } 058 } 059 } 060 for (int row = 0; row < c.getNumRows(); ++row) { 061 for (int col = 0; col < c.getNumCols(); ++col) { 062 if (!Double.isFinite(c.get(row, col))) { 063 throw new IllegalArgumentException( 064 "Elements of C aren't finite. This is usually due to model implementation errors."); 065 } 066 } 067 } 068 for (int row = 0; row < d.getNumRows(); ++row) { 069 for (int col = 0; col < d.getNumCols(); ++col) { 070 if (!Double.isFinite(d.get(row, col))) { 071 throw new IllegalArgumentException( 072 "Elements of D aren't finite. This is usually due to model implementation errors."); 073 } 074 } 075 } 076 077 this.m_A = a; 078 this.m_B = b; 079 this.m_C = c; 080 this.m_D = d; 081 } 082 083 /** 084 * Returns the system matrix A. 085 * 086 * @return the system matrix A. 087 */ 088 public Matrix<States, States> getA() { 089 return m_A; 090 } 091 092 /** 093 * Returns an element of the system matrix A. 094 * 095 * @param row Row of A. 096 * @param col Column of A. 097 * @return the system matrix A at (i, j). 098 */ 099 public double getA(int row, int col) { 100 return m_A.get(row, col); 101 } 102 103 /** 104 * Returns the input matrix B. 105 * 106 * @return the input matrix B. 107 */ 108 public Matrix<States, Inputs> getB() { 109 return m_B; 110 } 111 112 /** 113 * Returns an element of the input matrix B. 114 * 115 * @param row Row of B. 116 * @param col Column of B. 117 * @return The value of the input matrix B at (i, j). 118 */ 119 public double getB(int row, int col) { 120 return m_B.get(row, col); 121 } 122 123 /** 124 * Returns the output matrix C. 125 * 126 * @return Output matrix C. 127 */ 128 public Matrix<Outputs, States> getC() { 129 return m_C; 130 } 131 132 /** 133 * Returns an element of the output matrix C. 134 * 135 * @param row Row of C. 136 * @param col Column of C. 137 * @return the double value of C at the given position. 138 */ 139 public double getC(int row, int col) { 140 return m_C.get(row, col); 141 } 142 143 /** 144 * Returns the feedthrough matrix D. 145 * 146 * @return the feedthrough matrix D. 147 */ 148 public Matrix<Outputs, Inputs> getD() { 149 return m_D; 150 } 151 152 /** 153 * Returns an element of the feedthrough matrix D. 154 * 155 * @param row Row of D. 156 * @param col Column of D. 157 * @return The feedthrough matrix D at (i, j). 158 */ 159 public double getD(int row, int col) { 160 return m_D.get(row, col); 161 } 162 163 /** 164 * Computes the new x given the old x and the control input. 165 * 166 * <p>This is used by state observers directly to run updates based on state estimate. 167 * 168 * @param x The current state. 169 * @param clampedU The control input. 170 * @param dtSeconds Timestep for model update. 171 * @return the updated x. 172 */ 173 @SuppressWarnings("ParameterName") 174 public Matrix<States, N1> calculateX( 175 Matrix<States, N1> x, Matrix<Inputs, N1> clampedU, double dtSeconds) { 176 var discABpair = Discretization.discretizeAB(m_A, m_B, dtSeconds); 177 178 return (discABpair.getFirst().times(x)).plus(discABpair.getSecond().times(clampedU)); 179 } 180 181 /** 182 * Computes the new y given the control input. 183 * 184 * <p>This is used by state observers directly to run updates based on state estimate. 185 * 186 * @param x The current state. 187 * @param clampedU The control input. 188 * @return the updated output matrix Y. 189 */ 190 @SuppressWarnings("ParameterName") 191 public Matrix<Outputs, N1> calculateY(Matrix<States, N1> x, Matrix<Inputs, N1> clampedU) { 192 return m_C.times(x).plus(m_D.times(clampedU)); 193 } 194 195 @Override 196 public String toString() { 197 return String.format( 198 "Linear System: A\n%s\n\nB:\n%s\n\nC:\n%s\n\nD:\n%s\n", 199 m_A.toString(), m_B.toString(), m_C.toString(), m_D.toString()); 200 } 201}