# PID control brushed motors via PWM using Encoder Clicks at SetPoints/Measurements

I've read numerous posts (1 , 2, 3 ) about using PID to control motors. All examples I've seen deal with the control signal being in the same units as the measurement signal. For example, use PID to set PWM values and measure PWM values as feedback or read stepper motor values in encoder clicks and then set new motor values also in clicks.

What I'm not understanding is how does one use PWM as the control when the measurements are in Encoder Clicks as would be common on a DC brush motor with encoder. How does the output of the PID taking Clicks as input, outputs and setpoint get translated to an appropriate 0 to 255 PWM signal (Assuming 8-bit PWM)?

It seems like there's a fundamental disconnect between the encoder clicks which can be though of as velocity (change of distance over time) vs PWM control signal which is an amount of power provided to the motor.

My naive guess would be to:

1. Determine the MAX encoder clicks per second at a given PWM value of 255 emperically.
2. Determine the MIN encoder value for the PWM value right before the motor stops moving under load. (For example my Pololu motors need at least 60 PWM on my motor controller to actually turn, below that value they just 'whine'.)
3. Linearly map the encoder readings from the MIN and MAX to an appropriate MIN and MAX PWM reading?
// Using the Arduino PID library
encoderLow = 60;   // determined empirically
encoderHigh = 8400;// determined empirically
PWMLow = 60; // determined empirically
PWMHigh = 255;  // PWM as which we reach encoderHigh

Input = reading_from_encoder();  // clicks per second
Setpoint = 1000; // also in clicks
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
translated_pwm = map(Output, encoderLow, encoderHigh, PWMLow, PWMHigh)
drive_motor(translated_pwm); // control signal to motor as PWM value.


Where Input (the feedback from the encoders), Setpoint, and Output are in clicks. Output is the control signal from the Arduino PID library

Questions:

1. Is this the normal approach?
2. My map() function assumes a linear relationship between encoder values and PWM values. I doubt that's true. Is the PID going to compensate for the nonlinearities?
3. What is the correct way to do this?

A PID controller takes an error as input (the encoder ticks) and yields an output whose unit of measurement $$O$$ (the PWM percentage, usually) is uncorrelated with the unit of measurement $$I$$ of the input.

To this end, the PID gains $$K_P$$, $$K_I$$, and $$K_D$$ do have units of measurement:

• $$[K_P] = \frac{O}{I}$$
• $$[K_I] = \frac{O}{I \cdot s}$$
• $$[K_D] = \frac{O \cdot s}{I}$$
• If you know the relation between input and output signal what need is there for a PID controller? Mar 23, 2021 at 11:11
• We are talking here about measurement units only, which are assigned arbitrarily for a PID in order to guarantee the coherence between the input and the output units. The relation input-output is thus only accounted in terms of units. Mar 23, 2021 at 12:10
• @UgoPatticini yes but how do you map these units to each other? You obviously need a functional relation. That would mean that you know what PWM value gives a certain encoder value for a given load. Considering that PWM-to-encoder ticks is linear then the PID controller becomes obsolete. Do correct me if I am mistaken. Mar 23, 2021 at 12:22
• For example, the Kp unit accounts for how much error in degrees we have from the encoder readout to the target get into PWM, not the other way around. This is a mere convention that has nothing to do with solving the functional relation PWM -> encoder values. Mar 23, 2021 at 12:45
• PID is a unit-less operation. You are simply operating on the differences between where you want to be and where you are right now. The PID is scalar multiplication, integration and derivative of errors to create an actuation in the right direction. The magnitude of this control is what the constants above control. There aren't any units associated with these operations. Jun 17, 2021 at 17:23

You are correct that for PID your output and input should be in the same unit, generally position or velocity. You will need a way to convert your encoder input to that unit, which should be the same unit as your setpoint.

The arduino PID library appears to abstract this for you.

• Thanks. I'm not sure I see how the Arduino library abstracts the conversion. It does reference 0-255 PWM values as a 'default' range, but I don't see anything that otherwise helps convert between the units of position or velocity for input and set points and output as a PWM signal. It seems to assuming input, output, and setpoint are all expressed in PWM values. ?? Perhaps I'm misunderstanding the code. Aug 1, 2020 at 3:52
• If you know the relation between input and output signal what need is there for a PID controller? Mar 23, 2021 at 11:10

Output of a PID controller is a some kind of force ratio that will be applied to your motor. And the input of your PID is the error of what you are trying to control (velocity, position, torque etc.) so the input and the output dont have to be in same unit. it depends on what you are controlling with your pwm module.

Let's say you are trying to control your motor speed. first you have to calculate the difference between your target speed and current speed. Then you will send this error to your pid function. The output of your pid function is your DUTY CYCLE. Lets say your pwm resolution is 8 bits. So your code will look like this:

float error=TargetSpeed-CurrentSpeed;

int duty=CalculatePID(error);

if(duty>0)
SetDirectionPinHigh();
else if(duty<0)
{
SetDirectionPinLow();
duty=-duty;
}
analogWrite(MotorPin,duty);


....

    int CalculatePID(float error)
{
TotalError+=error;  /// TotalError is used for integral term and is the sum of all previous errors
/// it is useful to limit this value (anti-wind up)
int duty= kp*error+ki*TotalError+kd*(error-PreviousError);
PreviousError=error;
/// Check the duty value if it is between 0 and 255
if(duty>255)
duty = 255;
else if(duty<-255)
duty = -255;
return duty;
}


I hope this example helps. Dont get confused with input and output units of pid. it is a closed loop control so it will adjust the right force by itself eventually. The key here is to tune pid parameters correctly.

• If you know the relation between input and output signal what need is there for a PID controller? Mar 23, 2021 at 11:11

In my project I did exactly what you suggested; I measured calibration data for minimum speed/pwm and maximum speed at pwm = 255. Anything below minimum pwm is in the stall zone and yields zero velocity. I used this calibration data to calculate a feed-forward estimated pwm for the velocity set point, assuming linear relationship between velocity and pwm above the stall zone. The feed-forward estimate is applied when the velocity set point changes significantly. This causes the control to converge on the set point very quickly. Then I use a very simple constant-step controller (not PID) to do closed-loop control on the velocity. The constant-step controller requires no further calibration. The combination works very well for my simple differential drive robot and requires no calibration other than measuring min/max speed and pwm.

• So you don't have any need of a PID controller? Mar 23, 2021 at 11:10
• It does have closed-loop control, but using a constant-step controller with feed-forward. The feed-forward part uses some simple calibration (speed at max-pwm, stall-pwm and speed at stall-pwm) to estimate initial pwm given a speed. From then on it will increment or decrement pwm in steps based on velocity feedback from wheel encoders. I chose this because it is simple to tune for beginners. A well tuned PID controller would be best (especially with feed forward). Here is a video of my solution in action; youtu.be/TjE9ceNOTJE Mar 24, 2021 at 19:57