前面兩篇博文已經(jīng)實現(xiàn)了電機(jī)測速和PID速度環(huán)控制,在這篇博文中,我們主要說明位置環(huán)的代碼怎么寫以及PID參數(shù)怎么調(diào)。
1. 位置環(huán)代碼實現(xiàn)
? 寫完速度環(huán)后位置環(huán)就很簡單了。
? 在串級PID中,內(nèi)環(huán)的控制量一般是外環(huán)控制量的微分。在我們這里,外環(huán)是控制量是電機(jī)轉(zhuǎn)動的位置(也可以說是角度),內(nèi)環(huán)是電機(jī)轉(zhuǎn)動的速度,剛好滿足這個微分關(guān)系。
? 我們的思路是這樣的,我們給外環(huán)PID設(shè)定電機(jī)轉(zhuǎn)動的目標(biāo)位置,位置環(huán)PID計算得到電機(jī)此時的理想轉(zhuǎn)速,送到內(nèi)環(huán)速度環(huán),速度環(huán)計算得到此時PWM理想的占空比,并輸出給電機(jī)。雙環(huán)PID就需要兩個反饋量,速度環(huán)的反饋量仍然是電機(jī)的速度,而位置環(huán)的反饋量可以使用編碼器輸出的脈沖總數(shù)。由于電機(jī)正轉(zhuǎn)時脈沖總數(shù)會增加,而反轉(zhuǎn)時脈沖總數(shù)會減少,所以脈沖總數(shù)其實是和電機(jī)轉(zhuǎn)動的位置一一對應(yīng)的。
? 位置環(huán)實現(xiàn)代碼如下
? 因為死區(qū)和剎車這些東西位置環(huán)和速度環(huán)不一樣,為了和速度環(huán)區(qū)分開,我們需要在PID.c中加上一個位置環(huán)的PID函數(shù)。
PID pid_speed,pid_position;
/**********************************
* 功能:PID結(jié)構(gòu)體參數(shù)初始化
* 輸入:無
* 返回:無
* *******************************/
void PID_Init(void)//PID參數(shù)初始化
{
pid_speed.err = 0;
pid_speed.integral = 0;
pid_speed.maxIntegral = 1000;
pid_speed.maxOutput = __HAL_TIM_GetAutoreload(&PWM_TIM);
pid_speed.lastErr = 0;
pid_speed.output = 0;
pid_speed.kp = KP_speed;
pid_speed.ki = KI_speed;
pid_speed.kd = KD_speed;
pid_position.err = 0;
pid_position.integral = 0;
pid_position.maxIntegral = 80;
pid_position.maxOutput = __HAL_TIM_GetAutoreload(&PWM_TIM);
pid_position.lastErr = 0;
pid_position.output = 0;
pid_position.kp = KP_position;//這幾個宏定義要自己補(bǔ)充
pid_position.ki = KI_position;
pid_position.kd = KD_position;
}
/****************************************
* 作用:位置環(huán)PID計算
* 參數(shù):PID參數(shù)結(jié)構(gòu)體地址;目標(biāo)值;反饋值
* 返回值:無
* ****************************************/
float Location_PID_Realize(PID* pid,float target,float feedback)//一次PID計算
{
if(pid->err < 0.5 && pid->err > -0.5) pid->err = 0;//pid死區(qū)
pid->err = target - feedback;
pid->integral += pid->err;
if(pid->ki * pid->integral < -pid->maxIntegral) pid->integral = -pid->maxIntegral / pid->ki;//積分限幅
else if(pid->ki * pid->integral > pid->maxIntegral) pid->integral = pid->maxIntegral / pid->ki;
pid->output = (pid->kp * pid->err) + (pid->ki * pid->integral) + (pid->kd * (pid->err - pid->lastErr));//全量式PID
//輸出限幅
if(pid->output > pid->maxOutput) pid->output = pid->maxOutput;
if(pid->output < -pid->maxOutput) pid->output = -pid->maxOutput;
pid->lastErr = pid->err;
return pid->output;
}
? 定時器函數(shù)如下
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
Motor_Contorl(htim);//我把測速和PID封裝成一個函數(shù)了
}
void Motor_Contorl(TIM_HandleTypeDef *htim)
{
Motor_Get_Speed(htim);//得到電機(jī)轉(zhuǎn)速
Now_Position = (float)(motor1.totalCount-10000)// 得到當(dāng)前位置 10000編碼器脈沖計數(shù)的初始值
Target_Speed = Location_PID_Realize(&pid_position,Target_Position,Now_Position);//位置環(huán) Target_Position是目標(biāo)位置,自行定義即可
motor_Out = Speed_PID_Realize(&pid_speed,Target_Speed,motor1.speed);//速度環(huán)
if(motor_L_Out >= 0)
{
__HAL_TIM_SetCompare(&MOTOR_TIM, MOTOR_CHANNEL_FORWARD, 1000);
__HAL_TIM_SetCompare(&MOTOR_TIM, MOTOR_CHANNEL_BACKWARD, 1000-motor_Out);
}
else
{
__HAL_TIM_SetCompare(&MOTOR_TIM, MOTOR_CHANNEL_BACKWARD, 1000);
__HAL_TIM_SetCompare(&MOTOR_TIM, MOTOR_CHANNEL_FORWARD, 1000+motor_Out);
}
}
? 現(xiàn)在位置環(huán)的代碼就完成了,下一步我們需要進(jìn)行PID調(diào)參
2. 位置環(huán)PID調(diào)參
? 位置環(huán)調(diào)參和速度環(huán)有很大區(qū)別,按我的經(jīng)驗來說,一般用不到I和D,我們只要調(diào)整P就好。
? 我們從0開始逐步增大P,直到電機(jī)在前往目標(biāo)位置的過程中是滿速,而到達(dá)目標(biāo)位置后不會超調(diào)、震蕩就行,位置環(huán)調(diào)好后曲線應(yīng)該是這樣的:
? 上圖中,紅線是目標(biāo)位置,綠線是當(dāng)前位置,這里的位置并不是脈沖數(shù),而是換算到了實際場景中,單位是cm,用脈沖數(shù)也是一樣的。文章來源:http://www.zghlxwxcb.cn/news/detail-594277.html
如果覺得電機(jī)達(dá)到目標(biāo)速度的過程中速度過快,可以對位置環(huán)的輸出進(jìn)行限幅。文章來源地址http://www.zghlxwxcb.cn/news/detail-594277.html
到了這里,關(guān)于【STM32】使用HAL庫進(jìn)行電機(jī)PID位置環(huán)控制,代碼+調(diào)參的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!