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
|
|
|
|
2021-05-21 22:29:52 -07:00
|
|
|
package edu.wpi.first.math;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
2021-05-01 15:53:30 +00:00
|
|
|
import edu.wpi.first.math.geometry.Pose2d;
|
|
|
|
|
import edu.wpi.first.math.geometry.Rotation2d;
|
|
|
|
|
import edu.wpi.first.math.numbers.N1;
|
|
|
|
|
import edu.wpi.first.math.numbers.N2;
|
2020-12-29 22:45:16 -08:00
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import org.ejml.dense.row.MatrixFeatures_DDRM;
|
|
|
|
|
import org.ejml.simple.SimpleMatrix;
|
|
|
|
|
import org.junit.jupiter.api.Test;
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
public class StateSpaceUtilTest {
|
|
|
|
|
@Test
|
|
|
|
|
public void testCostArray() {
|
2020-12-29 22:45:16 -08:00
|
|
|
var mat = StateSpaceUtil.makeCostMatrix(VecBuilder.fill(1.0, 2.0, 3.0));
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
assertEquals(1.0, mat.get(0, 0), 1e-3);
|
|
|
|
|
assertEquals(0.0, mat.get(0, 1), 1e-3);
|
|
|
|
|
assertEquals(0.0, mat.get(0, 2), 1e-3);
|
|
|
|
|
assertEquals(0.0, mat.get(1, 0), 1e-3);
|
|
|
|
|
assertEquals(1.0 / 4.0, mat.get(1, 1), 1e-3);
|
|
|
|
|
assertEquals(0.0, mat.get(1, 2), 1e-3);
|
|
|
|
|
assertEquals(0.0, mat.get(0, 2), 1e-3);
|
|
|
|
|
assertEquals(0.0, mat.get(1, 2), 1e-3);
|
|
|
|
|
assertEquals(1.0 / 9.0, mat.get(2, 2), 1e-3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void testCovArray() {
|
2020-12-29 22:45:16 -08:00
|
|
|
var mat = StateSpaceUtil.makeCovarianceMatrix(Nat.N3(), VecBuilder.fill(1.0, 2.0, 3.0));
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
assertEquals(1.0, mat.get(0, 0), 1e-3);
|
|
|
|
|
assertEquals(0.0, mat.get(0, 1), 1e-3);
|
|
|
|
|
assertEquals(0.0, mat.get(0, 2), 1e-3);
|
|
|
|
|
assertEquals(0.0, mat.get(1, 0), 1e-3);
|
|
|
|
|
assertEquals(4.0, mat.get(1, 1), 1e-3);
|
|
|
|
|
assertEquals(0.0, mat.get(1, 2), 1e-3);
|
|
|
|
|
assertEquals(0.0, mat.get(0, 2), 1e-3);
|
|
|
|
|
assertEquals(0.0, mat.get(1, 2), 1e-3);
|
|
|
|
|
assertEquals(9.0, mat.get(2, 2), 1e-3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@SuppressWarnings("LocalVariableName")
|
|
|
|
|
public void testIsStabilizable() {
|
|
|
|
|
Matrix<N2, N2> A;
|
|
|
|
|
Matrix<N2, N1> B = VecBuilder.fill(0, 1);
|
|
|
|
|
|
|
|
|
|
// First eigenvalue is uncontrollable and unstable.
|
|
|
|
|
// Second eigenvalue is controllable and stable.
|
|
|
|
|
A = Matrix.mat(Nat.N2(), Nat.N2()).fill(1.2, 0, 0, 0.5);
|
|
|
|
|
assertFalse(StateSpaceUtil.isStabilizable(A, B));
|
|
|
|
|
|
|
|
|
|
// First eigenvalue is uncontrollable and marginally stable.
|
|
|
|
|
// Second eigenvalue is controllable and stable.
|
|
|
|
|
A = Matrix.mat(Nat.N2(), Nat.N2()).fill(1, 0, 0, 0.5);
|
|
|
|
|
assertFalse(StateSpaceUtil.isStabilizable(A, B));
|
|
|
|
|
|
|
|
|
|
// First eigenvalue is uncontrollable and stable.
|
|
|
|
|
// Second eigenvalue is controllable and stable.
|
|
|
|
|
A = Matrix.mat(Nat.N2(), Nat.N2()).fill(0.2, 0, 0, 0.5);
|
|
|
|
|
assertTrue(StateSpaceUtil.isStabilizable(A, B));
|
|
|
|
|
|
|
|
|
|
// First eigenvalue is uncontrollable and stable.
|
|
|
|
|
// Second eigenvalue is controllable and unstable.
|
|
|
|
|
A = Matrix.mat(Nat.N2(), Nat.N2()).fill(0.2, 0, 0, 1.2);
|
|
|
|
|
assertTrue(StateSpaceUtil.isStabilizable(A, B));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void testMakeWhiteNoiseVector() {
|
|
|
|
|
var firstData = new ArrayList<Double>();
|
|
|
|
|
var secondData = new ArrayList<Double>();
|
|
|
|
|
for (int i = 0; i < 1000; i++) {
|
|
|
|
|
var noiseVec = StateSpaceUtil.makeWhiteNoiseVector(VecBuilder.fill(1.0, 2.0));
|
|
|
|
|
firstData.add(noiseVec.get(0, 0));
|
|
|
|
|
secondData.add(noiseVec.get(1, 0));
|
|
|
|
|
}
|
|
|
|
|
assertEquals(1.0, calculateStandardDeviation(firstData), 0.2);
|
|
|
|
|
assertEquals(2.0, calculateStandardDeviation(secondData), 0.2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double calculateStandardDeviation(List<Double> numArray) {
|
|
|
|
|
double sum = 0.0;
|
|
|
|
|
double standardDeviation = 0.0;
|
|
|
|
|
int length = numArray.size();
|
|
|
|
|
|
|
|
|
|
for (double num : numArray) {
|
|
|
|
|
sum += num;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double mean = sum / length;
|
|
|
|
|
|
|
|
|
|
for (double num : numArray) {
|
|
|
|
|
standardDeviation += Math.pow(num - mean, 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Math.sqrt(standardDeviation / length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void testMatrixExp() {
|
|
|
|
|
Matrix<N2, N2> wrappedMatrix = Matrix.eye(Nat.N2());
|
|
|
|
|
var wrappedResult = wrappedMatrix.exp();
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
assertTrue(
|
|
|
|
|
wrappedResult.isEqual(Matrix.mat(Nat.N2(), Nat.N2()).fill(Math.E, 0, 0, Math.E), 1E-9));
|
2020-08-14 23:40:33 -07:00
|
|
|
|
|
|
|
|
var matrix = Matrix.mat(Nat.N2(), Nat.N2()).fill(1, 2, 3, 4);
|
|
|
|
|
wrappedResult = matrix.times(0.01).exp();
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
assertTrue(
|
|
|
|
|
wrappedResult.isEqual(
|
|
|
|
|
Matrix.mat(Nat.N2(), Nat.N2()).fill(1.01035625, 0.02050912, 0.03076368, 1.04111993),
|
|
|
|
|
1E-8));
|
2020-08-14 23:40:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void testSimpleMatrixExp() {
|
|
|
|
|
SimpleMatrix matrix = SimpleMatrixUtils.eye(2);
|
|
|
|
|
var result = SimpleMatrixUtils.exp(matrix);
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
assertTrue(
|
|
|
|
|
MatrixFeatures_DDRM.isIdentical(
|
|
|
|
|
result.getDDRM(),
|
|
|
|
|
new SimpleMatrix(2, 2, true, new double[] {Math.E, 0, 0, Math.E}).getDDRM(),
|
|
|
|
|
1E-9));
|
2020-08-14 23:40:33 -07:00
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
matrix = new SimpleMatrix(2, 2, true, new double[] {1, 2, 3, 4});
|
2020-08-14 23:40:33 -07:00
|
|
|
result = SimpleMatrixUtils.exp(matrix.scale(0.01));
|
|
|
|
|
|
2020-12-29 22:45:16 -08:00
|
|
|
assertTrue(
|
|
|
|
|
MatrixFeatures_DDRM.isIdentical(
|
|
|
|
|
result.getDDRM(),
|
|
|
|
|
new SimpleMatrix(
|
|
|
|
|
2, 2, true, new double[] {1.01035625, 0.02050912, 0.03076368, 1.04111993})
|
|
|
|
|
.getDDRM(),
|
|
|
|
|
1E-8));
|
2020-08-14 23:40:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void testPoseToVector() {
|
|
|
|
|
Pose2d pose = new Pose2d(1, 2, new Rotation2d(3));
|
|
|
|
|
var vector = StateSpaceUtil.poseToVector(pose);
|
|
|
|
|
assertEquals(pose.getTranslation().getX(), vector.get(0, 0), 1e-6);
|
|
|
|
|
assertEquals(pose.getTranslation().getY(), vector.get(1, 0), 1e-6);
|
|
|
|
|
assertEquals(pose.getRotation().getRadians(), vector.get(2, 0), 1e-6);
|
|
|
|
|
}
|
|
|
|
|
}
|