SparkMaxSim applying 0 output?

I’ve updated to 2025 robotpy and was pleasantly surprised to see the SparkSim class added. However, my code using the SparkSim doesn’t work. The simulated Spark sees its position, velocity, and setpoint, but always applies 0 output.
The code in question is at FRC2429_2025/other_robots/crank_rewritten/robot at main · aesatchien/FRC2429_2025 · GitHub
Please let me know if you have any questions.

In your PID settings you set P to 0. Nudge that up a bit and try again. Line 84 in the slarksim file.

I’m not using that sparksim (it’s “commented” out as a string literal) but rather the revlib sparksim. I set kP to 6 (a value that worked with 2024 revlib in real life) in line 28 of the lower_crank subsystem and line 9 of constants.py

I was messing with simulation in Java, and by my understanding of the documentation you need to call SparkMaxSim.iterate(double, double, double) in your periodic (or the python equivalent).

One of those parameters in the velocity of the motor based on the physics simulation. This is where I got a bit stuck, since you need the motor output to drive the physics sim. I did see motor output when forcing the physics sim, so this seems to be the right track.

I am calling that method. You don’t seem to strictly need the motor output, since some physics sims will settle due to simulated gravity, but I’m not sure what you mean.
Could you elaborate on “I did see motor output when forcing the physics sim”?
When I call

        self.arm_sim.setInput(0, 1) #self.arm_spark_sim.getAppliedOutput() * wpilib.RobotController.getInputVoltage())

bypassing the sparksim and giving the SingleJointedArmSim a constant input of 1 volt, the armsim does move, but the applied outpuet field of the sparkmax display on the other devices panel remains 0. Is this what you did?

Ah I was looking in the wrong file (subsystem periodic)

Yeah that’s exactly what I would do, but probably with 6 or 12V (it’s been a hot minute)

    @Override
    public void simulationPeriodic() {
        // I would set this to some voltage
        armSim.setInputVoltage(motorSim.getAppliedOutput() * motorSim.getBusVoltage());
        armSim.update(0.02);
        // Rad/s to RPM, and times gear ratio
        motorSim.iterate(armSim.getVelocityRadPerSec() * 9.549297 * 120, motorSim.getBusVoltage(), 0.02);
        
        // Tell the pot it's at the arm's angle
        potSim.setPosition(convertAngleToVolts(Math.toDegrees(armSim.getAngleRads())));
    }

It’s possible that the higher voltage had some effect? Once it was moving I could set the input volts back to the applied output and it would move a bit farther before settling. It’s been long enough that I don’t remember if the spark’s output volts matched the applied, but it was definitely showing signs of life in glass.

I tried

    def update_sim(self, now, tm_diff):

        print(f"voltage: {wpilib.RobotController.getInputVoltage()}")
        print(f"now: {now}")
        if now > 1:
            self.arm_sim.setInput(0, self.arm_spark_sim.getAppliedOutput() * wpilib.RobotController.getInputVoltage())
        else:
            self.arm_sim.setInput(0, 12)

        self.arm_sim.update(tm_diff)

        wpilib.simulation.RoboRioSim.setVInVoltage(
                wpilib.simulation.BatterySim.calculate([self.arm_sim.getCurrentDraw()])
        )

        voltage = wpilib.simulation.RoboRioSim.getVInVoltage()
        print(f"rio sim voltage: {wpilib.simulation.RoboRioSim.getVInVoltage()}")
        
        # self.arm_encoder_sim.setPosition(self.arm_sim.getAngle())
        # self.arm_spark_sim.setPosition(self.arm_sim.getAngle())
        self.arm_spark_sim.iterate(math.radians(self.arm_sim.getVelocityDps()), voltage, tm_diff)
        self.robot.container.lower_crank.set_encoder_position(self.arm_sim.getAngle())

        self.crank_mech2d.setAngle(math.degrees(self.arm_sim.getAngle()))

        # l_encoder = self.drivetrain.wheelSpeeds.left * tm_diff

to set a bypassed voltage and then “set the input volts back to the applied output”, as you said worked, but no luck- applied voltage remained 0…

Did you enable via the driver station. I haven’t tried revsim but for CTRE you need to enable in the driver station even in simulation to be able to get voltage

yup

I just double-checked, and even at 1V I do see movement

However, the AppliedOutput is very small (like <0.5V), which could be either due to the very over-geared system or a mistake on my part.

Here’s a gif where I override and then switch back around 45 deg. The arm continues until the top of the arc, even though the setpoint is still at 0 deg. (If I switch after it passes the arc, it’ll go back up and stop in the same place). It feels like it’s just damping out what velocity it has before stopping again.

ArmSim

This all makes me think we’re missing something. Hopefully the 2025 docs update will have more examples of how to use simulation, since so far I’m out of guesses.

1 Like

That’s what I get for reading this on a phone while Xmas shopping :slight_smile:

2 Likes

Simulation (the SparkSim::iterate method specifically) is currently broken in the C++ library. Robotpy-rev wraps the C++ library, java uses a native Java implementation that does not suffer from the bug.

I’ve posted the issue to rev’s binaries git repo, hopefully they’re able to fix it soon.

1 Like

I’ve found another bug - the configured velocityConversionFactor is not applied in absolute encoder mode (as with the turn motors of a swerve module). As a workaround you can configure both the encoder and absolute encoder velocity and position conversion factors during simulation.