Hi all newbie here. I called my brother Robert on this math problem, encountered in PID and the filter.
He is a scientist (Phd.) and a genius in math and computer science, which he taught at MIT in the ‘70’s. And yes, he knows how to program PIC’s in assembler.
He told me that the “standard solution to this issue is the make the input a “signed integer” by subtracting half the range (128), (and mentioned something about 2’s complement which the PIC supports) do the intermediate calculations and then add back the 128 when you output to the PWM. While on the phone, I tried this out with the filter problem. It did not work correctly. It worked ok on the positive going part, but got “hung” up when going back down. We trace the problem to a single line of code” output = (inputsgn-outputn)/tp + outputn 'compute output, where inputsgn is the a-d read –128. This line would execute correctly only if tp=1 and no other value. Dimensioning everything as word makes it go nuts!
He asked me to send him the .hex file and he would examine it with MPLab. Later he e-mailed me to say he could not load MPLab on his Vista computer and was going to try an old one. I do not know if he will ever get this solved, but it is his opinion that GCBasic is at fault.
I checked his assertions in an Excel spreadsheet and confirmed the equations work ok.
Try it yourself, here is the code we tested:
'A first order lag with PWM output
'Tested 02-23-09 does not work correctly
'Chip model
#chip 16F88, 8
#config INTRC_IO
'Set the pin directions
dir portA.0 in 'input test signal pin #17 (an0)
dir portB.0 out 'set pwm output pin 6
'dim Input as word
'dim inputsgn as word
'dim outputn as word
'dim output as word
'dim outputpwm as word
'dim PWMDC as word
outputn = 0 'Initial condition
PWMDC = 0 'Initial condition
tp = 2 'time constant = tp in seconds (ratio of tp/dt ) – only 1 works!
FirstLag: 'reads input and computes output
Input = readad(an0) 'get new data
Inputsgn = input -128 'make input a signed integer
output = (inputsgn-outputn)/tp + outputn 'compute output
outputn = output 'set n-1 value of output
outputpwm = output +128 'translate to 0-255
PWMon 'Turn PWM on so we can see the internal calulations
PWMDC = outputpwm 'scale PWM
HPWM 1, 40, PWMDC 'output PWM
wait 100 ms 'set sample rate = dt
goto firstlag
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Ed, as you know GCBasic doesn't support signed integers. Tried Santiago's signed math library with your code, but still hung up on proper data presentation.
The two's complement for a negative number representation can easily be done by variable = !variable + 1. Have used that for the Dallas DS18X20 temp senseors. But not sure if that's the way to go or not.
The most readable code is just use some conditional statements and keeping track of whether the result will be negative or positive.
Here is my thought process:
Make a header file for Santiago's signed math library. To do that, make a copy of any header file and plop it back in the include folder. Then copy and paste in his code to that file and rename it say SignedMath.h
Be sure to put his fix in per the signed math post. Do not use my posted fix!!!, because it messes up.
Use the #include <SignedMath.h>
Make sure that any variables and parameters that are used with the signed math functions are dimensioned as words.
:make your pot adjust from -127,0,+127
If Feedback > 127 Then Inputsgn = Feedback - 127 ;positve value
If Feedback = 127 Then Inputsgn = 0
If Feedback < 128 Then
Inputsgn = 127 - Feedback
Inputsgn = Inputsgn + 256 ;make a negative value
End if
Now for every little math operation you have to break it down to just two operators, oh joy! Surely there is a more simple way, but I am lost here.
All said and done the positive side works like a charm, but the negative side is another story. The PIDfactor sometimes comes back to zero (i.e. 256?) but the output value is not working for me. It is probably something simple.
Good luck,
Kent
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Kent, thanks, for the reply. I did not know that GCBasic did not support signed integers! Had I known that I would have saved much time.
I am not sure that this approch is easier than what I ended up doing, has just as many lines, maybe not quite as confusing thogh. Where do I find the signed math include file?
Santiogo, thanks for your help and the signed math libs. I take it you like the filter?! It is a "first oder lad" in engineering terms. In financial terms it is called an "exponential moving average"
I will have to study this in more detail
Best regards, Ed.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Kent, not supporting signed math is a significant limitation for Great Cow! That makes control system stuff and signal processing very difficult to do!
Great Cow is such a wonderful (and very flexible) program it seems a shame to have this glaring flaw. Is it on Hughes to do list? If not could you get him to put it on?
Santigo, could you post you PID code useing your library, so I can see how to best use it? Also so PID code needs to include clamps to prevent overflow for high gain terms, to prevent overflow (usually need on the Proportional and dervivitive terms).
Best regards, Ed.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Well, stumbled upon the fix to my negative input problem late last night. Still having a hard time visualizing the negative number input. The following fix is used to get the right output. There is a question if I have adjusted the data correctly, just to fit the form? The adjustment might somehow be related to the fact that the output.7 bit is not used in my example.
:To be applied to the output value after all
;calculations prior to readout. LCDinput = output
If LCDinput_H.0 On Then
Set LCDinput_H.0 Off
LCDinput = 255 - LCDinput
If LCDinput >= 0 Then Minus = True
End if
All my tests thus far have been using tp = 2.
Santiago, thanks for the visual representation of the filter, very nice! Would be great if you could get a shot of a negative voltage with the above fix?....... :-).
Ed, hard to say whether the missing signed maths, floating point, and trig functions are a glaring GCBasic oversight, or simply an opportunity? If in need ASAP, there is a commercially available floating point math co-processor, that might serve your needs. Serious digital signal analysis would entail a 16 bit dsPIC, (haven't tried one personally). You would probably be using C though, but the librarys are already done for you.
Not sure where these items line up priority wise with Hugh. Need to nail down the wait delay and RS232 routines first I would think.
Santiago's header/library file is in the contributors section of the forum, with the associated fix in the signed math thread in this(help) forum. You can use the posted code there, to test the routines on an lcd.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi Ed... i still testing all this stuff, but in the way i'm using it works for me. I added a flag to avoid overflow in add and mult fuctions... just using bit 1 of high byte as an overflow flag, then you can check the bit and limit the value to 255 if it is 1.
But take it with care as i'm not fully-tested this and perhaps it needs more thoughts and work to bring the functions to work in an easy way.
The way i'm testing signed math for PID is the following; there are the declared variables and some comments, the use of the resultant value can be different in different cases.
Note that every operation is started with a comment line of the equivalent "standard math":
'--------------------------------------------------------------------
dim pos_target as word 'Target value
dim pos_mot as word 'Readed value
dim error_pot as word 'error actual
dim error_pot_prev as word 'error prev.
dim K_propo as word 'Prop. gain
dim K_deriv as word 'Deriv. gain
dim K_integ as word 'Integ. gain (1/K).. i use gain < 1
dim T_propo as word 'Prop. term
dim T_deriv as word 'Deriv term
dim T_integ as wo 'Integ. term
I forgot to comment that the signed_math.h in the link has the fix to the bug finded by Kent and the overflow flag suff (take in account this bit if you use this library).
Kent:
I still Keep in the positive side of output (in the filter routine), the corrections made by the filter can be positive or negative (that's why the signed math), but the outputed value will be always positive.
Not tried representing negative values, but when i try it i will take in account your suggestions and post it.
In PID, the outputed value to PWM is always positive, i just toggle the motor turn by setting or clearing the opposite side of the motor... still doing tests with this... PID is complex when you try to find a proper combination of constants (gains) values, still don't understand this, and need a lot of reading and testing untill i can manage this properly, but i'm amazed with this kind of stuff...
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Kent thanks. You are right about DSP, however control stuff works well within the limitations of the current PIC families. For most control stuff 8 bits works fine (<.5% error). I really would like to see this fixed as it is an imprtant "opportunity." The other stuff can wait as far as I am concerned.
Santiago, I downloaded your file from your link and it has no indention and is almost unreable!
I find that it is easy to tune the PID. First raise the P gain until you see an overshoot. Then increase D gain to smoooth it out. If there is a steady state error, increase the I gain, then redo the D gain to compensate for any change in overshoot. That should do it.
Regards, Ed.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Kent, You mentioned RS-232 problems Hugh might be working on. Is this sominge I need to Know about as I am struggling to get mime to work? 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 called my brother Robert on this math problem, encountered in PID and the filter.
He is a scientist (Phd.) and a genius in math and computer science, which he taught at MIT in the ‘70’s. And yes, he knows how to program PIC’s in assembler.
He told me that the “standard solution to this issue is the make the input a “signed integer” by subtracting half the range (128), (and mentioned something about 2’s complement which the PIC supports) do the intermediate calculations and then add back the 128 when you output to the PWM. While on the phone, I tried this out with the filter problem. It did not work correctly. It worked ok on the positive going part, but got “hung” up when going back down. We trace the problem to a single line of code” output = (inputsgn-outputn)/tp + outputn 'compute output, where inputsgn is the a-d read –128. This line would execute correctly only if tp=1 and no other value. Dimensioning everything as word makes it go nuts!
He asked me to send him the .hex file and he would examine it with MPLab. Later he e-mailed me to say he could not load MPLab on his Vista computer and was going to try an old one. I do not know if he will ever get this solved, but it is his opinion that GCBasic is at fault.
I checked his assertions in an Excel spreadsheet and confirmed the equations work ok.
Try it yourself, here is the code we tested:
'A first order lag with PWM output
'Tested 02-23-09 does not work correctly
'Chip model
#chip 16F88, 8
#config INTRC_IO
'Set the pin directions
dir portA.0 in 'input test signal pin #17 (an0)
dir portB.0 out 'set pwm output pin 6
'dim Input as word
'dim inputsgn as word
'dim outputn as word
'dim output as word
'dim outputpwm as word
'dim PWMDC as word
outputn = 0 'Initial condition
PWMDC = 0 'Initial condition
tp = 2 'time constant = tp in seconds (ratio of tp/dt ) – only 1 works!
FirstLag: 'reads input and computes output
Input = readad(an0) 'get new data
Inputsgn = input -128 'make input a signed integer
output = (inputsgn-outputn)/tp + outputn 'compute output
outputn = output 'set n-1 value of output
outputpwm = output +128 'translate to 0-255
PWMon 'Turn PWM on so we can see the internal calulations
PWMDC = outputpwm 'scale PWM
HPWM 1, 40, PWMDC 'output PWM
wait 100 ms 'set sample rate = dt
goto firstlag
Ed, as you know GCBasic doesn't support signed integers. Tried Santiago's signed math library with your code, but still hung up on proper data presentation.
The two's complement for a negative number representation can easily be done by variable = !variable + 1. Have used that for the Dallas DS18X20 temp senseors. But not sure if that's the way to go or not.
The most readable code is just use some conditional statements and keeping track of whether the result will be negative or positive.
Here is my thought process:
Make a header file for Santiago's signed math library. To do that, make a copy of any header file and plop it back in the include folder. Then copy and paste in his code to that file and rename it say SignedMath.h
Be sure to put his fix in per the signed math post. Do not use my posted fix!!!, because it messes up.
Use the #include <SignedMath.h>
Make sure that any variables and parameters that are used with the signed math functions are dimensioned as words.
:make your pot adjust from -127,0,+127
If Feedback > 127 Then Inputsgn = Feedback - 127 ;positve value
If Feedback = 127 Then Inputsgn = 0
If Feedback < 128 Then
Inputsgn = 127 - Feedback
Inputsgn = Inputsgn + 256 ;make a negative value
End if
Now for every little math operation you have to break it down to just two operators, oh joy! Surely there is a more simple way, but I am lost here.
PIDfactor = Signed_Sub(Inputsgn, outputn)
PIDfactor = Signed_Div(PIDfactor, tp)
output = signed_add (PIDfactor, outputn)
outputn = output
All said and done the positive side works like a charm, but the negative side is another story. The PIDfactor sometimes comes back to zero (i.e. 256?) but the output value is not working for me. It is probably something simple.
Good luck,
Kent
Hi... i tried the filter with signed math and it works for me.
Deading adc 8 bits (0-255, i did not use +127 -127), tp=10 ( to see the effect)
Here you can see the effect of the filter, red is unfiltered reading, white is filtered:
http://docs.google.com/Doc?id=ddvgzjtz_48d89s2pf5
I forgot to say that i used exactly the code proposed by Kent ( not the +127 -127 adjust):
PIDfactor = Signed_Sub(Inputsgn, outputn)
PIDfactor = Signed_Div(PIDfactor, tp)
output = signed_add (PIDfactor, outputn)
outputn = output
Red channel is inputsgn, and white is output
Kent, thanks, for the reply. I did not know that GCBasic did not support signed integers! Had I known that I would have saved much time.
I am not sure that this approch is easier than what I ended up doing, has just as many lines, maybe not quite as confusing thogh. Where do I find the signed math include file?
Santiogo, thanks for your help and the signed math libs. I take it you like the filter?! It is a "first oder lad" in engineering terms. In financial terms it is called an "exponential moving average"
I will have to study this in more detail
Best regards, Ed.
Kent, not supporting signed math is a significant limitation for Great Cow! That makes control system stuff and signal processing very difficult to do!
Great Cow is such a wonderful (and very flexible) program it seems a shame to have this glaring flaw. Is it on Hughes to do list? If not could you get him to put it on?
Santigo, could you post you PID code useing your library, so I can see how to best use it? Also so PID code needs to include clamps to prevent overflow for high gain terms, to prevent overflow (usually need on the Proportional and dervivitive terms).
Best regards, Ed.
Well, stumbled upon the fix to my negative input problem late last night. Still having a hard time visualizing the negative number input. The following fix is used to get the right output. There is a question if I have adjusted the data correctly, just to fit the form? The adjustment might somehow be related to the fact that the output.7 bit is not used in my example.
:To be applied to the output value after all
;calculations prior to readout. LCDinput = output
If LCDinput_H.0 On Then
Set LCDinput_H.0 Off
LCDinput = 255 - LCDinput
If LCDinput >= 0 Then Minus = True
End if
All my tests thus far have been using tp = 2.
Santiago, thanks for the visual representation of the filter, very nice! Would be great if you could get a shot of a negative voltage with the above fix?....... :-).
Ed, hard to say whether the missing signed maths, floating point, and trig functions are a glaring GCBasic oversight, or simply an opportunity? If in need ASAP, there is a commercially available floating point math co-processor, that might serve your needs. Serious digital signal analysis would entail a 16 bit dsPIC, (haven't tried one personally). You would probably be using C though, but the librarys are already done for you.
Not sure where these items line up priority wise with Hugh. Need to nail down the wait delay and RS232 routines first I would think.
Santiago's header/library file is in the contributors section of the forum, with the associated fix in the signed math thread in this(help) forum. You can use the posted code there, to test the routines on an lcd.
Hi Ed... i still testing all this stuff, but in the way i'm using it works for me. I added a flag to avoid overflow in add and mult fuctions... just using bit 1 of high byte as an overflow flag, then you can check the bit and limit the value to 255 if it is 1.
But take it with care as i'm not fully-tested this and perhaps it needs more thoughts and work to bring the functions to work in an easy way.
To don't loose indentation and keep the forun clean you can download signed_math.h from here:
http://www.box.net/shared/0jk7mzeax0
The way i'm testing signed math for PID is the following; there are the declared variables and some comments, the use of the resultant value can be different in different cases.
Note that every operation is started with a comment line of the equivalent "standard math":
'--------------------------------------------------------------------
dim pos_target as word 'Target value
dim pos_mot as word 'Readed value
dim error_pot as word 'error actual
dim error_pot_prev as word 'error prev.
dim K_propo as word 'Prop. gain
dim K_deriv as word 'Deriv. gain
dim K_integ as word 'Integ. gain (1/K).. i use gain < 1
dim T_propo as word 'Prop. term
dim T_deriv as word 'Deriv term
dim T_integ as wo 'Integ. term
dim Total_integ as word 'Total integ
dim Total_term as word 'Total term
out_pwm = 0 'byte variable
'--------------------------------------------------------------------
'error_pot = pos_mot - pos_target
error_pot = Signed_Sub(pos_mot, pos_target)
'T_propo = error_pot * K_propo
T_propo = Signed_Mul(error_pot, K_propo)
if Signed_Mul_H.1 on then 'Avoid overflow, value limited to 255
MOVLW 255 'T_propo = 255
MOVWF T_propo
End if
'T_deriv = (error_pot - error_pot_prev) * K_deriv
T_deriv = Signed_Sub(error_pot, error_pot_prev)
T_deriv = Signed_Mul(T_deriv, K_deriv)
if Signed_Mul_H.1 on then 'Avoid overflow, value limited to 255
MOVLW 255 'T_deriv = 255
MOVWF T_deriv
End if
'T_integ = error_pot / K_integ + Total_integ
T_integ = Signed_Div(error_pot, K_integ)
Total_integ = Signed_Add(Total_integ, T_integ)
if Signed_Add_H.1 on then 'Avoid overflow, value limited to 255
MOVLW 255 'Total_integ = 255
MOVWF Total_integ
End if
error_pot_prev = error_pot
'Total_term = T_propo + T_deriv + T_integ
Total_term = Signed_Add(T_propo, T_deriv)
if Signed_Add_H.1 on then 'Avoid overflow, value limited to 255
MOVLW 255 'Total_term = 255
MOVWF Total_term
goto salida_pwm
End if
Total_term = Signed_Add(Total_term, Total_integ)
if Signed_Add_H.1 on then 'Avoid overflow, value limited to 255
MOVLW 255 'Total_term = 255
MOVWF Total_term
goto salida_pwm
End if
'Here is the output
salida_pwm:
out_pwm = Total_term
if Total_term_H.0 on then out_pwm = 255 - out_pwm 'invert pwm for neg. values
HPWM 2, 10, out_pwm
dir_motor = Total_term_H.0 'motor turn direction = sign flag
'--------------------------------------------------------------------
this asm lines:
MOVLW 255 'Total_term = 255
MOVWF Total_term
could be sustituted (i think) by this basic ones:
Total_term = Total_term | 255
I prefer the asm byte operation instead of the basic word opreration, but the result should be similar: low byte = 255
What cannot be done is:
Total_term = 255
This way you will loose sign flag
Regards.
I forgot to comment that the signed_math.h in the link has the fix to the bug finded by Kent and the overflow flag suff (take in account this bit if you use this library).
Kent:
I still Keep in the positive side of output (in the filter routine), the corrections made by the filter can be positive or negative (that's why the signed math), but the outputed value will be always positive.
Not tried representing negative values, but when i try it i will take in account your suggestions and post it.
In PID, the outputed value to PWM is always positive, i just toggle the motor turn by setting or clearing the opposite side of the motor... still doing tests with this... PID is complex when you try to find a proper combination of constants (gains) values, still don't understand this, and need a lot of reading and testing untill i can manage this properly, but i'm amazed with this kind of stuff...
Kent thanks. You are right about DSP, however control stuff works well within the limitations of the current PIC families. For most control stuff 8 bits works fine (<.5% error). I really would like to see this fixed as it is an imprtant "opportunity." The other stuff can wait as far as I am concerned.
Santiago, I downloaded your file from your link and it has no indention and is almost unreable!
I find that it is easy to tune the PID. First raise the P gain until you see an overshoot. Then increase D gain to smoooth it out. If there is a steady state error, increase the I gain, then redo the D gain to compensate for any change in overshoot. That should do it.
Regards, Ed.
Sainiagoi, forgot to mention - test it with a square wave. Ed.
Ed.. i downloaded the file and for me is ok...
In Windows perhaps you need to rename it to .txt to have a look to it... but not sure about this
Santiago, WordPad opens it fine , but Notepad has a problem. Regards, Ed.
Kent, You mentioned RS-232 problems Hugh might be working on. Is this sominge I need to Know about as I am struggling to get mime to work? Regards, Ed.
Hi Ed....
Try the hardware RS-232, it works fine for me; very useful to send data to PC.