Hi all newbie here. I finished the PID controller. Was good experience!
It is used for feedback controllers. Lots of stuff on the web about them. Please note that this version is not in “standard” form (related to the way the tree terms are combined).
Kent, please note that I now consult chip data for oscillator commands. I am also studying the chip datasheet and help with vigor.
Here is the code, use at your own risk. It can be touchy with gain settings because of rollovers in the Byte values.
'A PID controller with PWM output
'Tested 02-20-09
'Use at your own risk!
'Chip model
#chip 16F88, 8
#config INTRC_IO
'Set the pin directions
dir portA.0 in 'input feedback on pin #17 from rc filter
dir portB.0 out 'set pwm output on pin #6 filter with 10k and 10uF capacitor to simulate a 1 second plant
'Set parameters
Pg = 5 'set proportional gain
Ig = 1 'set integral gain-used to control steady stae error left by the proportional term
Dg = 1 'set dervitive gain- used to control overshoot
command = 127 'set command input - drive to this value
Iterm = command 'set integrator init set command to minimize errors
Ioutn = 0 'Set integrator init use 0 initally will update on first cycle
PWMon
for rampcmd = 0 to command 'Ramp input to command control ssyetms do not like step inputs
HPWM 1, 10, rampcmd
wait 10 ms
if rampcmd = command then goto Ready
next
Ready:
resp = readad(an0) 'get new data
errorn = command - resp 'generate error signal
Pterm = errorn * Pg 'generate proportional term
Iterm = errorn*Ig + Ioutn 'generate integral term I*dt
Dterm = (errorn-erroro)*Dg 'generate dervitive term D/dt
output = Pterm + Iterm + Dterm 'generate output
erroro = errorn 'set previous error
Ioutn = Iterm 'set previuos int value
HPWM 1, 10, output 'output PWM
wait 10 ms 'set sample rate = dt
goto ready
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The a-d converter returns values from 0-255, there are no negative values.
Yes these are the "rollovers" I was talking about. You can dim as word and use readad10 command, that will help.
The resp will usually be smaller than the command, however external disutrbances could produce what you suggest. Try it out your self. If you get something better please post it.
Regards, Ed.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I just comment somethings i think could bring problems... just my thoughts:
>The a-d converter returns values from 0-255, there are no negative values.
I was talking about:
errorn = command - resp
when resp > command
or may be:
Pterm = errorn * Pg
output = Pterm + Iterm + Dterm
When the resultant value is >255
Perhaps is not possible that this happen, but if possible it will happen.
>The resp will usually be smaller than the command
Within my ignorance about PID i think this is deppending of the kind of aplication.
Some aplications may need positive and negative corrections... i mean Pterm can be below or behind command point in every moment.
I'm just reading about PID and try to better understand how it works.
I see that the values of the terms and errors should not be very hight, then perhaps i'm talking about a problem that doesn't exist...
Regards...
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Very interesting the PID controlers... it's a very good thing.
Now i start understanding how derivative an integral control works, and i think that some kind of signed variables are needed:
Perhaps the system will never overshoot, then resp > command,.. this seems to be very optimistic, but possible if system is over-cushioned.
But for example in the derivative term, signed variable looks to be a must, because previous error is espected to be hihger than actual in some moments, then in this calculation erroro > errorn:
Dterm = (errorn-erroro)*Dg
This should cause unespected behaivors...
Other matter is avoiding values > 255, for example here:
Pterm = errorn * Pg
erron should be limited at 255/Pg, something like:
if erron > 255/Pg then erron = 255/Pg
And perhaps here:
output = Pterm + Iterm + Dterm
terms should be limited to 255/3... or limited to 512/3 and check STATUS,C bit... if STATUS,C=1 then output=255
this will bring in a more complex code, but it will stay in a known state.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Santiago, I apologize for the inadequate response to your question yesterday. It was late in the day and it was nearing gin and tonic time. After a few G&T's I realized what you were talking about, the integer math. You are correct. There is a problem with signs.
As you can tell I have no experience with integer math. I will rework this to get it right and post back later.
Thank you for your astute observations, Regards, Ed.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Ed,
Interesting discussion.
I have been working around process PID control for many years and just to throw in a couple of comments.
You have the basic concepts.
Integral term should have a "time" tuning variable. Integral allows you to get back to setpoint when because of nonlinearities in the process you have been running off of setpoint and proportional gain is not enough.
Deriative is the slope of the error. Noise in the measured signal('resp' usually called PV) will drive this crazy. Because it is a slope you would need +/- and so positive and neg values.
Actually Proportional gain can be +/- depending on the sense of the control, ie. heating/cooling
I also searched on the web and there is a lot of info but watch out because some of it isn't right.
I have too many irons in the fire to work much on this but you might inspire me enough to try it
good luck
mike
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I discovered PID and start understanding it thanks to Ed keep it basic...
Looking for PID info in the web is sometimes very confusing... lots of ecuations and complex math, but looking to this code is easy to understand how each term is working.
Now i have readen some pages about PID, i see integral constant is ussually very small. sometyhing like 0,001... but i suppose it dependes on the aplication. There is the problem of float math in GCBasic, but i think it can be managed as 1/100...
I think Ed uses a low-pass kind filter previous the data is sent to PID.. i saw some code about this in a previous thread.
I want to try PID to control motors in a small robot... i think this can solve some problems i have. I think the difficult side is to find the correct values for the constants of every term to it reacts as espected and quickly but not becoming unstable... for the moment just try to get a code that adapts to what i have.
Regards.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Santiago
Here is a corrected code that deals with the negative number issue. It may not be the most efficient but it seems to work ok.
I will have to revisit the low pass filter as well when I get a chance. If you get your robot working with a PID controller please post it. Also please use your eagle eyes when you see me post something. I need lots of help.
Mike, thanks for the input
Regards, Ed.
Ready:
resp = readad(an0) 'get new data
If resp > command then test=true 'Test input
'Generate Pterm
If test = false then
errorn = command - resp
Pterm = errorn * Pg + command 'For resp<command
end if
if test = true then
errorn = command -(resp - command) 'For resp>command
Pterm = command - (command -errorn)*Pg
end if
'Generate Iterm I*dt
if test = true then
Iterm = errorn*Ig + Ioutn 'For resp<command
end if
if test=false then
errorn = resp-command
Iterm = iout - errorn*Ig 'For resp>command
end if
'Generate Dterm D/dt
If test=false then
Dterm = (errorn-erroro)*Dg 'For resp<command
erroro = errorn 'set previous error
end if
If test=true then
errorn = resp-command 'For resp>command
Dterm = (erroro-errorn)*Dg
erroro = errorn 'set previous error
end if
output = Pterm + Iterm + Dterm 'generate output
Ioutn = Iterm 'set previuos int value
HPWM 1, 10, output 'output PWM
wait 1 ms 'set sample rate = dt
goto ready
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Santiago, Since you indicated an interest in robot control, I thought you would be interested in this simple controller code. A PID controller is a very good general-purpose controller because you are able to “tune” the response to just about any plant and get good results. But it is more complex and way overkill on simple motor control problems.
There is a much simpler controller that can perform well on motors. It is called a Bang-Bang controller because it is just on or off. It is used to control Laser Guided Bombs! It does not require a PWM on the chip, just uses one of the digital pins for output. You may have to use an L-C filter to keep the high frequency out of the motor as it just causes power loss (heating). A small “H Bridge” with an L-C filter and you’re done!
Hope this can help you. Thanks for your help. Regards, Ed.
'A Bang Bang Controller
'Tested 02-22-09
'Chip model
#chip 16F88, 8
#config INTRC_IO
'Set the pin directions
dir portA.0 in 'Feedback signal pin #17 (an0)
dir portB.0 out 'output signal pin #6
'name I/O
#define pulse portB.0
Command = 127 'set desired output from plant
Ready: 'reads input and computes output
Input = readad(an0) 'get new data
if Input < Command then set pulse on
if Input >= Command then set pulse off
'wait 1 ms 'option sampling rate control
goto Ready
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks Ed..
I'm learning about the different kinds of controlers.
This Bang-Bang controller was my first attempt, but then i wanted a more "soft" movements.. then i did somekind of "proportional" controller, it just pulsed the motors longer or shorter deppending on the "error", but i had some problems ( now i see the typical problems of proportional)... sometimes i had overshooting(derivative for this), and it never reached the target point as very short pulses just didn't do anything (integer for this) ... i think derivative an integer controls can solve this problems.
Other good thing is that i (or the robot itselfs) can change the constants in different situations... quick response little overshooted may be necessary sometimes, or slow overcushioned in other moments, to have different behaivors...
I didn't know anything about control... just think what i want and try to program it; now i have a "method" with a few mathematical operations, that is even simpler than my own "proportional" method.
I hope to learn more about control methods, it's a very interesting and powerful tool.
The L-C filter is something i have to add... i think just a capacitor in parallel with the motor, as the motor winding is already an inductor... what do you think?
Thanks for the suggestions.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Santiago, thanks for the reply. I hope you learn control stuff it is very powerful to have it in your bag. The PID is the best all around controller for any given situation as it can do everything you need to control overshoots and gain errors.
"Soft control" means lower bandwidth in a proportional controller. You could also use the ramp funtion like what I used to get the system approximately were you want it before "closing the loop."
No about capacitor that will blow out your amp. Add inductance in series to reduce transient currents on edges of signal. Also can add a little resistance to reduce ringing of filter if necessary.
Regards, Ed.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi all newbie here. I finished the PID controller. Was good experience!
It is used for feedback controllers. Lots of stuff on the web about them. Please note that this version is not in “standard” form (related to the way the tree terms are combined).
Kent, please note that I now consult chip data for oscillator commands. I am also studying the chip datasheet and help with vigor.
Here is the code, use at your own risk. It can be touchy with gain settings because of rollovers in the Byte values.
'A PID controller with PWM output
'Tested 02-20-09
'Use at your own risk!
'Chip model
#chip 16F88, 8
#config INTRC_IO
'Set the pin directions
dir portA.0 in 'input feedback on pin #17 from rc filter
dir portB.0 out 'set pwm output on pin #6 filter with 10k and 10uF capacitor to simulate a 1 second plant
'Set parameters
Pg = 5 'set proportional gain
Ig = 1 'set integral gain-used to control steady stae error left by the proportional term
Dg = 1 'set dervitive gain- used to control overshoot
command = 127 'set command input - drive to this value
Iterm = command 'set integrator init set command to minimize errors
Ioutn = 0 'Set integrator init use 0 initally will update on first cycle
PWMon
for rampcmd = 0 to command 'Ramp input to command control ssyetms do not like step inputs
HPWM 1, 10, rampcmd
wait 10 ms
if rampcmd = command then goto Ready
next
Ready:
resp = readad(an0) 'get new data
errorn = command - resp 'generate error signal
Pterm = errorn * Pg 'generate proportional term
Iterm = errorn*Ig + Ioutn 'generate integral term I*dt
Dterm = (errorn-erroro)*Dg 'generate dervitive term D/dt
output = Pterm + Iterm + Dterm 'generate output
erroro = errorn 'set previous error
Ioutn = Iterm 'set previuos int value
HPWM 1, 10, output 'output PWM
wait 10 ms 'set sample rate = dt
goto ready
Very interesting!
I didn't know about this kind of things and i think it is just what i needed...
But there are some considerations that you have to take care abut:
You are using byte variables, that means that you only can manage 0 to 255 values... not negative or >255 values are allowed.
Then lets suppose:
resp = 130
When this line is executed:
errorn = command - resp
you have:
errorn = 127 -130 ---> errorn = 252 '....(255-3)
Pterm = 252 * 5 ---> Pterm = 236 '... not 100% sure about this but the logic is:
252 * 5 = 1260 ---> 0100 1110 1100
as Pterm is just 8 bits then Pterm=11101100=236
Then i think something like this should be done to avoid <0 values:
if command > resp then
errorn = command -resp
error_sign = 1 'positive
end if
if command <= resp then
erron = resp - command
error_sign = 0
end if
this
Pterm and some other variables should declared as word as it can be >255
Perhaps the good choice is to declare all variables as word ( 2 bytes)...
Thanks for sharing ideas....
Santiago, Thanks for your comments.
The a-d converter returns values from 0-255, there are no negative values.
Yes these are the "rollovers" I was talking about. You can dim as word and use readad10 command, that will help.
The resp will usually be smaller than the command, however external disutrbances could produce what you suggest. Try it out your self. If you get something better please post it.
Regards, Ed.
Hi Ed
I just comment somethings i think could bring problems... just my thoughts:
>The a-d converter returns values from 0-255, there are no negative values.
I was talking about:
errorn = command - resp
when resp > command
or may be:
Pterm = errorn * Pg
output = Pterm + Iterm + Dterm
When the resultant value is >255
Perhaps is not possible that this happen, but if possible it will happen.
>The resp will usually be smaller than the command
Within my ignorance about PID i think this is deppending of the kind of aplication.
Some aplications may need positive and negative corrections... i mean Pterm can be below or behind command point in every moment.
I'm just reading about PID and try to better understand how it works.
I see that the values of the terms and errors should not be very hight, then perhaps i'm talking about a problem that doesn't exist...
Regards...
Hi again...
Very interesting the PID controlers... it's a very good thing.
Now i start understanding how derivative an integral control works, and i think that some kind of signed variables are needed:
Perhaps the system will never overshoot, then resp > command,.. this seems to be very optimistic, but possible if system is over-cushioned.
But for example in the derivative term, signed variable looks to be a must, because previous error is espected to be hihger than actual in some moments, then in this calculation erroro > errorn:
Dterm = (errorn-erroro)*Dg
This should cause unespected behaivors...
Other matter is avoiding values > 255, for example here:
Pterm = errorn * Pg
erron should be limited at 255/Pg, something like:
if erron > 255/Pg then erron = 255/Pg
And perhaps here:
output = Pterm + Iterm + Dterm
terms should be limited to 255/3... or limited to 512/3 and check STATUS,C bit... if STATUS,C=1 then output=255
this will bring in a more complex code, but it will stay in a known state.
Santiago, I apologize for the inadequate response to your question yesterday. It was late in the day and it was nearing gin and tonic time. After a few G&T's I realized what you were talking about, the integer math. You are correct. There is a problem with signs.
As you can tell I have no experience with integer math. I will rework this to get it right and post back later.
Thank you for your astute observations, Regards, Ed.
Ed,
Interesting discussion.
I have been working around process PID control for many years and just to throw in a couple of comments.
You have the basic concepts.
Integral term should have a "time" tuning variable. Integral allows you to get back to setpoint when because of nonlinearities in the process you have been running off of setpoint and proportional gain is not enough.
Deriative is the slope of the error. Noise in the measured signal('resp' usually called PV) will drive this crazy. Because it is a slope you would need +/- and so positive and neg values.
Actually Proportional gain can be +/- depending on the sense of the control, ie. heating/cooling
I also searched on the web and there is a lot of info but watch out because some of it isn't right.
I have too many irons in the fire to work much on this but you might inspire me enough to try it
good luck
mike
Hi mmotte. thanks for the aclarations.
I discovered PID and start understanding it thanks to Ed keep it basic...
Looking for PID info in the web is sometimes very confusing... lots of ecuations and complex math, but looking to this code is easy to understand how each term is working.
Now i have readen some pages about PID, i see integral constant is ussually very small. sometyhing like 0,001... but i suppose it dependes on the aplication. There is the problem of float math in GCBasic, but i think it can be managed as 1/100...
I think Ed uses a low-pass kind filter previous the data is sent to PID.. i saw some code about this in a previous thread.
I want to try PID to control motors in a small robot... i think this can solve some problems i have. I think the difficult side is to find the correct values for the constants of every term to it reacts as espected and quickly but not becoming unstable... for the moment just try to get a code that adapts to what i have.
Regards.
Santiago
Here is a corrected code that deals with the negative number issue. It may not be the most efficient but it seems to work ok.
I will have to revisit the low pass filter as well when I get a chance. If you get your robot working with a PID controller please post it. Also please use your eagle eyes when you see me post something. I need lots of help.
Mike, thanks for the input
Regards, Ed.
Ready:
resp = readad(an0) 'get new data
If resp > command then test=true 'Test input
'Generate Pterm
If test = false then
errorn = command - resp
Pterm = errorn * Pg + command 'For resp<command
end if
if test = true then
errorn = command -(resp - command) 'For resp>command
Pterm = command - (command -errorn)*Pg
end if
'Generate Iterm I*dt
if test = true then
Iterm = errorn*Ig + Ioutn 'For resp<command
end if
if test=false then
errorn = resp-command
Iterm = iout - errorn*Ig 'For resp>command
end if
'Generate Dterm D/dt
If test=false then
Dterm = (errorn-erroro)*Dg 'For resp<command
erroro = errorn 'set previous error
end if
If test=true then
errorn = resp-command 'For resp>command
Dterm = (erroro-errorn)*Dg
erroro = errorn 'set previous error
end if
output = Pterm + Iterm + Dterm 'generate output
Ioutn = Iterm 'set previuos int value
HPWM 1, 10, output 'output PWM
wait 1 ms 'set sample rate = dt
goto ready
Santiago, Since you indicated an interest in robot control, I thought you would be interested in this simple controller code. A PID controller is a very good general-purpose controller because you are able to “tune” the response to just about any plant and get good results. But it is more complex and way overkill on simple motor control problems.
There is a much simpler controller that can perform well on motors. It is called a Bang-Bang controller because it is just on or off. It is used to control Laser Guided Bombs! It does not require a PWM on the chip, just uses one of the digital pins for output. You may have to use an L-C filter to keep the high frequency out of the motor as it just causes power loss (heating). A small “H Bridge” with an L-C filter and you’re done!
Hope this can help you. Thanks for your help. Regards, Ed.
'A Bang Bang Controller
'Tested 02-22-09
'Chip model
#chip 16F88, 8
#config INTRC_IO
'Set the pin directions
dir portA.0 in 'Feedback signal pin #17 (an0)
dir portB.0 out 'output signal pin #6
'name I/O
#define pulse portB.0
Command = 127 'set desired output from plant
Ready: 'reads input and computes output
Input = readad(an0) 'get new data
if Input < Command then set pulse on
if Input >= Command then set pulse off
'wait 1 ms 'option sampling rate control
goto Ready
Thanks Ed..
I'm learning about the different kinds of controlers.
This Bang-Bang controller was my first attempt, but then i wanted a more "soft" movements.. then i did somekind of "proportional" controller, it just pulsed the motors longer or shorter deppending on the "error", but i had some problems ( now i see the typical problems of proportional)... sometimes i had overshooting(derivative for this), and it never reached the target point as very short pulses just didn't do anything (integer for this) ... i think derivative an integer controls can solve this problems.
Other good thing is that i (or the robot itselfs) can change the constants in different situations... quick response little overshooted may be necessary sometimes, or slow overcushioned in other moments, to have different behaivors...
I didn't know anything about control... just think what i want and try to program it; now i have a "method" with a few mathematical operations, that is even simpler than my own "proportional" method.
I hope to learn more about control methods, it's a very interesting and powerful tool.
The L-C filter is something i have to add... i think just a capacitor in parallel with the motor, as the motor winding is already an inductor... what do you think?
Thanks for the suggestions.
Santiago, thanks for the reply. I hope you learn control stuff it is very powerful to have it in your bag. The PID is the best all around controller for any given situation as it can do everything you need to control overshoots and gain errors.
"Soft control" means lower bandwidth in a proportional controller. You could also use the ramp funtion like what I used to get the system approximately were you want it before "closing the loop."
No about capacitor that will blow out your amp. Add inductance in series to reduce transient currents on edges of signal. Also can add a little resistance to reduce ringing of filter if necessary.
Regards, Ed.