電賽練習之旋轉倒立擺


2019年電賽已經結束,雖然結果不能令人滿意,但閑下來,還是總結一下電賽學到的東西與失敗的地方。這一次先來談一下一階旋轉倒立擺。

一、題目分析:

拿到一道題目,其實最應該做的事情是分析題目,因為我們往往可以發現某些發揮題是在基礎題的基礎上進行的,但是,可能某些發揮題需要在基礎題的基礎上修改結構,我們也可以發現,題目中的某些問題具有相似性,當我們合並同類項的時候,可以把題目的要求變得簡單。一下,我粘貼過來2013年一階旋轉倒立擺的題目以及要求:
在這里插入圖片描述
在這里插入圖片描述在這里插入圖片描述
我們很容易知道本題的核心是使得擺桿保持穩定,之后再其基礎上進行一些功能的延伸。為了更好地理解題意,我做了如下的分析:

graph TD; 一階旋轉到倒立擺-->手動起擺; 一階旋轉到倒立擺-->自動起擺; 手動起擺-->擺桿的運動; 自動起擺-->擺桿的運動; 擺桿的運動-->電動機的控制;

可以看到,題目中所要求完成的就是通過對一個電機的控制來實現的,控制題的核心還是要落在控制上。歷屆電子設計大賽經常被人戲稱為電子機械設計大賽,雖然控制題的核心是落在控制上,但系統的機械結構也至關重要。此次練習采用的是平衡小車之家的套件,所以省去了搭硬件的很多麻煩。單單就完成題目要求的功能來說,數值分析並不是很必要(強迫症或者沖擊國獎者除外)。因此,本篇內容僅僅從代碼的角度來進行分析。
俗話說的好,不寫注釋的程序猿是流氓。我個人很喜歡在主函數的前面寫上所用引腳的定義,這樣一來方便接線,二來可以避免引腳重復使用,造成不必要的麻煩。

/************************************************
圓周倒立擺實驗
ADC:PA1    Motor:PC0 PC1   Pwm:PB4 PB5
編碼器A相:PB6  B相:PB7
按鍵:PA0 2 3 4 
      
					
					 @JackFu
************************************************/

先來談一談我程序的風格,
一、
在主函數里我將初始化的函數寫在了Board_Init();這個函數里,這樣看起來比較方便后期的更改與檢查,減少了主函數的代碼量,使得主函數看起來簡潔、調理。

void Board_Init(void)
{
	delay_init();
	Adc_Init();
	KEY_Init();
	Ecoder_TIM4_Init(); 
	Motor_Init();
	Timer_pwm_Init(999,8);
//	Timer_Init_TIM5(4999,71);
//	TIM_SetCompare2(TIM3,800);
	uart_init(115200);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
}

二、我一般習慣於建立兩個文件夾:HARDWARE、CONTROL。其中前者用來存放於硬件相關的一些配置函數,后者用來放與軟件相關的一些配置函數。這樣方便后期的維護與移植。

對於其中用到的基本配置,如ADC,ENCODER,MOTOR等等,此處不再贅述。網上有很多的例程與講解。
該題的核心控制為電機,關鍵函數如下:

//電機調節函數    (控制電機倒立,很重要)
void Motor_Config(short pwm)
{
	int speed;
	if(start == 1)                         //開啟倒立,調節倒立擺平衡
	{
		if(pwm<0 && pwm > -PWM_MAX)
		{
			speed = PWM_MAX + pwm;
			if(speed < 0)
			{
				speed = 0;
			}
			TIM_SetCompare2(TIM3,speed);
			Motor_reverse();
		}
		else if(pwm > 0 && pwm < PWM_MAX)
		{
			speed = PWM_MAX - pwm;
			if(speed > 400)
			{
				speed = 400;
			}
			TIM_SetCompare2(TIM3,speed);
			Motor_Forward();
		}
	}
	else                                   //電位器偏角過大,無法調節,為了安全,關閉電機
	{
		Motor_stop();
	}
}

傳入的參數為電機ENCODER,電位器ADC采集值后經過PID處理的數據,具體的處理函數如下:

short PID_Pos_PosCalc(short NextPoint)
{
  register float  iError,dError;
	iError = pos_Set_Pos - NextPoint;        				// 偏差
	pos_SumError += iError*0.2;				    				// 積分
	if(pos_SumError > 1000.0)								//積分限幅1000
		pos_SumError = 1000.0;
	if(pos_SumError < -1000.0)
		pos_SumError = -1000.0;	
	dError = iError - pos_LastError; 						// 當前微分
	pos_LastError = iError;
	
	return(short)(pos_p * iError*0.8 + pos_i * pos_SumError + pos_d * dError);	//返回計算值
}


short PID_Ang_PosCalc(short NextPoint)
{
  register float  iError,dError;
	iError = ang_Set_Pos - NextPoint;        				// 偏差
	ang_SumError += iError;				    				// 積分
	if(ang_SumError > 1000.0)								//積分限幅1000
		ang_SumError = 1000.0;
	if(ang_SumError < -1000.0)
		ang_SumError = -1000.0;	
	dError = iError - ang_LastError; 						// 當前微分
	ang_LastError = iError;
	
	return(short)(ang_p * iError + ang_i * ang_SumError + ang_d * dError);	//返回計算值
}
void Task3(void)
{
	short temp,temp1,temp2;
	temp = 0;
	temp1 = ANG_MIN_VAL;										//角度最大值
	temp2 = ANG_MAX_VAL;										//角度最小值
	
	ang_Cur_Pos = Get_Adc_Average(ADC_Channel_1,15);			//獲取當前角度
	
	
	ang_Set_Pos = ANG_MID_VAL;									//將平衡時位置角度應當采樣的值賦給設定值
	
	if((ang_Cur_Pos > temp1)&&(ang_Cur_Pos<temp2))
	{
		temp += PID_Ang_PosCalc(ang_Cur_Pos);					//角度還在可以調整的范圍內	
		start = 1;		
	}
	
	else
	{															//角度過大,調整不了,為了安全,關機
		start = 0;
	}
	
//	if(++Position_Target>5)
		pos_Cur_Pos = PID_Pos_PosCalc(Encoder),Position_Target=0;
	temp -= pos_Cur_Pos;
//	if(pos_Cur_Pos > 0 && flag == -1)
//	{
//		ANG_MID_VAL = ANG_MID_VAL-5;
//		flag = flag*(-1);
//	}
//	else if(pos_Cur_Pos < 0 && flag == 1)
//	{
//		ANG_MID_VAL = ANG_MID_VAL+5;
//		flag = flag*(-1);
//	}

	Motor_Config(temp);
	printf("%d    ",temp);
	printf("%d\r\n",Encoder);
}

該題的控制采用了雙環的PID來進行處理,在進行學習的過程中發現,僅僅采用電位器環的PID可以使得擺桿倒立起來,但整個擺桿會整體順時針或者逆時針的旋轉,並不能滿足題目要求。正如前面所述,在考慮機械結構時,要整體考慮,否則當做到這里時,發現需要用雙環來進行控制,再改機械結構,會浪費很多的時間。

這里附上源碼 (鏈接: Mycode(雙環控制)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM