Hello! I’m working on a project utilizing LQR to achieve accurate positions for Swerve Module azimuths. as a proof of concept that it could possibly be a better approach in season as opposed to my team’s tradition approach of PID control (Which cost us a lot of time this season). I’ve built my Module class around a pre-defined Linear System Loop. However, my error originates in constants, where each module is defined when the Modules are created.
Here’s what the Constants file which defines the Module(s) and where the error originates from looks like:
public static final class FL_Module {
public static final class Values {
public static final Double MAXIMUM_AZIMUTH_ACCELERATION = (Math.PI*2);
public static final Double AZIMUTH_ENCODER_POSITION_OFFSET = (-313.006);
public static final Double MAXIMUM_AZIMUTH_VOLTAGE = (12.0);
public static final Double MAXIMUM_AZIMUTH_VELOCITY = (5.4);
public static final Double MAXIMUM_LINEAR_VELOCITY = (5.4);
public static final Double ACCELERATION_GAIN = (1.0); //TODO <---- Find Acceleration Gain (kA) of Linear Position System, Volts/(Units/Sec^2), DO NOT DEPLOY THIS UNTIL FOUND
public static final Double VELOCITY_GAIN = (1.0); //TODO <---- Find Velocity Gain (kV) of Linear Position System, Volts/(Units/Sec), DO NOT DEPLOY THIS UNTIL FOUND
public static final Double LINEAR_ENCODER_SENSITIVITY = (2048.0);
public static final Double ENCODER_SENSITIVITY = (4096.0);
public static final Double MODEL_VELOCITY_ACCURACY_DEGREES = (7e-5);
public static final Double MODEL_POSITION_ACCURACY_RPM = (2.5e-4);
public static final Double DISCRETIZATION_TIMESTEP = (1/50.0);
public static final Integer AZIMUTH_ID = (21);
public static final Integer LINEAR_ID = (11);
public static final Integer SENSOR_ID = (4);
}
public static final class Components {
public static final WPI_CANCoder AZIMUTH_SENSOR = DrivebaseModule.configureRotationEncoder((new WPI_CANCoder(Values.SENSOR_ID)), Values.AZIMUTH_ENCODER_POSITION_OFFSET);
public static final WPI_TalonFX LINEAR_CONTROLLER = DrivebaseModule.configureTranslationController(new WPI_TalonFX(Values.LINEAR_ID), Constants.Values.ComponentData.DRIVE_CURRENT_LIMIT);
public static final WPI_TalonFX AZIMUTH_CONTROLLER = DrivebaseModule.configureRotationController(new WPI_TalonFX(Values.AZIMUTH_ID), AZIMUTH_SENSOR, Constants.Values.ComponentData.AZIMUTH_CURRENT_LIMIT, Constants.Values.ComponentData.AZIMUTH_DEADBAND);
public static final Supplier<SwerveModuleState> STATE_SENSOR = () -> new SwerveModuleState(
(2.0)*(((LINEAR_CONTROLLER.getSelectedSensorVelocity() / Values.LINEAR_ENCODER_SENSITIVITY) * (10)) / Constants.Values.Chassis.DRIVETRAIN_GEAR_RATIO) * Math.PI * Constants.Values.Chassis.WHEEL_DIAMETER,
new Rotation2d(AZIMUTH_SENSOR.getAbsolutePosition()));
public static final DrivebaseModule MODULE = new DrivebaseModule(new MotorControllerGroup(AZIMUTH_CONTROLLER), new MotorControllerGroup(AZIMUTH_CONTROLLER), STATE_SENSOR, () -> Values.MAXIMUM_LINEAR_VELOCITY, () -> Control.CONSTRAINTS, Control.CONTROL_LOOP);
}
public static final class Control {
public static final TrapezoidProfile.Constraints CONSTRAINTS = new TrapezoidProfile.Constraints(Values.MAXIMUM_AZIMUTH_VELOCITY, Values.MAXIMUM_AZIMUTH_ACCELERATION);
public static final LinearSystem<N2,N1,N1> PLANT = LinearSystemId.identifyPositionSystem(Values.VELOCITY_GAIN, Values.ACCELERATION_GAIN);
public static final LinearSystemLoop<N2,N1,N1> CONTROL_LOOP = new LinearSystemLoop<>(
PLANT,
new LinearQuadraticRegulator<>(
PLANT,
VecBuilder.fill(Units.degreesToRadians(Components.AZIMUTH_SENSOR.getAbsolutePosition()), Units.rotationsPerMinuteToRadiansPerSecond(Components.AZIMUTH_SENSOR.getVelocity())), //<---- Controller error weight, how much we want to conserve controller error, or how accurate we want to be, increased time to target
VecBuilder.fill(Values.MAXIMUM_AZIMUTH_VOLTAGE), //<---- Controller effort weight, how much we want to conserve controller expenditure or how fast we want to be; increased state error
Values.DISCRETIZATION_TIMESTEP),
new KalmanFilter<>(
Nat.N2(),
Nat.N1(),
PLANT,
VecBuilder.fill(Units.degreesToRadians(Values.MODEL_VELOCITY_ACCURACY_DEGREES), Units.rotationsPerMinuteToRadiansPerSecond(Values.MODEL_POSITION_ACCURACY_RPM)), //<---- How accurate we think our models are
VecBuilder.fill(Units.rotationsPerMinuteToRadiansPerSecond(1 / Values.ENCODER_SENSITIVITY)), //<---- How accurate we think our encoders are
Values.DISCRETIZATION_TIMESTEP),
Values.MAXIMUM_AZIMUTH_VOLTAGE,
Values.DISCRETIZATION_TIMESTEP);
}
}
For some additional context, I’m also trying to utilize Advantage Kit for the replay and logging features it has, but I can’t get much use out of it so far considering nothing can be logged if it crashed prior to logging starting.
This is what the console looks like:
********** Robot program starting **********
NT: Listening on NT3 port 1735, NT4 port 5810
DataLog: Logging to 'src\main\java\frc\deploy\logs\Log_f8b5b944bd570ad7.wpilog'
[phoenix] CANbus Connected: sim
[phoenix] CANbus Network Up: sim
CTR: CAN frame not received/too-stale.
CANCoder 4 GetAbsolutePosition
CTR: CAN frame not received/too-stale.
CANCoder 4 GetAbsolutePosition
abort: Failure at D:\a\allwpilib\allwpilib\wpimath\src\main\native\thirdparty\drake\src\math\discrete_algebraic_riccati_equation.cpp:417 in DiscreteAlgebraicRiccatiEquation(): condition 'is_approx_equal_abstol(Q, Q.transpose(), 1e-10)' failed.
My initial solution was to remove all references to special values such as Double.NaN and Infinity as per this: drake Namespace Reference, and Github Issue.
At some point during my debugging session, I was able to yield a different (Much more revealing) error, (With a stacktrace!):
********** Robot program starting **********
NT: Listening on NT3 port 1735, NT4 port 5810
DataLog: Logging to 'src\main\java\frc\deploy\logs\Log_9ed21a554a9f2c61.wpilog'
[phoenix] CANbus Connected: sim
[phoenix] CANbus Network Up: sim
CTR: CAN frame not received/too-stale.
CANCoder 4 GetAbsolutePosition
CTR: CAN frame not received/too-stale.
CANCoder 4 GetAbsolutePosition
Error at frc.robot.subsystems.drivebase.Constants$Hardware$Modules$FL_Module$Control.<clinit>(Constants.java:109): Unhandled exception: java.lang.RuntimeException: Failure at D:\a\allwpilib\allwpilib\wpimath\src\main\native\thirdparty\drake\src\math\discrete_algebraic_riccati_equation.cpp:420 in DiscreteAlgebraicRiccatiEquation(): condition 'es.eigenvalues()[i] >= 0' failed.
at edu.wpi.first.math.WPIMathJNI.discreteAlgebraicRiccatiEquation(Native Method)
at edu.wpi.first.math.Drake.discreteAlgebraicRiccatiEquation(Drake.java:24)
at edu.wpi.first.math.Drake.discreteAlgebraicRiccatiEquation(Drake.java:53)
at edu.wpi.first.math.controller.LinearQuadraticRegulator.<init>(LinearQuadraticRegulator.java:114)
at edu.wpi.first.math.controller.LinearQuadraticRegulator.<init>(LinearQuadraticRegulator.java:49)
at frc.robot.subsystems.drivebase.Constants$Hardware$Modules$FL_Module$Control.<clinit>(Constants.java:109)
at frc.robot.subsystems.drivebase.Constants$Hardware$Modules$FL_Module$Components.<clinit>(Constants.java:100)
at frc.robot.subsystems.drivebase.DrivebaseSubsystem.<clinit>(DrivebaseSubsystem.java:59)
at frc.robot.Constants$Subsystems.<clinit>(Constants.java:14)
at frc.robot.RobotContainer.<init>(RobotContainer.java:16)
at frc.robot.RobotContainer.getInstance(RobotContainer.java:33)
at frc.robot.Robot.robotInit(Robot.java:68)
at org.littletonrobotics.junction.LoggedRobot.startCompetition(LoggedRobot.java:59)
at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:358)
at edu.wpi.first.wpilibj.RobotBase.lambda$startRobot$0(RobotBase.java:431)
at java.base/java.lang.Thread.run(Thread.java:833)
Warning at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:373): The robot program quit unexpectedly. This is usually due to a code error.
The above stacktrace can help determine where the error occurred.
See https://wpilib.org/stacktrace for more information.
Error at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:380): The startCompetition() method (or methods called by it) should have handled the exception above.
[phoenix-diagnostics] Server shutdown cleanly. (dur:0)
[phoenix] Library shutdown cleanly
Unfortunately, I have since gone back to the original error message, which isn’t very helpful for debugging my system. Looking at those two issues since then again, I tried removing any references to negative Qelms and Relms, but I don’t actually have any when defining LinearSystemLoop or in my Constants (Where the modules are defined) as a whole. The only place they could appear is getting simulated encoder Absolute values, or From Joystick Inputs (Which wouldn’t make sense because the Joystick isn’t reference yet or even plugged in for that matter).
Therefore, as my last option and not making any progress for the last 3 hours, I have posted here. Anybody have any suggestions? Is this just limited to Simulated code because of something to do with the JNI?