| View previous topic :: View next topic   | 
	
	
	
		| Author | 
		Message | 
	
	
		
			Gabriel
 
 
  Joined: 03 Aug 2009 Posts: 1074 Location: Panama 
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				| PID issues - with Code | 
			 
			
				 Posted: Tue Jan 16, 2024 4:35 pm     | 
				     | 
			 
			
				
  | 
			 
			
				Hi all, its been a while.
 
 
after 20 years of PICs its time for my first PID proyect...
 
 
I got 2 small 12v brushed DC motores with gear reductions and magnetic encoders on the back:
 
https://www.amazon.com/uxcell-Motor-Encoder-201RPM-Ratio/dp/B0792SBB5T
 
...driven by the classic L298 red PCB available under any rock.
 
 
Im using one motor as output (plant) and one as input by simply spining the encoder manually.
 
 
the goal is to be able to mirror the input to the output.
 
input encoder goes to 200 steps, the PID driven motor goes to 200 steps.
 
"simple", right.
 
 
there is no load on my output motor, just a plastic arrow for visual confirmation.
 
 
I did a lot of testing and got the best PWM response at a low frequency. lowest i was able to get was 133Hz which gave me a PWM range of 50 to 255 Duty.
 
 
anything below 50 the motor would not start, just vibrate.
 
while spinning it would go down to 25(duty).
 
 
PROBLEMS:
 
1) I would like to bring the PWM frequency lower. possibly 100 or 60Hz as it seems to continue to improve response. i tested using the 32MHz internal clock but was not able to get it working (the PWM) reliably... but in the moments it did work, i did get well below the previous 50 duty limit at 133hz.
 
 
2) I have crazy jitter/chatter on my motor when near the setpoint.
 
ive adjusted K gains as much as any sane person could... and get close to no improvement... probably worse.
 
any help going over the logic of my code would be awesome... maybe i have some bug i cant see from clawing my eyes out with this.
 
 
3) Having a faster loop time (calls to Compute()) seems to improve things. makes sense. BUT because im bringing the clock down to get lower PWM frequencies im stuck in this push pull scenario where fixing something breaks another...
 
 
current code below has me at a jittery medium... faster loop, not so low PWM which limits my duty 100-255.... kinda works.
 
 
help?
 
 
CODE:
 
 	  | Code: | 	 		  
 
#include <18F67K40.h>
 
#device PASS_STRINGS=IN_RAM
 
#device adc=10
 
#fuses MCLR
 
#fuses NOWDT
 
#fuses NOLVP
 
#fuses NOXINST
 
#fuses NODEBUG
 
#fuses NOPROTECT
 
#fuses HS
 
#fuses RSTOSC_EXT_PLL
 
#use delay(clock=32MHz)
 
 
#include <string.h>
 
#include <stdlib.h>
 
 
#include <Hardware - AZ.h>
 
 
 
#INT_EXT
 
void Encoder_1();
 
#INT_EXT1
 
void Encoder_2();
 
 
#INT_TIMER2
 
void Timer_2_ISR();
 
 
void Set_Motor_1(int,int);
 
void Compute_PID();
 
 
signed int32 Control_One_Position=0; //setpoint or...
 
signed int32 Motor_One_Position=0;
 
signed int32 Set_Point=0;            //Control_one_positon depending on what im doing...
 
signed int32 Last_Set_Point=0;
 
 
float Kp=10;        //the guessing game #1
 
float Ki=0;         //the guessing game #2
 
float Kd=1;         //the guessing game #3
 
float Interval=0.0082;          //Timer 2 is calling Compute() at about 8.2ms as per O-Scope
 
 
signed int32 Error=0;
 
signed int32 Error_Prev=0;
 
float Integral=0;
 
float Derivative=0;
 
float Output=0;
 
int16 M1_Duty=0;
 
int Current_Direction=0;
 
 
//========================== 
 
void main() 
 
