[wpimath] Reorganize LinearSystem factories (#8468)

This commit is contained in:
Tyler Veness
2026-01-12 19:09:35 -08:00
committed by GitHub
parent 89d0759ef2
commit 00fa8361dd
108 changed files with 1808 additions and 2138 deletions

View File

@@ -14,7 +14,7 @@ import org.wpilib.math.linalg.Matrix;
import org.wpilib.math.linalg.VecBuilder;
import org.wpilib.math.numbers.N1;
import org.wpilib.math.numbers.N2;
import org.wpilib.math.system.plant.LinearSystemId;
import org.wpilib.math.system.Models;
class DifferentialDriveAccelerationLimiterTest {
@Test
@@ -24,7 +24,7 @@ class DifferentialDriveAccelerationLimiterTest {
final double maxA = 2.0;
final double maxAlpha = 2.0;
var plant = LinearSystemId.identifyDrivetrainSystem(1.0, 1.0, 1.0, 1.0);
var plant = Models.differentialDriveFromSysId(1.0, 1.0, 1.0, 1.0);
var accelLimiter = new DifferentialDriveAccelerationLimiter(plant, trackwidth, maxA, maxAlpha);
@@ -114,7 +114,7 @@ class DifferentialDriveAccelerationLimiterTest {
final double trackwidth = 0.9;
final double dt = 0.005;
var plant = LinearSystemId.identifyDrivetrainSystem(1.0, 1.0, 1.0, 1.0);
var plant = Models.differentialDriveFromSysId(1.0, 1.0, 1.0, 1.0);
// Limits are so high, they don't get hit, so states of constrained and
// unconstrained systems should match
@@ -178,7 +178,7 @@ class DifferentialDriveAccelerationLimiterTest {
final double maxA = 2.0;
final double maxAlpha = 2.0;
var plant = LinearSystemId.identifyDrivetrainSystem(1.0, 1.0, 1.0, 1.0);
var plant = Models.differentialDriveFromSysId(1.0, 1.0, 1.0, 1.0);
var accelLimiter =
new DifferentialDriveAccelerationLimiter(plant, trackwidth, minA, maxA, maxAlpha);
@@ -237,7 +237,7 @@ class DifferentialDriveAccelerationLimiterTest {
@Test
void testMinAccelGreaterThanMaxAccel() {
var plant = LinearSystemId.identifyDrivetrainSystem(1.0, 1.0, 1.0, 1.0);
var plant = Models.differentialDriveFromSysId(1.0, 1.0, 1.0, 1.0);
assertDoesNotThrow(() -> new DifferentialDriveAccelerationLimiter(plant, 1, -1, 1, 1e3));
assertThrows(
IllegalArgumentException.class,

View File

@@ -12,7 +12,7 @@ import org.wpilib.math.linalg.VecBuilder;
import org.wpilib.math.numbers.N1;
import org.wpilib.math.numbers.N2;
import org.wpilib.math.system.LinearSystem;
import org.wpilib.math.system.plant.LinearSystemId;
import org.wpilib.math.system.Models;
class DifferentialDriveFeedforwardTest {
private static final double kVLinear = 1.0;
@@ -27,8 +27,7 @@ class DifferentialDriveFeedforwardTest {
DifferentialDriveFeedforward differentialDriveFeedforward =
new DifferentialDriveFeedforward(kVLinear, kALinear, kVAngular, kAAngular, trackwidth);
LinearSystem<N2, N2, N2> plant =
LinearSystemId.identifyDrivetrainSystem(
kVLinear, kALinear, kVAngular, kAAngular, trackwidth);
Models.differentialDriveFromSysId(kVLinear, kALinear, kVAngular, kAAngular, trackwidth);
for (int currentLeftVelocity = -4; currentLeftVelocity <= 4; currentLeftVelocity += 2) {
for (int currentRightVelocity = -4; currentRightVelocity <= 4; currentRightVelocity += 2) {
for (int nextLeftVelocity = -4; nextLeftVelocity <= 4; nextLeftVelocity += 2) {
@@ -58,7 +57,7 @@ class DifferentialDriveFeedforwardTest {
DifferentialDriveFeedforward differentialDriveFeedforward =
new DifferentialDriveFeedforward(kVLinear, kALinear, kVAngular, kAAngular);
LinearSystem<N2, N2, N2> plant =
LinearSystemId.identifyDrivetrainSystem(kVLinear, kALinear, kVAngular, kAAngular);
Models.differentialDriveFromSysId(kVLinear, kALinear, kVAngular, kAAngular);
for (int currentLeftVelocity = -4; currentLeftVelocity <= 4; currentLeftVelocity += 2) {
for (int currentRightVelocity = -4; currentRightVelocity <= 4; currentRightVelocity += 2) {
for (int nextLeftVelocity = -4; nextLeftVelocity <= 4; nextLeftVelocity += 2) {

View File

@@ -12,14 +12,14 @@ import org.wpilib.math.linalg.Matrix;
import org.wpilib.math.linalg.VecBuilder;
import org.wpilib.math.numbers.N1;
import org.wpilib.math.numbers.N2;
import org.wpilib.math.system.plant.LinearSystemId;
import org.wpilib.math.system.Models;
class ImplicitModelFollowerTest {
@Test
void testSameModel() {
final double dt = 0.005;
var plant = LinearSystemId.identifyDrivetrainSystem(1.0, 1.0, 1.0, 1.0);
var plant = Models.differentialDriveFromSysId(1.0, 1.0, 1.0, 1.0);
var imf = new ImplicitModelFollower<>(plant, plant);
@@ -61,10 +61,10 @@ class ImplicitModelFollowerTest {
void testSlowerRefModel() {
final double dt = 0.005;
var plant = LinearSystemId.identifyDrivetrainSystem(1.0, 1.0, 1.0, 1.0);
var plant = Models.differentialDriveFromSysId(1.0, 1.0, 1.0, 1.0);
// Linear acceleration is slower, but angular acceleration is the same
var plantRef = LinearSystemId.identifyDrivetrainSystem(1.0, 2.0, 1.0, 1.0);
var plantRef = Models.differentialDriveFromSysId(1.0, 2.0, 1.0, 1.0);
var imf = new ImplicitModelFollower<>(plant, plantRef);

View File

@@ -18,8 +18,8 @@ import org.wpilib.math.numbers.N1;
import org.wpilib.math.numbers.N2;
import org.wpilib.math.numbers.N5;
import org.wpilib.math.system.LinearSystem;
import org.wpilib.math.system.Models;
import org.wpilib.math.system.NumericalIntegration;
import org.wpilib.math.system.plant.LinearSystemId;
import org.wpilib.math.trajectory.TrajectoryConfig;
import org.wpilib.math.trajectory.TrajectoryGenerator;
import org.wpilib.math.util.MathUtil;
@@ -52,7 +52,7 @@ class LTVDifferentialDriveControllerTest {
private static final double kAngularV = 1.382; // V/(m/s)
private static final double kAngularA = 0.08495; // V/(m/s²)
private static final LinearSystem<N2, N2, N2> plant =
LinearSystemId.identifyDrivetrainSystem(kLinearV, kLinearA, kAngularV, kAngularA);
Models.differentialDriveFromSysId(kLinearV, kLinearA, kAngularV, kAngularA);
private static final double kTrackwidth = 0.9;
private static Matrix<N5, N1> dynamics(Matrix<N5, N1> x, Matrix<N2, N1> u) {

View File

@@ -10,9 +10,9 @@ import org.junit.jupiter.api.Test;
import org.wpilib.math.linalg.MatBuilder;
import org.wpilib.math.linalg.Matrix;
import org.wpilib.math.linalg.VecBuilder;
import org.wpilib.math.system.DCMotor;
import org.wpilib.math.system.Discretization;
import org.wpilib.math.system.plant.DCMotor;
import org.wpilib.math.system.plant.LinearSystemId;
import org.wpilib.math.system.Models;
import org.wpilib.math.util.Nat;
import org.wpilib.math.util.Num;
@@ -25,7 +25,7 @@ class LinearQuadraticRegulatorTest {
var r = 0.0181864;
var G = 1.0;
var plant = LinearSystemId.createElevatorSystem(motors, m, r, G);
var plant = Models.elevatorFromPhysicalConstants(motors, m, r, G);
var qElms = VecBuilder.fill(0.02, 0.4);
var rElms = VecBuilder.fill(12.0);
@@ -42,7 +42,7 @@ class LinearQuadraticRegulatorTest {
var dt = 0.020;
var plant =
LinearSystemId.createElevatorSystem(
Models.elevatorFromPhysicalConstants(
DCMotor.getVex775Pro(4), 8.0, 0.75 * 25.4 / 1000.0, 14.67);
var K =
@@ -61,7 +61,7 @@ class LinearQuadraticRegulatorTest {
var r = 0.4;
var G = 100.0;
var plant = LinearSystemId.createSingleJointedArmSystem(motors, 1d / 3d * m * r * r, G);
var plant = Models.singleJointedArmFromPhysicalConstants(motors, 1d / 3d * m * r * r, G);
var qElms = VecBuilder.fill(0.01745, 0.08726);
var rElms = VecBuilder.fill(12.0);
@@ -160,7 +160,7 @@ class LinearQuadraticRegulatorTest {
var dt = 0.02;
var plant =
LinearSystemId.createElevatorSystem(
Models.elevatorFromPhysicalConstants(
DCMotor.getVex775Pro(4), 8.0, 0.75 * 25.4 / 1000.0, 14.67);
var regulator =

View File

@@ -14,10 +14,10 @@ import org.wpilib.math.linalg.Matrix;
import org.wpilib.math.linalg.VecBuilder;
import org.wpilib.math.numbers.N1;
import org.wpilib.math.numbers.N2;
import org.wpilib.math.system.DCMotor;
import org.wpilib.math.system.LinearSystem;
import org.wpilib.math.system.LinearSystemLoop;
import org.wpilib.math.system.plant.DCMotor;
import org.wpilib.math.system.plant.LinearSystemId;
import org.wpilib.math.system.Models;
import org.wpilib.math.trajectory.TrapezoidProfile;
import org.wpilib.math.util.Nat;
@@ -27,7 +27,7 @@ class LinearSystemLoopTest {
private static final Random random = new Random();
LinearSystem<N2, N1, N2> m_plant =
LinearSystemId.createElevatorSystem(DCMotor.getVex775Pro(2), 5, 0.0181864, 1.0);
Models.elevatorFromPhysicalConstants(DCMotor.getVex775Pro(2), 5, 0.0181864, 1.0);
@SuppressWarnings("unchecked")
KalmanFilter<N2, N1, N1> m_observer =
@@ -95,7 +95,7 @@ class LinearSystemLoopTest {
@Test
void testFlywheelEnabled() {
LinearSystem<N1, N1, N1> plant =
LinearSystemId.createFlywheelSystem(DCMotor.getNEO(2), 0.00289, 1.0);
Models.flywheelFromPhysicalConstants(DCMotor.getNEO(2), 0.00289, 1.0);
KalmanFilter<N1, N1, N1> observer =
new KalmanFilter<>(
Nat.N1(), Nat.N1(), plant, VecBuilder.fill(1.0), VecBuilder.fill(kPositionStddev), kDt);

View File

@@ -18,9 +18,9 @@ import org.wpilib.math.numbers.N2;
import org.wpilib.math.numbers.N3;
import org.wpilib.math.numbers.N5;
import org.wpilib.math.random.Normal;
import org.wpilib.math.system.DCMotor;
import org.wpilib.math.system.NumericalIntegration;
import org.wpilib.math.system.NumericalJacobian;
import org.wpilib.math.system.plant.DCMotor;
import org.wpilib.math.trajectory.TrajectoryConfig;
import org.wpilib.math.trajectory.TrajectoryGenerator;
import org.wpilib.math.util.Nat;

View File

@@ -19,9 +19,9 @@ import org.wpilib.math.numbers.N1;
import org.wpilib.math.numbers.N2;
import org.wpilib.math.numbers.N3;
import org.wpilib.math.numbers.N6;
import org.wpilib.math.system.DCMotor;
import org.wpilib.math.system.LinearSystem;
import org.wpilib.math.system.plant.DCMotor;
import org.wpilib.math.system.plant.LinearSystemId;
import org.wpilib.math.system.Models;
import org.wpilib.math.trajectory.TrajectoryConfig;
import org.wpilib.math.trajectory.TrajectoryGenerator;
import org.wpilib.math.util.Nat;
@@ -41,7 +41,7 @@ class KalmanFilterTest {
var m = 5.0;
var r = 0.0181864;
var G = 1.0;
elevatorPlant = LinearSystemId.createElevatorSystem(motors, m, r, G);
elevatorPlant = Models.elevatorFromPhysicalConstants(motors, m, r, G);
}
// A swerve drive system where the states are [x, y, theta, vx, vy, vTheta]ᵀ,

View File

@@ -23,11 +23,11 @@ import org.wpilib.math.numbers.N3;
import org.wpilib.math.numbers.N4;
import org.wpilib.math.numbers.N5;
import org.wpilib.math.random.Normal;
import org.wpilib.math.system.DCMotor;
import org.wpilib.math.system.Discretization;
import org.wpilib.math.system.Models;
import org.wpilib.math.system.NumericalIntegration;
import org.wpilib.math.system.NumericalJacobian;
import org.wpilib.math.system.plant.DCMotor;
import org.wpilib.math.system.plant.LinearSystemId;
import org.wpilib.math.trajectory.TrajectoryConfig;
import org.wpilib.math.trajectory.TrajectoryGenerator;
import org.wpilib.math.util.Nat;
@@ -222,7 +222,7 @@ class MerweUKFTest {
@Test
void testLinearUKF() {
var dt = 0.020;
var plant = LinearSystemId.identifyVelocitySystem(0.02, 0.006);
var plant = Models.flywheelFromSysId(0.02, 0.006);
var observer =
new MerweUKF<>(
Nat.N1(),

View File

@@ -23,11 +23,11 @@ import org.wpilib.math.numbers.N3;
import org.wpilib.math.numbers.N4;
import org.wpilib.math.numbers.N5;
import org.wpilib.math.random.Normal;
import org.wpilib.math.system.DCMotor;
import org.wpilib.math.system.Discretization;
import org.wpilib.math.system.Models;
import org.wpilib.math.system.NumericalIntegration;
import org.wpilib.math.system.NumericalJacobian;
import org.wpilib.math.system.plant.DCMotor;
import org.wpilib.math.system.plant.LinearSystemId;
import org.wpilib.math.trajectory.TrajectoryConfig;
import org.wpilib.math.trajectory.TrajectoryGenerator;
import org.wpilib.math.util.Nat;
@@ -222,7 +222,7 @@ class S3UKFTest {
@Test
void testLinearUKF() {
var dt = 0.020;
var plant = LinearSystemId.identifyVelocitySystem(0.02, 0.006);
var plant = Models.flywheelFromSysId(0.02, 0.006);
var observer =
new S3UKF<>(
Nat.N1(),

View File

@@ -9,17 +9,36 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.wpilib.math.linalg.MatBuilder;
import org.wpilib.math.linalg.Matrix;
import org.wpilib.math.linalg.VecBuilder;
import org.wpilib.math.system.plant.DCMotor;
import org.wpilib.math.system.plant.LinearSystemId;
import org.wpilib.math.util.Nat;
class LinearSystemIDTest {
class ModelsTest {
@Test
void testDrivetrainVelocitySystem() {
void testFlywheelFromPhysicalConstants() {
var model = Models.flywheelFromPhysicalConstants(DCMotor.getNEO(2), 0.00032, 1.0);
assertTrue(model.getA().isEqual(VecBuilder.fill(-26.87032), 0.001));
assertTrue(model.getB().isEqual(VecBuilder.fill(1354.166667), 0.001));
assertTrue(model.getC().isEqual(VecBuilder.fill(1), 0.001));
assertTrue(model.getD().isEqual(VecBuilder.fill(0), 0.001));
}
@Test
void testFlywheelFromSysId() {
var kv = 1.0;
var ka = 0.5;
var model = Models.flywheelFromSysId(kv, ka);
assertEquals(model.getA(), VecBuilder.fill(-kv / ka));
assertEquals(model.getB(), VecBuilder.fill(1 / ka));
}
@Test
void testDifferentialDriveFromPhysicalConstants() {
var model =
LinearSystemId.createDrivetrainVelocitySystem(DCMotor.getNEO(4), 70, 0.05, 0.4, 6.0, 6);
Models.differentialDriveFromPhysicalConstants(DCMotor.getNEO(4), 70, 0.05, 0.4, 6.0, 6);
assertTrue(
model
.getA()
@@ -41,8 +60,8 @@ class LinearSystemIDTest {
}
@Test
void testElevatorSystem() {
var model = LinearSystemId.createElevatorSystem(DCMotor.getNEO(2), 5, 0.05, 12);
void testElevatorFromPhysicalConstants() {
var model = Models.elevatorFromPhysicalConstants(DCMotor.getNEO(2), 5, 0.05, 12);
assertTrue(
model.getA().isEqual(MatBuilder.fill(Nat.N2(), Nat.N2(), 0, 1, 0, -99.05473), 0.001));
@@ -54,52 +73,24 @@ class LinearSystemIDTest {
}
@Test
void testFlywheelSystem() {
var model = LinearSystemId.createFlywheelSystem(DCMotor.getNEO(2), 0.00032, 1.0);
assertTrue(model.getA().isEqual(VecBuilder.fill(-26.87032), 0.001));
assertTrue(model.getB().isEqual(VecBuilder.fill(1354.166667), 0.001));
assertTrue(model.getC().isEqual(VecBuilder.fill(1), 0.001));
assertTrue(model.getD().isEqual(VecBuilder.fill(0), 0.001));
}
@Test
void testDCMotorSystem() {
var model = LinearSystemId.createDCMotorSystem(DCMotor.getNEO(2), 0.00032, 1.0);
assertTrue(
model.getA().isEqual(MatBuilder.fill(Nat.N2(), Nat.N2(), 0, 1, 0, -26.87032), 0.001));
assertTrue(model.getB().isEqual(VecBuilder.fill(0, 1354.166667), 0.001));
assertTrue(model.getC().isEqual(Matrix.eye(Nat.N2()), 0.001));
assertTrue(model.getD().isEqual(new Matrix<>(Nat.N2(), Nat.N1()), 0.001));
}
@Test
void testIdentifyPositionSystem() {
// By controls engineering in frc,
// x-dot = [0 1 | 0 -kv/ka] x = [0 | 1/ka] u
void testElevatorFromSysId() {
var kv = 1.0;
var ka = 0.5;
var model = LinearSystemId.identifyPositionSystem(kv, ka);
var model = Models.elevatorFromSysId(kv, ka);
assertEquals(model.getA(), MatBuilder.fill(Nat.N2(), Nat.N2(), 0, 1, 0, -kv / ka));
assertEquals(model.getB(), VecBuilder.fill(0, 1 / ka));
}
@Test
void testIdentifyVelocitySystem() {
// By controls engineering in frc,
// V = kv * velocity + ka * acceleration
// x-dot = -kv/ka * v + 1/ka \cdot V
void testSingleJointedArmFromSysId() {
var kv = 1.0;
var ka = 0.5;
var model = LinearSystemId.identifyVelocitySystem(kv, ka);
assertEquals(model.getA(), VecBuilder.fill(-kv / ka));
assertEquals(model.getB(), VecBuilder.fill(1 / ka));
var model = Models.singleJointedArmFromSysId(kv, ka);
assertTrue(
model.getA().isEqual(MatBuilder.fill(Nat.N2(), Nat.N2(), 0.0, 1.0, 0.0, -kv / ka), 0.001));
assertTrue(model.getB().isEqual(MatBuilder.fill(Nat.N2(), Nat.N1(), 0.0, 1.0 / ka), 0.001));
}
}

View File

@@ -2,13 +2,13 @@
// 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.
package org.wpilib.math.system.plant.proto;
package org.wpilib.math.system.proto;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.wpilib.math.proto.Plant.ProtobufDCMotor;
import org.wpilib.math.system.plant.DCMotor;
import org.wpilib.math.proto.System.ProtobufDCMotor;
import org.wpilib.math.system.DCMotor;
class DCMotorProtoTest {
private static final DCMotor DATA = new DCMotor(1.91, 19.1, 1.74, 1.74, 22.9, 3);

View File

@@ -2,14 +2,14 @@
// 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.
package org.wpilib.math.system.plant.struct;
package org.wpilib.math.system.struct;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.junit.jupiter.api.Test;
import org.wpilib.math.system.plant.DCMotor;
import org.wpilib.math.system.DCMotor;
class DCMotorStructTest {
private static final DCMotor DATA = new DCMotor(1.91, 19.1, 1.74, 1.74, 22.9, 3);