Encoder decoding factors & the Talon SRX

So I’m confused about the decoding factors. So if I wire up an encoder to the roboRIO and decide to program with Java, then I need to use the Encoder class from wpilibj. However, I don’t understand decoding factors. So if you instantiate an encoder, if you don’t specify the decoding factor (like 1X, 2X, or 4X), then the encoder, by default, uses 4X decoding. If this post is too long, then please bare with me, because I’m so confused :confused: :confused: :confused:

So if I use a PG71 motor and I use the encoder that comes with the PG71, the encoder makes 497 counts per revolution of the shaft at the end of the gearbox. Let’s say that I don’t specify the decoding factor when I instantiate it and, as a result, the encoder uses the default decoding factor (which is 4X). And I choose the unit for distance as a revolution of the shaft at the end of the PG71 gearbox. My understanding of the decoding factor is that each of the “counts” in the 497 counts/rev is really an encoder cycle, so if decoding factor is 4X, then all 4 edges of each encoder cycle is counted, and so the actual number of counts/rev is 497 x 4 = 1988 counts/rev. So, when I use setDistancePerPulse(), do I do setDistancePerPulse(1/497)? or do I do setDistancePerPulse(1/1988)? Does changing the decoding factor affect the double that you put in the setDistancePerPulse() method?

Also, what’s the difference between the get() and getRaw() methods? Does getRaw() only get you the number of encoder cycles, and get() will get the actual counts by multiplying the number of encoder cycles by the decoding factor?

Now, as for programming an encoder when it’s wired to a Talon SRX. The Talon SRX always does 4X decoding, right? And let’s say that I use the set(ControlMode mode, double value) method. Let’s say that I, again, use the PG71 motor, and I wire the encoder from the PG71 to the Talon SRX, and that the control mode I wanna use is Position, so the output value is in encoder “ticks”. If I want to make the shaft at the end of the PG71 motor turn 1 full revolution, and the Talon SRX always does 4X decoding, do I do set(ControlMode.Position, 497) or set(ControlMode.Position, 1988)?

Also, can I use a Talon SRX with an encoder wired to it without specifying a PID loop and the PID gains? By that, I mean that can I do something like set(ControlMode.Position, 497) and when I run it, the PG71 will turn until 497 encoder ticks has been made and that in the event that it overshoots, it will rotate backwards until the encoder ticks go to 497 when I don’t specify any PID loop and PID gains whatsoever?

Don’t loose hope, you can learn!

Quadrature encoders produce two square waves, offset by a “phase angle” of 90 degrees.

“Decoding Factor” refers to the fact that in every period of a quadrature encoder has four edges you could potentially count:

For most FRC applications, choose 4x. Then, any time you convert from “encoder edges” to some other unit, remember there are 4 counts per period.

Now, as you mentioned, manufactures will publish some info on what the encoder resolution is. I have seen “counts” be ambiguous sometimes. I’ve seen datasheets both were it refers to the number of periods per revolution or number of edges per revolution. I’m not 100% sure which it is for the PG71. However, you know you will either be correct or off by a factor of 4. Historically, when setting setDistancePerPulse(), we’ve just figured it out by trial and error.

FYI, the following documentation for setDistancePerPulse() is provided in a recent version of wpilibj:

   * Set the distance per pulse for this encoder. This sets the multiplier used to determine the
   * distance driven based on the count value from the encoder. Do not include the decoding type in
   * this scale. The library already compensates for the decoding type. Set this value based on the
   * encoder's rated Pulses per Revolution and factor in gearing reductions following the encoder
* shaft. This distance can be in any units you like, linear or angular.

Per the code comments in a recent wpilibj commit, the following functions return:

getRaw() - encoder edge count
get() - period count (edges, but accounts for decoding factor)
getDistance() - meaningful unit (accounts for setDistancePerPulse)

For the talon, it uses separate encoder processing from wpilib content, so assumptions will not necessarily transfer. I think the behavior depends on the firmware version of the SRX. I recall in 2018 we ended up doing some conversion for velocity units in our code (SRX had a per/100ms factor baked in to their desired units, I think). I haven’t done position control on these however, someone else would be better to answer with experience.

