Skip to content

REVLib - SparkSim does not work #284

@DylanB5402

Description

@DylanB5402

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:

Image

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions