본문 바로가기

Enginius/Firmware

PID 제어

제어에 쓰이는 PID
개념은 너무나 쉽기 때문에 설명하지 않겠다.
게인을 잡는 것은 너무 어렵기 때문에 그것도 설명하지 않겠다.

그냥 경험상 P게인 잡고, D게인 잡고, I게인 잡고, 다시 반복하면 된다.
스텝 리스펀스를 이용해서 잡으면 된다.

헤더 파일

typedef struct {	float  Ref;   			// Input: Reference input 
			float  Fdb;   		// Input: Feedback input 
			float  Err;			// Variable: Error
			float	 ErrSum;		// Variable: Error Sum
			float  Kp;			// Parameter: Proportional gain
			float  Up;			// Variable: Proportional output 
			float  Ui;			// Variable: Integral output 
			float  Ud;			// Variable: Derivative output 	
			float  OutPreSat; 		// Variable: Pre-saturated output
			float  OutMax;		// Parameter: Maximum output 
			float  OutMin;	    	// Parameter: Minimum output
			float  Out;   		// Output: PID output 
			float  iMax;		// Parameter: Maximum Integration
			float  Ki;			// Parameter: Integral gain				  
			float  Kd; 		        // Parameter: Derivative gain
			float  ErrPrev;	   	// History: Previous error				
	 	 	void  (*calc)();	  	// Pointer to calculation function
			} PIDREG3;	 				            
typedef PIDREG3 *PIDREG3_handle;

#define PWM_MIN		1500 


// 20ms PID 제어주기 
// 속도 제어 PID 게인  
#define L_Kp 	33.0		//30.0		//30.0		
#define L_Ki 	6.0		//5.0		//8.0
#define L_Kd 	0
#define L_OutMax 	4900.0
#define L_OutMin 	-4900.0
#define L_iMax 	4900.0

void pid_reg3_calc_v(PIDREG3_handle);

#define PIDREG3_V_LEFT     { 0.0, 0.0, 0.0, 0.0, L_Kp, 0.0, 0.0, 0.0, 0.0, \
                           L_OutMax, L_OutMin, 0.0, L_iMax, L_Ki, L_Kd, 0.0, \
                           (void (*)(Uint32))pid_reg3_calc_v }

 


소스 파일

void pid_reg3_calc_v(PIDREG3 *v)
{
	float outpresave;
	
	// Compute the error
	v->Err = v->Ref - v->Fdb;

	// Compute the error sum
	v->ErrSum = v->ErrSum + v->Err;

	// Compute the proportional output
	v->Up = v->Kp * v->Err;

	// Compute the integral output
	v->Ui = v->Ki*v->ErrSum;
	if(v->Ui > v->iMax) 			v->Ui = v->iMax;
	else if(v->Ui < (-1.0 * v->iMax) ) 	v->Ui = -1.0 * v->iMax;

	// Compute the derivative output
	v->Ud = v->Kd * (v->Err - v->ErrPrev);

	// Compute the pre-saturated output
	v->OutPreSat = v->Up + v->Ui + v->Ud;
	outpresave = v->OutPreSat;

	// PWM 값이 20KHz에서 0 ~ 4000 까지의 값을 가질 수 있지만
	// 최소 어느 정도의 값을 줘야 실제 로봇(O2)의 바퀴가
	// 천천히 돌기 시작하기 때문에 다음과 같이 한다.
	if(v->OutPreSat < PWM_MIN && v->OutPreSat > 0.0 && v->Ref!=0) 		v->OutPreSat = PWM_MIN;
	else if(v->OutPreSat > -PWM_MIN && v->OutPreSat < 0.0 && v->Ref!=0)	v->OutPreSat = -PWM_MIN;

	// Saturate the output
	if(v->OutPreSat > v->OutMax)	v->Out =  v->OutMax;
	else if(v->OutPreSat < v->OutMin)	v->Out =  v->OutMin;
	else 						v->Out = v->OutPreSat;
	v->ErrPrev = v->Err;

//	myprintf("Vcalc: ");
//	myprintf("Kp: %d   Ki: %d   Kd: %d \r\n", (int)v->Kp, (int)v->Ki, (int)v->Kd );
//	myprintf("Ref: %d   Fdb: %d   Err: %d   ErrSum: %d   Outpre: %d(%d+%d+%d)   Out: %d  \r\n", (int)(v->Ref), (int)(v->Fdb), (int)(v->Err), (int)(v->ErrSum), (int)(outpresave), (int)(v->Up), (int)(v->Ui), (int)(v->Ud), (int)(v->Out) );
}

 


실제 사용

PIDREG3 pidVR = PIDREG3_V_RIGHT;
PIDREG3 pidVL = PIDREG3_V_LEFT;

		{
			........
			pidVR.Fdb = (float)(g_EncoderR - Encoder_R_prev);
			pidVL.Fdb = (float)(g_EncoderL - Encoder_L_prev);
			pidVR.calc(&pidVR);
			pidVL.calc(&pidVL);		
			rightS = (int32)pidVR.Out;
			leftS = (int32)pidVL.Out;
			SetDriveDirection(leftS, rightS);
			mR_PWM(rightS);
			mL_PWM(leftS);							
		}
		Encoder_R_prev = g_EncoderR;
		Encoder_L_prev = g_EncoderL;

'Enginius > Firmware' 카테고리의 다른 글

PIC32 - PWM  (0) 2010.02.19
PIC32 - Input Capture  (0) 2010.02.19
PIC32 - I2C  (0) 2010.02.17
PIC32 - GPIO  (0) 2010.02.17
PIC32 - Interrupt  (0) 2010.02.17