1
\$\begingroup\$

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. enter image description here

enter image description here

\$\endgroup\$
  • 1
    \$\begingroup\$ Analog circuitry or microcontroller? Resolution 10° or 0.1°? Datasheet link for the pots? How do they transition from 100 to 0 without a deadspot? Why do you need to use both pots? You can determine position from either one on its own. \$\endgroup\$ – Transistor Nov 20 '19 at 21:29
  • \$\begingroup\$ I think I might have misinterpreted your question. What do you mean by "continuous angle"? Do you mean an angle that wraps around between 0 and 360? Or an angle that can count up towards infinite (which is what I interpreted it as). Well, I suppose my answer is still relevant either way since you need to be able to calculate the distance between two points for the shortest path around the circle in both cases. \$\endgroup\$ – DKNguyen Nov 20 '19 at 22:12
  • \$\begingroup\$ @Transistor resolution of 0.1 degrees, the pots are part of a steer by wire sensor. I will update the main thread to contain the datasheet. \$\endgroup\$ – Akash Hegde Nov 22 '19 at 13:32
  • \$\begingroup\$ @DKNguyen I mean an angle that can count up to infinity \$\endgroup\$ – Akash Hegde Nov 22 '19 at 13:32
5
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ Once again Spehro beats me to the Best Answer. \$\endgroup\$ – TimWescott Nov 20 '19 at 22:16
  • \$\begingroup\$ That is similar to my current approach, however it is after completing a full rotation where I am having problems. I check one sensor to see if it has completed a full cycle by check the duty cycle and if it passes 95%. When it does cross one full cycle, I have it multiply my angle scaling function by 1,2,3,4,etc so after one rotation my function = 360*1, after two rotations I have 360*2, etc. The problem appears due to imperfections in the pots, while one pot will read 95, the other should read 50 but it really floats around 51-49. So what will happen is my angle will jump unexpectedly \$\endgroup\$ – Akash Hegde Nov 22 '19 at 13:44
  • \$\begingroup\$ Still a helpful answer, ill reduce my center range to 50% +-22.5. I just need to eliminate any errors at discontinuities as this sensor is used for a steer by wire application. \$\endgroup\$ – Akash Hegde Nov 22 '19 at 13:46
3
\$\begingroup\$

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:

  1. Between two points on a circle, you can always take two paths between the points. Which is the one the rotation actually took?
  2. 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.

\$\endgroup\$
  • 1
    \$\begingroup\$ Tip of the day: SE supports HTML entities in the posts (but not in the comments). Try °, Ω, μ, ± and try guessing anything else you need. \$\endgroup\$ – Transistor Nov 20 '19 at 21:46
  • \$\begingroup\$ We were investigating using modulus math, taking the mod 10 of our angle so we get a value repeating from 0-9, but we ran into the same problem at the discontinuity when you get to 9. Our controller is limited in what can be coded. This "shortest path" implementation is interesting and ill try and give it a shot. Thanks! \$\endgroup\$ – Akash Hegde Nov 22 '19 at 13:51
3
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ Never thought of using complex numbers for mapping, I'll try this approach out as well. Thanks! \$\endgroup\$ – Akash Hegde Nov 22 '19 at 13:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.