{  
 
    setup_oscillator(OSC_EXTOSC_PLL|OSC_CLK_DIV_BY_2|OSC_EXTOSC_ENABLED|OSC_EXTOSC_READY);
 
    setup_timer_2(T2_DIV_BY_128|T2_CLK_INTERNAL,255,2);
 
    delay_ms(1000); //Stabilize CLK
 
    
 
    setup_ccp1(CCP_PWM);   // Configure CCP1 as a PWM
 
    setup_pwm6(PWM_ENABLED|PWM_ACTIVE_HIGH|PWM_TIMER2); //Uses timer 2
 
    
 
    enable_interrupts(INT_TIMER2);     //Calls PID Compute at regular intervals
 
    enable_interrupts(INT_EXT);        //Motor Encoder
 
    enable_interrupts(INT_EXT1);       //Control Encoder
 
    
 
    ext_int_edge(0, INT_EXT_L2H);       //Encoder interrupts Edges
 
    ext_int_edge(0, INT_EXT1_L2H);      //Encoder2 interrupts Edges
 
    
 
    enable_interrupts(GLOBAL);
 
    
 
    fprintf(FTDI,"\fSTART\r\n"); //just to know things are running...
 
    delay_ms(1000); //to be able to read the start message.
 
  
 
    
 
    while(TRUE)
 
    {
 
        fprintf(FTDI,"%03Ld,%03Ld,%03Ld,%3.2f,%3.2f,%lu,Dir:%d\r\n",Set_Point,Motor_One_Position,Error,Integral,Derivative,M1_Duty,Current_Direction);
 
        
 
        
 
        /*
 
        //FOR PWM TESTING
 
        fprintf(FTDI,"Position:%Lu - PWM:%Lu\r\n",Motor_One_Position, M1_Duty);
 
        Set_Motor_1(1,M1_Duty);
 
        delay_ms(1000);
 
        
 
        if(Flag==1)
 
            M1_Duty+=5;  
 
        else
 
            M1_Duty-=5; 
 
       
 
        if(M1_Duty==250)Flag=0;
 
        if(M1_Duty==25)Flag=1;
 
        */
 
        //fprintf(FTDI,"Position:%04Ld\r",Control_One_Position);
 
    }
 
    
 
}
 
 
void Compute_PID()
 
{
 
    output_high(PIN_H2);                        //set pins to see call frequency and execution time.
 
    
 
    //PROPORTINAL
 
    Error=Set_Point-Motor_One_Position;
 
    //INTEGRAL
 
    Integral+=(Error*Interval);
 
    if(Integral>255)Integral=255;               //Cap Integral at 255 to prevent windup
 
    else if(Integral<-255)Integral=-255;
 
    //DERIVATIVE
 
    Derivative=(Error-Error_Prev)/Interval;
 
    if(Derivative>255)Derivative=255;           //Cap Derivative at 255 --delete??
 
    else if(Derivative<-255)Derivative=-255;
 
    
 
    //COMPUTE OUTPUT
 
    Output=(Kp*Error)+(Ki*Integral)+(Kd*Derivative);
 
    
 
    M1_Duty=abs(Output);      
 
    if(M1_Duty>255)M1_Duty=255;             //ensure duty is within values that actually cause the motor to spin
 
    if(M1_Duty<50)M1_Duty=50;
 
      
 
    if(Error==0)                            //added this because otherwise it never stops jittering
 
    Set_Motor_1(0,M1_Duty);                 //dispair solution, dont judge.
 
    
 
    else if(Output<0)                       //control the motor This way
 
    Set_Motor_1(-1,M1_Duty);
 
    else                                    //control the motor That way
 
    Set_Motor_1(1,M1_Duty);
 
 
 
    Error_Prev=Error;
 
    
 
    output_low(PIN_H2);                     //set pins to see call frequency and execution time.
 
}
 
 
void Set_Motor_1(int Dir,int Duty)          //controls a L298 H-bridge board.
 
{
 
    set_pwm1_duty(Duty);                    //Change PWM Duty
 
    
 
    if(Dir!=Current_Direction)              //figured it was a good idea to bring both Ctrl lines low
 
    {                                       //before changing direction on the L298
 
        output_low(M1_FWD);                 //avoid any timing or whatever pin switching mishappenings.
 
        output_low(M1_RWD);
 
    }
 
 
        if(Dir==1)
 
        {
 
            output_high(M1_FWD);
 
            output_low(M1_RWD);
 
        }
 
        else if(Dir==-1)
 
        {
 
            output_low(M1_FWD);
 
            output_high(M1_RWD);
 
        }
 
        else
 
        {
 
            output_low(M1_FWD);
 
            output_low(M1_RWD);
 
            Duty=0;
 
        }
 
 
    Current_Direction=Dir;
 
}
 
 
//encoder reading stuff.
 
#INT_EXT
 
void Encoder_1()
 
{
 
    if(input(ENC_B)==TRUE)
 
        Motor_One_Position++;
 
    else Motor_One_Position--;
 
}
 
#INT_EXT1
 
void Encoder_2()
 
{
 
    if(input(CTRL_B)==TRUE)
 
        Set_Point+=10;          //since im spining this encoder by hand ive added a multiplier to make changes bigger.
 
        //Control_One_Position++;
 
    else //Control_One_Position--; 
 
        Set_Point-=10;
 
}
 
 
#INT_TIMER2
 
void Timer_2_ISR()
 
{  
 
    Compute_PID();
 
}
 
 | 	  
 
 
there is a re-entrancy warning on the timer 2 ISR.
 
I know, i need help with that too.
 
 
 
