I'm working on a hobby project (not for school). This project is about controlling speed and position on a DC motor with an optical encoder (334 CPR).At the present time I'm working with speed control. I understand the basics of PID (how it works, transfer function, etc.). What I really don't understand is:
If the setpoint is a value given in RPM, the feedback is also RPM, the gains are constants (no units), the error is also RPM; How am I supposed to handle the output of the controller in order to obtain a voltage to feed the motor? Has anyone implement this kind of control.
Thanks in advance for your help/comments!
Edit: I have read a lot of posts and info about this with no satisfactory results
The most sensible way to do that would be for the PID input to be the RPM error, and the PID output to be the motor power demand. You would implement the demand by converting the output to a PWM value and using analogWrite() to apply that PWM to the motor power supply.
You have a choice of how you're going to scale the PID inputs and outputs i.e. whether you're going to use raw RPM/Hz/whatever values and raw PWM duty cycle, or normalise those values in some way. The main effect of that decision will be to determine the effect of the PID parameters i.e. if your error signal is +/- 1 then you would need very different PID tuning parameters than if your input was +/- 10000.
A lot of what you are trying to do involves defining some stuff for yourself. Like mentioned by PeterH, you'll need to create some form of variable for output speed, and this variable will be fairly arbitrary based on your PWM output just like PeterH described. You'll then probably want to try and estimate a function to describe speed versus PWM output. Also note, you probably don't want to make your 'max' speed to be the maximum power to the motor, or your control systems become very restricted.
Depending on how much control theory you are doing, this all gets incorporated into your plant functions mathematically, but it can be a pain to wrap your head around.
Don't forget to use a loaded motor (IE for a vehicle, it is actually moving the vehicle), and not an unloaded motor (Wheels up in the air), or your numbers will be wrong. Also you may need to calibrate independently per wheel/motor.
PeterH:
The most sensible way to do that would be for the PID input to be the RPM error, and the PID output to be the motor power demand. You would implement the demand by converting the output to a PWM value and using analogWrite() to apply that PWM to the motor power supply.
You have a choice of how you're going to scale the PID inputs and outputs i.e. whether you're going to use raw RPM/Hz/whatever values and raw PWM duty cycle, or normalise those values in some way. The main effect of that decision will be to determine the effect of the PID parameters i.e. if your error signal is +/- 1 then you would need very different PID tuning parameters than if your input was +/- 10000.
I don't think I agree with that. In classic PID control loop, the input, called the 'process variable' is always the property you wish to control, in this case the actual rpm read from whatever sensing method is being used to measure the rpm. The 'error value' is a result/calculation of the pid algorithm of abs(user's desired rpm ('setpoint') - actual rpm ('process variable).
PeterH:
The most sensible way to do that would be for the PID input to be the RPM error, and the PID output to be the motor power demand. You would implement the demand by converting the output to a PWM value and using analogWrite() to apply that PWM to the motor power supply.
You have a choice of how you're going to scale the PID inputs and outputs i.e. whether you're going to use raw RPM/Hz/whatever values and raw PWM duty cycle, or normalise those values in some way. The main effect of that decision will be to determine the effect of the PID parameters i.e. if your error signal is +/- 1 then you would need very different PID tuning parameters than if your input was +/- 10000.
I don't think I agree with that. In classic PID control loop, the input, called the 'process variable' is always the property you wish to control, in this case the actual rpm read from whatever sensing method is being used to measure the rpm. The 'error value' is a result/calculation of the pid algorithm of abs(user's desired rpm ('setpoint') - actual rpm ('process variable).
I agree and a good way of describing it, that's why I suggest trying to find a function that will in an open loop, control the RPM (roughly) of the motor based on a PWM input. That way you can translate your error into RPM. This is usually lumped into a plant function, and luckily, you'll probably end up with a proportial variable between the two and not have to have some odd input.
This is an example for two fans controlled by a temp sensor. One fan is controlled by the pid and one is a simple on/off controll.
#include <PID_v1.h>
const int FanPin = 9;
const int FanPin2= 8;
const int testpin =A2; //only used for debugging
const int tempPin=A1;
#define tk1 93.26
#define tk2 9.26
#define tempsetpoint 32
#define tempsetpoint2 35
double Setpoint, Input, Output;
PID myPid(&Input,&Output,&Setpoint,10,15,2,REVERSE); /the reverse here means that a high
int sensorValue = 0; // raw temperature data
int outputValue = 0; // raw signal to Fan1
long test = 0; /for debugging only
boolean t2on=false; //Fan 2 controlvariableset to off initially
void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(9600);
pinMode(FanPin2,OUTPUT);
Input=25; Arbitrary starting value for controller.
Setpoint=tempsetpoint; //set desired setpoint. Note that this is the converted tempsensor value i.e. in celsius
myPid.SetMode(AUTOMATIC); //turn PID on.
}
void loop() {
float temp=0;
;
// read the raw sensor value:
sensorValue = analogRead(tempPin);
temp=((float)sensorValue-tk1)/tk2; convert raw sensor value to temp;
Input=temp; //send temp to PID
myPid.Compute();
outputValue=Output; // Get raw output value from PID
test =analogRead (testpin); /debugging only
if (temp>tempsetpoint2) t2on=true; // set fan 2 on or off
if (temp<(tempsetpoint2-2)) t2on=false;
/*The following two lines ensures that maximum and minimum values are not outside the possible outputs. If this is not done the PWM output loops around. (i.e. if the pid output would give a value of 256 the digital.write would yield a value of 0 and 257 would give 1.)
A way to scale a parameter is using map as follows: suppose you have the analog read value from a pot in avg, the statement :
outputValue = map(avg, 00, 1023, 0, 255);
would give scales it from its minimum and maximum values (0-1023) to the digital.output values (0-255).
if(outputValue>255)outputValue=255;
if(outputValue<0)outputValue=0;
// change the analog out value:
analogWrite(FanPin, outputValue);
digitalWrite(FanPin2,t2on);
delay(500);
}
/*The following two lines ensures that maximum and minimum values are not outside the possible outputs. If this is not done the PWM output loops around. (i.e. if the pid output would give a value of 256 the digital.write would yield a value of 0 and 257 would give 1.)
A way to scale a parameter is using map as follows: suppose you have the analog read value from a pot in avg, the statement :
outputValue = map(avg, 00, 1023, 0, 255);
would give scales it from its minimum and maximum values (0-1023) to the digital.output values (0-255).
if(outputValue>255)outputValue=255;
if(outputValue<0)outputValue=0;
// change the analog out value:
analogWrite(FanPin, outputValue);
digitalWrite(FanPin2,t2on);
How "good" does that work? I mean, were you able to control the fan accurately?
It works good for my application. The temperature is within one degree in fan ones working range. The parmeters sent to the Pid for your application will be different and will need some fiddling with.