STM32 Cubemax(十五) —— 以控制電機角度值為例,詳解串級PID
前言
一、單級PID控制電機角度
二、串級PID
三、串級PID代碼
電機結構體
串級PID計算
串級PID初始化
四、串級PID調參
總結
前言
很早前說要補的坑,今天補一下。之前介紹過單級的PID來控制電機的速度值,建議先看下面這篇文章,因為後面代碼和這篇文章有關聯!
STM32 Cubemax(七) —— 單級PID控制帶編碼器的直流減速電機速度
而這次,我們來講解一下怎麼控制電機的角度,如果用單級PID控制角度會有什麼問題,為什麼要用串級PID。
當然串級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)得出的角度值,也不會如我們想控制的一樣。
那麼解決方案就很簡單了,就是去控制這個速度值,使其輸出的速度值是我們控制器想要得到的值即可了。我們容易得出下面框圖。
這裡我們加了一級速度級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上了/電子設計驅動