i would like to eventually move this to the 18F47Q43 or Q84 - i have both so if pic choice makes a diference, let me know!
 
 
thanks G. _________________ CCS PCM 5.078 & CCS PCH 5.093 | 
			 
		  | 
	
	
		  | 
	
	
		
			newguy
 
 
  Joined: 24 Jun 2004 Posts: 1924
  
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		 | 
	
	
		  | 
	
	
		
			Ttelmah
 
 
  Joined: 11 Mar 2010 Posts: 19967
  
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Wed Jan 17, 2024 2:12 am     | 
				     | 
			 
			
				
  | 
			 
			
				Several things.
 
 
First the L298, is not a great driver. It introduces a lot of voltage drop 
 
(typically about 2v). To drive a motor well, you need a good driver that 
 
allows narrow PWM pulses at full power. 
 
Second, your PID loop should not use floating point maths. The PID
 
calculation wants to be quick. Look at using scaled integer maths. This
 
has been discussed here before. This is your main problem. The sheer 
 
time needed by the loop means the motor has started moving the wrong
 
way before the loop corrects. So oscillation. 
 
Then voltage. Now it sounds odd here, this is a 12v motor, but the way
 
to drive a motor slower with PWM, is to use a much higher supply voltage!.
 
You have to setup the PWM and controller to limit the maximum current 
 
at the maximum the motor is rated for, but with any inductive load, the
 
speed the current rises in the coil when power is applied, is limited by
 
the supply voltage. Apply a really narrow pulse, and it takes time for the
 
current to build. I have an industrial controller here using 24v servos. The
 
supply voltage is 150v DC!...
 
Chattering is a sign your servo parameter are not correctly tuned. Some 
 
research online will find a lot of posts about how best to perform this
 
tuning, and even programs to help with this. | 
			 
		  | 
	
	
		  | 
	
	
		
			temtronic
 
 
  Joined: 01 Jul 2010 Posts: 9589 Location: Greensville,Ontario 
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Wed Jan 17, 2024 6:57 am     | 
				     | 
			 
			
				
  | 
			 
			
				also...
 
be sure to use a power supply with LOTS of AMPS !
 
While the 298 driver is rated for 2 amps per unit, you need more than a simple 4 amp PSU
 
Depending on layout, wiring, connections,oad, etc. the motor might end up 'current starved'.
 
In the 'dinosaur days' I'd use a linear supply good for 5x the 'needed' current for the servomotors. Maybe 'overkill' but never ,ever had a servo not perform properly. | 
			 
		  | 
	
	
		  | 
	
	
		
			Ttelmah
 
 
  Joined: 11 Mar 2010 Posts: 19967
  
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Wed Jan 17, 2024 7:52 am     | 
				     | 
			 
			
				
  | 
			 
			
				and remember that the trap diodes across the bridge elements are
 
essential, but that thee only work if the supply is capable of sinking 
 
this current, without it's output voltage shooting up...... | 
			 
		  | 
	
	
		  | 
	
	
		
			Gabriel
 
 
  Joined: 03 Aug 2009 Posts: 1074 Location: Panama 
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Wed Jan 17, 2024 4:17 pm     | 
				     | 
			 
			
				
  | 
			 
			
				Im gonna run this again with a 24V psu:
 
 
https://www.mouser.com/ProductDetail/MEAN-WELL/SE-1000-24?qs=4Ewz1atfbqKd24YTAQMbSQ%3D%3D
 
 
The one on the link to be specific... should be enough to run a small 12v motor.
 
If that doesnt cut it i dont know what will...
 
 
Im reading the PID without PHD doc...
 
 
I know L298 is dated... availability and ease of use, available resources, etc made it a good choice for me. Plenty tutorials using it... _________________ CCS PCM 5.078 & CCS PCH 5.093 | 
			 
		  | 
	
	
		  | 
	
	
		
			Gabriel
 
 
  Joined: 03 Aug 2009 Posts: 1074 Location: Panama 
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Wed Jan 17, 2024 6:04 pm     | 
				     | 
			 
			
				
  | 
			 
			
				higher/better power supply did not bring any changes... it settles a bit faster, and the motor gets a bit warm when oscilating.
 
 
im having the same behaviour, just faster jejejeje... _________________ CCS PCM 5.078 & CCS PCH 5.093 | 
			 
		  | 
	
	
		  | 
	
	
		
			Ttelmah
 
 
  Joined: 11 Mar 2010 Posts: 19967
  
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Thu Jan 18, 2024 5:50 am     | 
				     | 
			 
			
				
  | 
			 
			
				Which means the parameters of the PID are set wrong. You are 
 
over-correcting for the initial error and then not damping enough. | 
			 
		  | 
	
	
		  | 
	
	
		
			Gabriel
 
 
  Joined: 03 Aug 2009 Posts: 1074 Location: Panama 
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Thu Jan 18, 2024 6:16 am     | 
				     | 
			 
			
				
  | 
			 
			
				Ive played for hours with the P/D gains... keeping Integral at 0.
 
