天天看點

STM32 Cubemax(十五) —— 以控制電機角度值為例,詳解串級PIDSTM32 Cubemax(十五) —— 以控制電機角度值為例,詳解串級PID 前言一、單級PID控制電機角度 二、串級PID 三、串級PID代碼 總結

STM32 Cubemax(十五) —— 以控制電機角度值為例,詳解串級PID

前言

一、單級PID控制電機角度

二、串級PID 

三、串級PID代碼

電機結構體

串級PID計算

串級PID初始化

四、串級PID調參

總結

前言

很早前說要補的坑,今天補一下。之前介紹過單級的PID來控制電機的速度值,建議先看下面這篇文章,因為後面代碼和這篇文章有關聯!

STM32 Cubemax(七) —— 單級PID控制帶編碼器的直流減速電機速度

而這次,我們來講解一下怎麼控制電機的角度,如果用單級PID控制角度會有什麼問題,為什麼要用串級PID。

當然串級PID不隻是可以控制電機的角度,如現在諸多的控制系統,倒立擺,風力擺,平衡小車等等,都是基于串級PID的控制。

一、單級PID控制電機角度

如果還用我們之前熟知的單級PID來控制角度值,我們很容易的可以得出以下框圖。

STM32 Cubemax(十五) —— 以控制電機角度值為例,詳解串級PIDSTM32 Cubemax(十五) —— 以控制電機角度值為例,詳解串級PID 前言一、單級PID控制電機角度 二、串級PID 三、串級PID代碼 總結

 看上去很完美,沒有什麼問題,但這裡面最大的問題就在于單片機給電機的是PWM值(或者電壓值)并不是直接給定電機旋轉的角度值,而可以認為是經過一個函數變換f(PWM)得出的值。

比如這裡以角度為例,PWM輸出的值,經過一個g(x)變換得到速度值,速度值經過h(x)(即積分)得到角度值,f(PWM)即為h(g(x))。

根據上述分析可知,對于簡單的系統來說,這個f函數的變換,可以用較好的PID參數來抵消掉其中的誤差量。但當系統變的複雜,比如平衡小車中,控制的角度不僅是單純電機的角度,而是車上的位姿姿态,這種時候f(x)就較為複雜!這種時候,十分難得到一個PID參數來使系統穩定。

是以當我們遇到控制問題時,先看看這個f函數,是不是屬于我上述說的情況,來看看單級PID是否可以使用。

我們接下來引入串級PID來解決上述f函數出現的問題。

二、串級PID 

其實單級PID來控制角度,出現的具體問題就是控制回報不全導緻的!

以控制角度為例,其中最大的問題不在于h(x),因為h(x)相當于速度對時間的積分,問題在于g(x)的存在,輸出的PWM會經過g(x)得出一個不可控的速度值(因為存在電機阻尼,或者外屆阻力等),那麼此時經過h(x)得出的角度值,也不會如我們想控制的一樣。

那麼解決方案就很簡單了,就是去控制這個速度值,使其輸出的速度值是我們控制器想要得到的值即可了。我們容易得出下面框圖。

STM32 Cubemax(十五) —— 以控制電機角度值為例,詳解串級PIDSTM32 Cubemax(十五) —— 以控制電機角度值為例,詳解串級PID 前言一、單級PID控制電機角度 二、串級PID 三、串級PID代碼 總結

 這裡我們加了一級速度級PID來控制電機的輸出速度,外面外環仍然使用一級角度級PID來控制電機最終速度角度,這就是串級的意義。

其本質内涵,可以認為速度環的引入,相當于給系統增加了阻尼,即抑制了g(x)中因為電機阻尼,外界阻尼等因素的影響。

三、串級PID代碼

其實如果看懂上面串級PID的框圖後,代碼編寫也十分簡單了,就是相當于計算兩次單級PID,其中外環角度環的目标值為設定的角度,回報值為電機回報的角度值,内環速度環的輸入即為外環角度環的輸出,回報為電機回報的速度值。

電機結構體

串級PID結構體

typedef struct _CascadePID
{
	PID inner;    // 内環速度環PID
	PID outer;    // 外環角度環PID
	float output;
}CascadePID;
           

 在電機中加入串級PID

typedef struct _Motor
{
	int32_t lastAngle;       //上次計數結束時轉過的角度
	int32_t totalAngle;      //總共轉過的角度
	int16_t loopNum;         //電機計數過零計數
	float speed;             //電機輸出軸速度
	float targetSpeed;       //添加設定的目标速度
    CascadePID anglePID;     //串級PID 
}Motor;
           

串級PID計算

//串級PID計算,參數為串級PID指針,角度目标值,角度傳回值,速度傳回值
void PID_CascadeCalc(CascadePID *pid,float angleRef,float angleFdb,float speedFdb)
{
	PID_SingleCalc(&pid->outer,angleRef,angleFdb);            //外環角度環
	PID_SingleCalc(&pid->inner,pid->outer.output,speedFdb);   //内環速度環
	pid->output=pid->inner.output;
}
           

串級PID初始化

即對内環和外環的參數進行初始化

PID_Init(&Motor.anglePID.inner,0,0.0,0,0,0);
PID_Init(&Motor.anglePID.outer,0,0,0,0,0,0);
           

然後外面隻需要把Motor_Send裡的單級PID計算更換成我們的串級PID計算,就完成了串級PID的計算,這裡要解釋一下就是這裡我們角度的傳回值,填的是totalAngle,即我們編碼器總讀出來的角度值,是以我們這裡targetAngle是基于totalAngle上的累加值。

void Motor_Send(enum Mode mode)
{
	float output = 0;
	PID_CascadeCalc(&motor.anglePID, motor.targetAngle, motor.totalAngle, motor.speed);
	output = motor.anglePID.output;
	
	if(output > 0)
	{
		__HAL_TIM_SetCompare(&htim5, TIM_CHANNEL_2, (uint32_t)output);
		IN1(0);
		IN2(1);
	}
	else										
	{
		__HAL_TIM_SetCompare(&htim5, TIM_CHANNEL_2, (uint32_t)(-output));
		IN1(1);
		IN2(0);
	}
}
           

四、串級PID調參

我們編寫完有關串級PID的代碼程式後,就到了最關鍵的調參環節。我們采用的調參方式是按照内環穩定,再調外環的方式,由我們上述的分析也可知,隻有内環速度環穩定後,角度環才有意義!

這裡我們需要先利用上次的程式,把内環調好!!,是利用上次的函數,即隻有單級PID控制速度的程式。

我們把單級速度環調好後,将參數填入串級PID的内環中,然後我們就可以開debug去調外環了。

外環的調節方式和内環其實是完全一緻的,具體就不再闡述了,看上篇文章,先P再I,最後D,這些都是按自己的具體項目需求分析,看看到底用PI控制好,還是PD或者是PID控制效果好。

如果在調節外環的時候發現,不管怎麼調都不理想,其中一個可能就是内環沒有調好!!再次去精調内環的值。

總結

本文是根據自己的實際使用經曆來寫的,有可能有些地方表達不是很準确,如果有錯誤,請指正!​​​​​​​

整個代碼放到gitee上了/電子設計驅動