I am trying to generate a continuous angle based on the output of the two potentiometers that are inside my device. The pots are shifted by 180 degrees and are capable of spinning continuously. How can I use the signals from both pots to determine direction while also getting a continuous angle function? I understand how to do this with rotary encoders but am having difficulties doing the same with this device.
There will be a gap in each of the two pots near where they (ideally) instantaneously switch from 95% to 5% or vice-versa depending on the direction of rotation.
I suggest using the center range of each- switching from one to the other when the current one goes out of the center of the range, in this case it would be 50% +/- 22.5%. There will tend to be a smallish glitch at the transition. If you need to eliminate that you could blend the two readings together with a function that weights the two readings in a variable fashion. You still want to ignore the output that is too close to the transition, but to blend the two inputs in the intermediate range.
You don't need two potentiometers to determine direction. These aren't quadrature encoders. The reason you need two signals from a quadrature encoder (which are 90° out of phase, not 180°) is because they output pulses which do not carry any inherent position information.
Potentiometers outright give their absolute position every time you take a reading from them. You only need one pot to determine direction. Just compare the current position with the previous position.
As for tracking direction and cumulative changes in angle, "just" calculate the difference in angle between the present and past position and add it into a counter that keeps track of angular displacement.
This sounds pretty simple and if you knew what you were doing then everything I have already said makes sense. Where you are getting stuck is probably the next part: Actually calculating the difference in angle between present and past position in a way that reflects the actual direction of rotation.
There are two difficulties here:
- Between two points on a circle, you can always take two paths between the points. Which is the one the rotation actually took?
- Even if you knew which path to pick between the two points, how do you calculate the distance between the two points if the path crosses the "wraparound point" (i.e. zero degrees, 360 degrees, etc.)
Assuming that you are sampling fast enough relative to rotational speed that the shortest path around the circle is indeed equivalent to the movement between previous and present angular position, then the shortest path of the two paths is always the one representative of the angle that was traversed.
Therefore, you will need code that decides which way around the circle is shortest. The most straightforward way to do this is to literally calculate the distance of both paths (one of which will always cross through zero), and compare the two.
Calculating the path between two points around a circle is easy if the path does not cross through the wraparound angle. You just subtract their angular positions. But it is trickier if the path does cross over the wraparound point.
Whaty you do is find the distance from one point to the zero, then find the distance from the other point to the zero and add those distances together. Of course, when finding these two distances between each point to zero you also pick the shortest path around the circle since, they make up the shortest path around the circle between those two points. In this case, it is easy to know which is the shortest path since you can just compare the angular position to 180 degrees and you always know which direction is the shortest path to zero.
It's tricky, I know. But really, what you need to do is sit down with a pencil and paper, draw a few circles with two points on them in different scenarios (shortest path does not cross through zero, shortest path does cross through zero, etc) and work out each case. Then you write branching code to handle each case.
You can also use modulus math for a mathematically rigorous solution, but it is much more abstract.
What I ultimately did in my code, was I wrote a library that made classes of a "wrapping integer type" with operator overloading where each integer had three values: it's actual value, maximum value, and a minimum value (that is often, but not always zero for the sake of generality). That formed a circle of numbers that the actual value traveled around as you added to it or subtracted from it causing it to overflow or underflow. The subtraction in this case was made to always return the length of the shortest path. Both addition and subtraction also wrapped around the result if required. I was using those operations a lot in my code and it was unwieldly enough that and did not want to have distinct code every time it popped up. That would have been unmanageable.
EDIT: If you want to combine the two potentiometers so that the deadpoint around zero is ignored, it's easy once you have the above figured out. Just pick the encoder where the shortest path between the past and present point does not cross through zero and calculate the length of that path. Then add it to a counter. You can choose whether you want the counter to wraparound between 0 and 360 or to count to infinite. Or you can just calculate the distances for both potentiometers and average them before adding them to the counter, rejecting one of the pots if either its present or past position was too close to zero.
Part of the problem seems to be the 'unwrapping' of the phase information. The other part perhaps merging the readings from the two pots.
Assuming the two pots are read by ADCs, and then the results processed in an MCU, the following may at first seem a bit heavy-handed and slow, but it's conceptually rigorous.
A good representation for angle is a complex number. It can be rotated continuously, without having any discontinuity at the +/- 180 point.
I suggest then first mapping the potentiometer readings to complex numbers. This would involve a scaling and a sine and cosine lookup. In order to merge the two readings really neatly, one could reduce the magnitude of the complex number when it was close to the extremes of the potentiometer reading. Say have unity magnitude in the middle 50% of the range, then taper to zero magnitude at the ends. The two angle readings are now combined by averaging. The resultant vector will be more strongly weighted by the pot in the middle of its range, and the final reading will transition smoothly from one to the other, with no discontinuities.
You could probably achieve the same sort of result with simple angle processing, if then elses, and interpolation, with fewer machine cycles than involved in the sin/cos generation, but the complex route is clearer and less prone to error, and IMHO somewhat cooler.