Ajusting loop times too...when semi acceptable peformance is achieved i add some integral... more.hours... no improvement.
 
 
Is my math right?
 
 
This cant be this dificult...
 
I believe i need to lower my PWM frequency for finer adjustments.
 
With higher voltages my PWM DUTY range narrowed 30 to 100 duty vs 45ish to 255.
 
Up my loop time to as high as possible
 
 
i want to try and use the 32mhz internal clock to drive the PWM/timer2 and use the ext osc with pll at full blast to run the code.
 
I dont know if this is possible... datasheet reading time.
 
This should give me lower PWM and higher loop times. _________________ CCS PCM 5.078 & CCS PCH 5.093 | 
			 
		  | 
	
	
		  | 
	
	
		
			Gabriel
 
 
  Joined: 03 Aug 2009 Posts: 1074 Location: Panama 
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Thu Jan 18, 2024 6:51 am     | 
				     | 
			 
			
				
  | 
			 
			
				PWM has to run from fosc/4... so im at the lowest feasable PWM freq. Which impacts my execution time clock so, slower loop times too.
 
 
I know its possible to go lower with other osc configurations but im trying to keep loop times fast. _________________ CCS PCM 5.078 & CCS PCH 5.093 | 
			 
		  | 
	
	
		  | 
	
	
		
			gaugeguy
 
 
  Joined: 05 Apr 2011 Posts: 350
  
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Thu Jan 18, 2024 7:22 am     | 
				     | 
			 
			
				
  | 
			 
			
				PID aside, you need to test your hardware.
 
Using direct PWM drive, what is the smallest rotation you can reliably move the motor.  Is it less than one encoder count?
 
As you have already found the motor requires more power to start turning than to keep turning.
 
Your PID cannot work better than your hardware is capable of. | 
			 
		  | 
	
	
		  | 
	
	
		
			Gabriel
 
 
  Joined: 03 Aug 2009 Posts: 1074 Location: Panama 
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Thu Jan 18, 2024 7:33 am     | 
				     | 
			 
			
				
  | 
			 
			
				That makes sense... i may be atributing super powers to PID.
 
However i see in youtube plenty people reaching precision with similar hardware.
 
 
How can i test this?
 
Should i set this to accept values within lets say 5% of target?...
 
 
So if im trying to get 100 encoder counts anything within 95 to 105 encoder counts is taken as a succesfull setpoint reach? Is that the suggestion/fix? _________________ CCS PCM 5.078 & CCS PCH 5.093 | 
			 
		  | 
	
	
		  | 
	
	
		
			gaugeguy
 
 
  Joined: 05 Apr 2011 Posts: 350
  
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Thu Jan 18, 2024 7:53 am     | 
				     | 
			 
			
				
  | 
			 
			
				| Regarding PID, what is your PID calculation loop time?  Your sample time? | 
			 
		  | 
	
	
		  | 
	
	
		
			Gabriel
 
 
  Joined: 03 Aug 2009 Posts: 1074 Location: Panama 
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Thu Jan 18, 2024 8:25 am     | 
				     | 
			 
			
				
  | 
			 
			
				i was calling my compute function within the TMR2 ISR which was executing at 8.2ms more or less.
 
 
othertimes in dispair i would call Compute_PID() from within the main loop and nothing else... and that would give me about 250us
 
 
the sample time i have not measured... its interrupt driven with the Encoder pulses EXT_INT.
 
 
 
I tried "slowing" the encoder by having the encoder mark 2 pulses as 1 step. this would make the motor travel twice as far obviously... but i figured it would give the PID more time to react to the measurements...
 
 
I could try again. _________________ CCS PCM 5.078 & CCS PCH 5.093 | 
			 
		  | 
	
	
		  | 
	
	
		
			Gabriel
 
 
  Joined: 03 Aug 2009 Posts: 1074 Location: Panama 
			
			 
			 
			
			
			
			
			
			
			
  
		  | 
		
			
				 | 
			 
			
				 Posted: Sat Jan 20, 2024 7:46 am     | 
				     | 
			 
			
				
  | 
			 
			
				Ive added a conditional to the Error calculation.
 
Basically if im with 0 to 2 counts away from the setpoint just make error =0.
 
Good enough = 0 error jajajaj
 
 
So basically starting from 0 if i setpoint =100 my motor goes to 99 or 101... im getting about 1 count error...
 
 
I also upped my loop time to 4.1ms
 
And increased my min output to 80 instead of 50 duty.
 
 
No integral and a bit of derivative damping and its smooth as butter now.
 
 
 
Ill be testing repeatability and maybe some drift compensation if any (adding back that missed count)... _________________ CCS PCM 5.078 & CCS PCH 5.093 | 
			 
		  | 
	
	
		  | 
	
	
		 |