I submitted this to REV support via email last week, also filing an issue here as I was asked to:
I've noticed this on REVLib Java 2027.0.0-alpha-2 and REVLib Java 2027.0.0-alpha-3
I've been experimenting with REVLib in sim using the 2027 WPILib Alpha, and I think I've noticed a small bug that's breaking the SparkSim class:
Spark Maxes are constructed as Sim Devices with a name like SPARK MAX [bus id, device id]. This is how I see them on my SimGUI:
However, the SparkSim constructor constructs SimDeviceSim with only the device id.
m_deviceName = deviceType + " [" + spark.getDeviceId() + "]";
SimDeviceSim sparkSim = new SimDeviceSim(m_deviceName);
m_appliedOutput = sparkSim.getDouble("Applied Output");
As such, attempting to access any data from the SparkSim object (ex: sparkSim.getAppliedOutput()), returns a null pointer exception due to all the SimDoubles (m_appliedOutput) being null.
Also adding the code I was trying to use for reference:
public class DrivetrainSim {
private SparkMaxSim leftSim, rightSim;
private final SparkMax leftSpark, rightSpark;
private final double kGearRatio = 10.71;
private final double kWheelRadiusMeters = 0.0762; // 3 inches
private static final double kBusVoltage = 12.0;
private final DifferentialDrivetrainSim m_driveSim =
new DifferentialDrivetrainSim(
DCMotor.getNEO(2), // 2 NEO motors on each side of the drivetrain.
kGearRatio,
2.1, // MOI of 2.1 kg m^2 (from CAD model).
26.5, // Mass of the robot is 26.5 kg.
kWheelRadiusMeters, // Robot uses 3" radius (6" diameter) wheels.
0.546, // Distance between wheels in meters.
null);
private final StructPublisher<Pose2d> simPosePublisher =
NetworkTableInstance.getDefault()
.getStructTopic("SimPose", Pose2d.struct)
.publish();
/**
* Creates a drivetrain simulation that bridges the REV SparkMax motor controllers and the
* Systemcore onboard IMU with WPILib's {@link DifferentialDrivetrainSim} physics model.
*
* @param leftSpark the left-side SparkMax motor controller
* @param rightSpark the right-side SparkMax motor controller
* @param imu the Systemcore onboard IMU
*/
public DrivetrainSim(SparkMax leftSpark, SparkMax rightSpark) {
this.leftSpark = leftSpark;
this.rightSpark = rightSpark;
}
/**
* Call once after construction. SparkMax inversion/orientation is handled through motor controller
* configuration (e.g. {@link com.revrobotics.spark.config.SparkBaseConfig#inverted}) rather than
* in simulation setup.
*/
public void init() {
this.leftSim = new SparkMaxSim(leftSpark, DCMotor.getNEO(2));
this.rightSim = new SparkMaxSim(rightSpark, DCMotor.getNEO(2));
}
/**
* Call every 20ms from {@code simulationPeriodic()}. Reads the applied output from each
* SparkMaxSim (one-tick delayed from the previous iterate), feeds the resulting motor voltage into
* the physics model, then writes the resulting velocity and position back to the SparkMaxSim and
* the heading to the OnboardIMU simulation.
*/
public void periodic() {
leftSim.setBusVoltage(kBusVoltage);
rightSim.setBusVoltage(kBusVoltage);
double leftMotorVoltage = leftSim.getAppliedOutput() * kBusVoltage;
double rightMotorVoltage = rightSim.getAppliedOutput() * kBusVoltage;
m_driveSim.setInputs(leftMotorVoltage, rightMotorVoltage);
m_driveSim.update(0.02);
double leftRPM = metersPerSecToRPM(m_driveSim.getLeftVelocity());
double rightRPM = metersPerSecToRPM(m_driveSim.getRightVelocity());
leftSim.iterate(leftRPM, kBusVoltage, 0.02);
rightSim.iterate(rightRPM, kBusVoltage, 0.02);
OnboardIMUSim.setYaw(m_driveSim.getHeading().getRadians());
simPosePublisher.set(m_driveSim.getPose());
}
/**
* Converts a linear wheel velocity (m/s) into motor RPM. Uses the same wheel radius and gearing
* ratio as the physics simulation. The SparkMax relative encoder defaults to a velocity
* conversion factor of 1 (RPM); if a custom factor is configured, this method must be updated
* accordingly.
*/
private double metersPerSecToRPM(double metersPerSecond) {
return metersPerSecond / kWheelRadiusMeters * kGearRatio * 60.0 / (2.0 * Math.PI);
}
}
Error for reference:
********** Robot program startup complete **********
Error at com.revrobotics.spark.SparkSim.setBusVoltage(SparkSim.java:502): Unhandled exception: java.lang.NullPointerException: Cannot invoke "org.wpilib.hardware.hal.SimDouble.set(double)" because "this.m_busVoltage" is null
at com.revrobotics.spark.SparkSim.setBusVoltage(SparkSim.java:502)
at first.robot.simulation.DrivetrainSim.periodic(DrivetrainSim.java:76)
at first.robot.Robot.simulationPeriodic(Robot.java:74)
at org.wpilib.framework.OpModeRobot.loopFunc(OpModeRobot.java:756)
at org.wpilib.internal.PeriodicPriorityQueue.runCallbacks(PeriodicPriorityQueue.java:176)
at org.wpilib.framework.OpModeRobot.startCompetition(OpModeRobot.java:786)
at org.wpilib.framework.RobotBase.runRobot(RobotBase.java:359)
at org.wpilib.framework.RobotBase.lambda$startRobot$0(RobotBase.java:428)
at java.base/java.lang.Thread.run(Thread.java:1474)
Warning at org.wpilib.driverstation.DriverStationErrors.reportWarning(DriverStationErrors.java:40): 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 org.wpilib.driverstation.DriverStationErrors.reportError(DriverStationErrors.java:20): The startCompetition() method (or methods called by it) should have handled the exception above.
NT: Got a NT4 connection from 127.0.0.1 port 54076
I submitted this to REV support via email last week, also filing an issue here as I was asked to:
I've noticed this on REVLib Java 2027.0.0-alpha-2 and REVLib Java 2027.0.0-alpha-3
I've been experimenting with REVLib in sim using the 2027 WPILib Alpha, and I think I've noticed a small bug that's breaking the SparkSim class:
Spark Maxes are constructed as Sim Devices with a name like SPARK MAX [bus id, device id]. This is how I see them on my SimGUI:
However, the SparkSim constructor constructs SimDeviceSim with only the device id.
m_deviceName = deviceType + " [" + spark.getDeviceId() + "]";
SimDeviceSim sparkSim = new SimDeviceSim(m_deviceName);
m_appliedOutput = sparkSim.getDouble("Applied Output");
As such, attempting to access any data from the SparkSim object (ex: sparkSim.getAppliedOutput()), returns a null pointer exception due to all the SimDoubles (m_appliedOutput) being null.
Also adding the code I was trying to use for reference:
Error for reference: