View previous topic :: View next topic |
Author |
Message |
nasbyc
Joined: 27 Apr 2009 Posts: 50
|
pid motor |
Posted: Fri Aug 19, 2011 9:20 am |
|
|
Hi I try to control dc motor angle with pot (single turn) as my feedback. I used l293d to control the direction of the motor. I have been testing this pid for a month, however up until now still couldn't move it to the desired angle without having high overshoot and long settling time. I post my code here, because not quite sure either its my pid or pwm setting might be wrong.
Code: |
#include <16f877a.h>
#device adc = 10
#use delay(clock=20000000)
#fuses hs,noprotect,nowdt,nolvp
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, parity=N)
void MotorFish_CW(); void MotorFish_CCW(); void MotorFish_Stop ();
void main ()
{
set_tris_b(0b11100001); //
setup_port_a(ALL_ANALOG);
//setup_adc(ADC_CLOCK_INTERNAL);
setup_adc(ADC_CLOCK_DIV_32);
setup_ccp1(CCP_PWM);
setup_ccp2(CCP_PWM);
setup_timer_2(T2_DIV_BY_4, 250, 1); //Frequency = 5kHz
set_adc_channel(0);
delay_us(10);
int16 P_Error = 0;
int16 Prev_Error = 0;
int16 Acc_Error = 0;
int16 I_Error,D_Error;
int8 P = 160;
int8 I = 3;
int8 D = 7;
int16 Desired_Angle,Theta_out;
signed int32 U_Signal;
int Motor_Status;
signed int32 numeric_val;
byte mystat=0;
char mydata;
Desired_Angle = 527; //approx 8 degree
MotorFish_Stop();
for(;;)
{
if(kbhit())
{ mydata=getch();
mystat=1;}
if(mystat==1)
{
P_Error = 0;
Acc_Error = 0;
if(mydata=='a'||mydata=='A')
{
Desired_Angle = 512;
}
if(mydata=='b'||mydata=='B')
{
Desired_Angle = 527;
}
if(mydata=='c'||mydata=='C')
{
Desired_Angle = 487;
}
mystat=0;
}
Theta_out = read_adc();
if(Theta_out>= 731)
{
MotorFish_Stop();
}
printf("Desired_Angle:%Ld and Theta_out:%Ld\n", Desired_Angle,Theta_out);
delay_ms(33);
//I_Error = Acc_Error + P_Error;
P_Error = Desired_Angle - Theta_out;
if (abs(P_Error) <= 3)
{
P_Error = 0;
Acc_Error = 0;
}
I_Error = Acc_Error + P_Error;
D_Error = (P_Error - Prev_Error);
U_Signal = P_Error*P + I_Error*I + D_Error*D;
numeric_val = U_Signal & 0x7FFF;
if (U_Signal & 0x8000)
U_Signal = -32768 + numeric_val;
if(U_Signal > 0)
{
Motor_Status = 0;
}
else if(U_Signal < 0)
{
Motor_Status = 1;
}
else
MotorFish_Stop();
Prev_Error = P_Error;
Acc_Error = Acc_Error + P_Error;
if(U_Signal > 32767)
{
U_Signal = 32767;
}
else if(U_Signal < -32767)
{
U_Signal = 32767;
}
U_Signal = U_Signal*((float)1023/32767);
U_Signal = abs(U_Signal);
if(Motor_Status == 0)
{
MotorFish_CW();
set_pwm2_duty(U_Signal);
}
else if(Motor_Status == 1)
{
MotorFish_CCW();
set_pwm2_duty(U_Signal);
}
}
}
void MotorFish_CW()
{
output_low(PIN_B1);
output_high(PIN_B2);
}
void MotorFish_CCW()
{
output_high(PIN_B1);
output_low(PIN_B2);
}
void MotorFish_Stop()
{
output_low(PIN_B1);
output_low(PIN_B2);
} |
|
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9174 Location: Greensville,Ontario
|
|
Posted: Fri Aug 19, 2011 11:46 am |
|
|
PID systems have to be 'tuned' based upon 'what happens when these commands' are issued. In the good old days this meant a lot of trial and error, recording of data, testing, etc. Nowadays you could use Matlab (or other software' to get a good 'feel' for the numbers.
You might 'google' PID calculators or something like that for help.
My experience lies in PID with DC motors (servo systems) that send voltages to the motor not PWM signals. Good old school stuff with LM12CLK power op amps. |
|
![](templates/subSilver/images/spacer.gif) |
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Fri Aug 19, 2011 4:03 pm |
|
|
I wonder if you'll get anywhere doing this in integer math, though one hates to force the processor to do floating point. But I don't think it can be done any other way. Try looking up PIC PID in Google and you might get some ideas. I think getting the derivative term (velocity) from a pot might be a big problem too. |
|
![](templates/subSilver/images/spacer.gif) |
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Fri Aug 19, 2011 7:24 pm |
|
|
Properly scaled 16 bit ints should be fine. If people do FFTs with ints on PICs, doing PID should be easy. Using floats may lead to lag problems from the computation time. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
![](templates/subSilver/images/spacer.gif) |
nasbyc
Joined: 27 Apr 2009 Posts: 50
|
|
Posted: Mon Aug 22, 2011 5:08 am |
|
|
I already simulate it using matlab and obtained the desired output, the problem are the values for P , I , D are in float. Whenever I tried to change the int to float, it produces an error in calculation and the duty cycle just set to the max which is 255. |
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9174 Location: Greensville,Ontario
|
|
Posted: Mon Aug 22, 2011 5:34 am |
|
|
You've got Usignal declared as a signed 32 integer.._+ and - values)
signed int32 U_Signal;
and the PICs PWM module only needs an 8 or 16 bit integer(uses only 10 bits though).
Somewhere 'in the math' you need to 'cast' the proper values.
I suggest doing the math by hand (or on a PC) to see what the correct numbers should be, then write a small program to have the PIC display the results. Say full right, 1/2 right, center, half 1/2 left, full left. This should show you where your original math went wrong.
Been there done that !! |
|
![](templates/subSilver/images/spacer.gif) |
nasbyc
Joined: 27 Apr 2009 Posts: 50
|
|
Posted: Mon Aug 22, 2011 6:07 am |
|
|
Temtronic, what do you mean by cast the proper values?. Is it means that I have to put for example input scale factor to be multiply to my proportional, integral and derivative term. |
|
![](templates/subSilver/images/spacer.gif) |
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Mon Aug 22, 2011 6:42 am |
|
|
In most cases, a PID can work with 16 bit signed integer. The trick is to aplly suitable scaling. A 16*16 multiply results in a 32 bit number that has to be reduced to 16 bit appying suitable truncation with saturation. |
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9174 Location: Greensville,Ontario
|
|
Posted: Mon Aug 22, 2011 7:05 am |
|
|
Casting is C's way of converting variables from one type to another.
Variables can be bits, bytes, words, double words (1,8,16,32 bits wide).
Depending on the 'math' or other operation performed, the results of using 'mixed' sized variables can be a disaster!
That's why it's important to 'try the math' with known values BEFORE you run a servo plant ! Just have the PIC display the numbers and compare to what you KNOW they should be.
also...
Check the onscreen CCS C compiler help (press F11) and look for 'variable types' or similar wording.
You should also 'Google' for a 'C for dummies' type book or reference to better understand C. Like every high level language it has it's quirks. |
|
![](templates/subSilver/images/spacer.gif) |
|