The TLDR for this topic is quick and simple:
When possible, you should probably be running your PID loops within your motor controller
But there is a nice story behind the previous statement which lead to some interesting discoveries which I will share here.
1741’s students this past season decided to go with a swerve drivetrain, which wasn’t new terrain for us, but was certainly a rusty skill as it had been a few years and COVID since we last had a swerve bot. The students landed on the SDS MK4i modules and decided to use a Falcon500 for the drive motor and a NEO + SRX Mag Encoder for the turn motor.
At the beginning of the season, the goal was to run all of our drivetrain’s PID loops within the motor controllers. As we all know, the RoboRIO is slow (50 Hz) compared to the PID loops which are running within the motor controllers (1000 Hz).
For this reason, we had the students create a custom ribbon cable to feed the Mag Encoder’s absolute signal (duty cycle signal) into the Spark Max. “Gravy,” we thought, “we should be able to run a PID loop within the Spark Max.” And then… Gotcha!! At the time, the Spark Max firmware didn’t support the absolute encoder through the data port in the way that we needed. (Shoutout to REV, who just recently pushed a firmware update “1.5.0” to add this capability) “Whatever,” we thought, “we can just get the value from the Spark Max in our Java code, and run the PID loop there.” “50 Hz isn’t that bad… right?”
And so we did. It was exciting to watch the students discover the idiosyncrasies of FRC swerve as they got the bot up and running, but at the end of the day, the bot never quite felt right. I had driven a swerve bot when I was a student, and our 2023 bot Tempest didn’t pass the “good swerve sniff test” per se. It wasn’t for a lack of trying though… In the background, this drove us 1741 programming mentors a little crazy. At one point, I sat down and rederived the matrix algebra within WPILib’s swerve calculations to make sure there wasn’t some small mistake. No dice… everything looked correct, and everything that we tried didn’t really help the swerve drive to feel better. At this point, we gave up and accepted the state of the swerve drive.
Fast forward the story to now, in the offseason. I was doing some unrelated research into the FRC CAN bus to look at monitoring the bus and potentially controlling the brushless motors controllers via a Raspberry Pi. Regardless, I landed on this page of the Spark Max documentation which rocked my world:
The absolute encoder position was only being communicated to the RoboRIO every 200ms!! 5 Hz!!!
All it took was one line of code, and our swerve drive felt incomprehensibly better:
m_turnMotor.setPeriodicFrameRate(PeriodicFrame.kStatus5, 20);
This line of code forces the Spark Max to send the absolute encoder position to the RoboRIO every 20ms, which means that PID loop within the RIO will be able to pull up-to-date encoder values each cycle rather than only getting an updated value every 10 cycles.
I’m aware that this is a very niche issue (the perfect storm was required to cause this one), but I hope that you learned some stuff and don’t make the same mistake we made.
Do you have any related niche programming issues which plagued you this season?