I am using a magnetic rotary encoder(AS5048B) to detect the rotation angle of a motor shaft. This is fed to a pid loop to calculate the PWM value for motor position control. The encoder has a 14Bit output. It gives an output of 0 - 16383 which is then right shifted by 4 to get a range of 0 - 1023.

When we give values closer to 1023 the sensor value goes back to 0 after a complete rotation and the pid error goes to huge values resulting in unstable motor movements. Suppose my current position is 10 and setpoint is 1023. The error is 1023 - 10 = 1013 and the motor starts to move towards 1023 to decrease error. When it reaches 1023 sometimes it overshoots and goes back to a value of 0 which gives us a wrong PWM value of 1023(1023 - 0). Is there any workaround for this issue?

Implementation details:- The experimentation setup consists of a 60RPM geared DC motor, an H bridge(VNH5019) and a magnetic rotary encoder(AS5048B). The H bridge has a PWM input for speed control and 2 other pins for direction control. It is an absolute encoder which gives a 14Bit output corresponding to 0 - 360-degree rotation of the motor shaft and the same act as a feedback signal for the plant. PID update function is written as a periodic timer interrupts with 10KHz frequency. Inside the ISR I read the setpoint from a rotary encoder and the feedback signal from the magnetic encoder. An error is then calculated, corresponding PWM values are generated and sent to the H bridge IC for changing the motor shaft position for that error. Because of the nature of the PID loops the motor starts rotating and when the error approaches zero the PWM value also approaches 0, thus stopping the motor at the set-point. My plan is to make this motor as a 0 - 359-degree position servo for normal applications (and 0 - 180-degree servo for hobby servo applications in the future).

I tried with a P only controller. It works fine at all the positions except the overflow point. Since the error is calculated as the difference between setpoint and input, an overflow makes the error value extremely high. The motor thinks that it needs to apply that much of PWM(which decreases as the error approaches 0) to reach that position. The net result is a motor which goes from 0 - 359 degrees (when the setpoint is a value corresponding to 359 degrees), overflows by 1 degree and goes back to 359, thus repeating the process infinite times.


Use modulo arithmetic.

// Pseudo-code
// error
error = (setPoint + 512 - currPos) % 1024 - 512;

This code shifts the setpoint from {0 .. 1023} to {512 .. 1535}, subtracts the actual position and gets the modulus. The answer will be in the range 0 - 1023 but will have a positive offset of 512 which is subtracted out. The resultant error will be in the range {-512 .. 511}.

Suppose my current position is 10 and setpoint is 1023. The error is 1023 - 10 = 1013 ...

// Calculation
error = (1023 + 512 - 10) % 1024 - 512
      = 1525 % 1024 - 512
      = 501 - 512
      = -11

Update 1.

It seems that you want the geared output to run from 0° to 360° but that the assembly could overshoot slightly at 360° and, I presume, at 0°. The only way to handle this with your proportional control is to convert your encoder in software into a multi-turn encoder. This means keeping track of your revolutions and allowing your "total" value to run from, say, -90° to +450°.

enter image description here

Figure 1. Flowchart for 0° transition testing.

|improve this answer|||||
  • \$\begingroup\$ This is a good option but when the setpoint is 1023 and the current position is 10 then I want the motor to move 1013 counts in Clock Wise direction. From your suggestion, it reaches the setpoint but the direction is Counter Clock Wise. \$\endgroup\$ – Supreeth Dec 4 '18 at 11:10
  • \$\begingroup\$ Is the motor supposed to always move the same direction and take the long way around on a large setpoint change? \$\endgroup\$ – Transistor Dec 4 '18 at 11:16
  • \$\begingroup\$ Actually, I am planning to implement a configurable position control motor with 0 - 360 or 0 -180 depending on the application. If I handle the overflow somehow then I don't need to take care of setting the zero position for a particular encoder. \$\endgroup\$ – Supreeth Dec 4 '18 at 11:21
  • \$\begingroup\$ I think an edit to your question is required. If PWM sets the motor position then there is something else in the control loop that you haven't explained. Give some context to the problem so that we can understand the real problem you're trying to solve. \$\endgroup\$ – Transistor Dec 4 '18 at 18:10
  • \$\begingroup\$ @Transisor I have edited the post with implementation details for more clarity \$\endgroup\$ – Supreeth Dec 5 '18 at 4:36

A work-around that I use all the time is to ignore the actual encoder value in my process. Rather, I poll the encoder value every x microseconds (or milliseconds, or whatever), and determine the difference in count from the last scan, and add that to my actual count register.
Just determine the difference every scan using signed integer mathematics. In your case, I would only look at the lower 8 bits of the encoder value as a signed byte.

|improve this answer|||||
  • \$\begingroup\$ That works for finding the difference in position between readings. OP is looking for the position error. \$\endgroup\$ – Transistor Dec 4 '18 at 11:05
  • \$\begingroup\$ I may have misread the question to some extent so your answer may be useful. Let's see if any clarification comes. \$\endgroup\$ – Transistor Dec 4 '18 at 11:21
  • \$\begingroup\$ This assumes an absolute encoder, of course. \$\endgroup\$ – Scott Seidman Dec 5 '18 at 13:29
  • \$\begingroup\$ Absolute or incremental, makes no difference. \$\endgroup\$ – R Drast Dec 6 '18 at 10:04

Your Answer