As to your final PID gains spec, unfortuantely the answer is no, today. What you’re describing is a sort of bang-bang controller (full command to push the actuator toward the setpoint at all times). This is still a closed loop system, just with a different controller than a PID. As far as I know, right now, Talon SRX’s do not support bang-bang as the feedback controller. However…

A bang-bang controller as you describe is actually a PID with gains
I = 0
D = 0

So indirectly yes, you can do what you’re asking (since I have specified the PID gains, you don’t have to :)).

Thanks. That help cleared up some confusion. So I need to keep in mind whether the counts or distance are off by a factor of 4 if I’m using get(), getRaw(), getDistance(), and setDistancePerPulse(), right? And also, since your team historically had some trial and error every time they used setDistancePerPulse(), my team will also go through some trial and error with setDistancePerPulse() and figure out which ratio that’s off by a factor of 4 to use, if we decide to use wpilibj and wire the encoder to the roboRIO.

Judging by the comments on setDistancePerPulse(), though, which said “Do not include the encoding type,” I guess that if I use “revolution” as the unit for “distance” and I use a PG71, then I would need to do setDistancePerPulse(1/497) rather than setDistancePerPulse(1/1988) because the former doesn’t include the encoding type. That’s only my guess, though.

Your comments about the difference between get() and getRaw() are super helpful, btw. I have a much better idea of what the difference between the 2 methods are now. So getRaw() is basically the number of periods from get() multiplied by the decoding type, right? So if we use the default decoding type (4X), then the value we get from getRaw() is basically the number of periods multiplied by 4, right?

Also, as for programming an encoder that’s connected to a Talon SRX and PID gains, since you said that the Talon SRX probably doesn’t support that kind “bang-bang” control, that would mean that I need to specify the PID gains and a PID loop, right? My team is preparing to test an encoder on a PG188 and we’re planning to wire the encoder to a Talon SRX. You really saved us time on figuring out whether we need to specify PID gains or a PID loop.

Thanks for the help :D! None of the programmers on my team know what an encoder is until 2 weeks ago. So, we’re a bit uninformed on encoders, to say the least. Your comments are sorely needed

No prob, glad to help! The nuances of working with encoders are often glossed over in documentation, so glad this brought some clarity.

I too would guess that a setting of setDistancePerPulse(1/497) should get ya to be measuring number of gearbox output shaft resolutions. Furthermore, reading a bit into andymark’s documentation, the comment of “pulses per revolution” implies each signal (A or B by themselves) will output 497 pulses per output shaft rotation. That means there will also be 497 total electrical periods (or 1988 edges) per revolution.

As far as software terminology goes, the “Raw” portion of the function name generally implies you’re reading values straight from the hardware with no modification to make the value more useful. For this reason, it makes sense that the “Raw” value wouldn’t account for encoding, under the presumption that the encoder reading hardware (inside the FPGA) is just counting edges.

For the SRX, yea, program it just like you’re setting up a normal PID. Note that the trick of “Just use giant P” works only because the system under control isn’t very sensitive to inputs. What “Sensitive to input” means can get a bit harry (mathematically speaking). A rule of thumb is is that if hooking a full 12V up to the motor for more than a second or two causes bad things to happen, you probably need something a bit more nuanced than bang-bang control. For example, our elevator this year had a top-to-bottom traversal time of about 7 seconds while loaded. Bang-bang worked great for it. 254’s elevator (and many others) had a traversal time closer to 1 second. Bang Bang would have ended very poorly here. Of course, all this is much less about encoders and much more about just general closed-loop control.

One thing you may not have seen yet - to get the WPILib or SRX built-in closed loop algorithms to work, you have to get everything configured such that positive control effort (ie motor command values > 0) cause the sensor value to increase. SRX provides some API’s to invert the sensor read direction (“Phase”, I think is what they call it?). I recommend checking this before running closed loop by just commanding motor values and seeing which way the sensor value changes. If this is backward, your system will rail the output command at the opposite value of what it should be and keep it there till you disable.