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.
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.
That’s what I get for reading this on a phone while Xmas shopping
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.
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.