每個接觸PID的人,都知道PID的公式吧:
然后根據這個公式,就可以編出計算output的arduino程序如下:
注:程序源碼可以在原文上復制。
可是在上面的程序中,當PID不規(guī)則的調用時就會出現兩個問題:
1.有時候定時調用,有時候又停止調用,將得不到PID的持續(xù)穩(wěn)定特性。
2.需要額外對積分和微分進行數學計算,因為他們都是和時間息息相關的。
解決方法:保證PID在一個固定的的時間間隔內被調用,我通過每個周期內事先設置好的采樣時間調用compute函數,PID再決定是計算還是立即返回數值。一旦我們知道PID在固定時間間隔內被調用,積分和微分的計算就能被簡化。
于是我們的程序就變成了:
灰色部分是新加的語句。
在這個新的程序中,作者在29行把時間轉化為秒。這樣可以簡化運算。同時新加入后面兩個函數把PID參數轉化為以秒為基準的參數。
當上面的PID程序在運行中,可能會出現微分過沖的問題。
看上面的圖,因為error=Setpoint-Input,只要Setpoint發(fā)生改變,就會導致error的值發(fā)生一個突變。在微分運算里這種微分值的突變是無窮大的。
解決方法:
于是我們的程序就變成了:
可以看到我們只是用-dInput代替掉了+dError。
當我們的PID在運行的時候,這個時候改變PID的參數,會導致輸出發(fā)生變化,如:
我們只是把ki的值減少一半,輸出就減少了一半。為什么會這樣呢?可以從下面的積分公式里找到原因:
這就解釋了為什么KI沒改變之前系統工作的一直很穩(wěn)定。突然,你乘了一個新的KI值與之前的偏差累計總和。這樣帶來的變化不是我們所希望的,我們只想改變后能朝著我們希望的方向發(fā)展。
解決方法:解決的方法有很多,我在最新的Arduino PID庫里使用的方法是重新調整errSum(偏差總和).KI變?yōu)樵瓉淼膬杀稌r,把errSum變?yōu)樵瓉淼囊话耄m然這個方法有點笨拙,下面有更加明智的方法。這個方法要求有基礎的代數基礎或者計算技巧。
把KI乘到里面,雖然看起來沒什么變化,但是我們將會看到這個小變化帶來了很大的不同?,F在我們將error和Ki相乘。并且把乘積和保存起來,當Ki變化時,這時不會有很大的變化了,因為之前的KI的乘積和值已經存儲起來了。
于是我們的程序就變成了:
這樣,我們再改變參數,輸出也不會發(fā)生很大的變化。
本帖最后由 對折之內 于 2015-4-13 19:17 編輯
當我們的PID在運行的時候,有時候會發(fā)生擺尾的情況。如圖:
這種情況發(fā)生在PID認為它能做一些它實際上做不到的事情時。比如最大只能輸出255,可是PID輸出了300。原因在于PID并不知道最大的輸出只能為255。所以我們在程序里給PID的輸出值加一些限制。告訴PID可以輸出的最大值為多少。我們的程序就變成了:
當有的時候,你不需要使用PID計算的輸出值,想用自己設定的值。(比如想讓輸出為0),則你的程序可能會這樣寫:
void loop()
{
Compute();
Output=0;
}
這樣的話,不管PID想輸出什么,程序始終輸出0。這樣就會使PID變得很迷惑:“我嘗試著改變output,卻什么都沒有發(fā)生,怎么辦?我把改變的力度增大吧。”這樣,當你停止使用自己設定的值作為輸出,重新使用PID作為輸出時。PID輸出很大的值,這樣系統就亂套了。
因此我們要在程序里,加一個開關,在我們使用自己設定的值時,把PID關掉,不讓它進行計算。
我們的程序就變成了:
在上面的程序中,我們給PID加了一個開關,但是當我們把PID的開關打開,重新回到PID的時候,會發(fā)生下面的情況:
沒錯,PID的輸出值會跳回成它發(fā)出的上一個輸出值。并且從那個值開始來調整自己。
因此當開關重新打開時,我們需要一個初始化函數,來重新初始化一下數據,先看程序:
可以知道,當換模式的時候,會執(zhí)行IniTIalize()函數,把lasTInput的值更新成現在的采樣值,把ITerm的值更新成output的值,這樣就避免了第一次執(zhí)行PID時的跳變了。