PID控制算法

文章目錄

  • 引言
  • 一、基本原理
    • 1.1.簡介
    • 1.2.開環與閉環
    • 1.3.PID 的公式
      • 1.3.1.比例項(Proportional)
      • 1.3.2.積分項(Integral)
      • 1.3.3.微分項(Differential)
    • 1.4.連續形式與離散形式的 PID 公式
      • 1.4.1.連續形式
      • 1.4.2.離散形式
    • 1.5.位置式 PID 與增量式 PID
      • 1.5.1.位置式
      • 1.5.2.增量式
    • 1.6.程序實現
  • 二、實驗
    • 2.1.位置式PID定速控制
    • 2.2.增量式 PID 定速控制
    • 2.3.位置式 PID 定位置控制
    • 2.4.增量式 PID 定位置控制
  • 三、算法改進
    • 3.1.積分限幅
    • 3.2.積分分離
    • 3.3.變速積分
    • 3.4.微分先行
    • 3.5.不完全微分
    • 3.6.輸出偏移
    • 3.7.輸入死區

引言

在工業領域上,精準性和穩定性是非常重要的,人工的實時調節不能夠完全勝任高精度和高穩定性的工作,于是用數學手段替代人工經驗,自動校正系統誤差(設定值與實際值的偏差)。

一、基本原理

1.1.簡介

PID(比例-積分-微分)控制器是一種廣泛應用于工業控制系統的反饋控制算法,PID 是一種閉環控制算法,它動態改變施加到被控對象的輸出值(Out),使得被控對象某一物理量的實際值(Actual),能夠快速、準確、穩定地跟蹤到指定的目標值(Target)。PID 是一種基于誤差(Error)調控的算法,其中規定:誤差=目標值-實際值,PID 的任務是使誤差始終為 0,對被控對象模型要求低,無需建模,即使被控對象內部運作規律不明確,PID 也能進行調控。PID 就像駕駛汽車時:

  • P:眼睛看到偏離車道,立即打方向盤修正(現在)
  • I:如果持續偏離,逐漸加大修正力度(過去)
  • D:預判車輛趨勢,提前減速或減速防止過度轉向(未來)

1.2.開環與閉環

  • 開環(Open Loop):控制器單向輸出值給被控對象,不獲取被控對象的反饋,控制器對被控對象的執行狀態不清楚

在這里插入圖片描述

  • 閉環(Closed Loop):控制器輸出值給被控對象,同時獲取被控對象的反饋,控制器知道被控對象的執行狀態,可以根據反饋修改輸出值以優化控制

在這里插入圖片描述

1.3.PID 的公式

  • 定義誤差:

在這里插入圖片描述

  • PID輸出值:

在這里插入圖片描述

下圖是 PID 閉環控制圖,首先,用戶輸入目標值給被控對象并通過被控對象反饋出來的實際值算出誤差,基于本次誤差和歷史誤差算出輸出值給被控對象,實現實際值跟蹤目標值:

在這里插入圖片描述

1.3.1.比例項(Proportional)

比例項的輸出值僅取決于當前時刻的誤差,與歷史時刻無關。當前存在誤差時,比例項輸出一個與誤差呈正比的值,當前不存在誤差時,比例項輸出 0,下面式子是只含有比例項的 PID 輸出值:

在這里插入圖片描述

Kp 越大,比例項權重越大,系統響應越快,但超調也會隨之增加,如下圖紅框所示:

在這里插入圖片描述

如果只是純比例項控制,系統一般會存在穩態誤差,Kp 越大,穩態誤差越小,實際值與目標值存在恒定偏差,即穩態誤差。產生穩態誤差的原因:純比例項控制時,若誤差為 0,則比例項結果也為 0。被控對象輸入 0 時,一般會自發地向一個方向偏移,產生誤差。產生誤差后,誤差非 0,比例項負反饋調控輸出,當調控輸出力度和自發偏移力度相同時,系統達到穩態.

1.3.2.積分項(Integral)

積分項的輸出值取決于 0 ~ t 所有時刻誤差的積分,與歷史時刻有關。積分項將歷史所有時刻的誤差累積,乘上積分項系數 Ki 后作為積分項輸出值,用于彌補純比例項產生的穩態誤差,若系統持續產生誤差,則積分項會不斷累積誤差,直到控制器產生動作,讓穩態誤差消失。下面是含有比例項和積分項的 PID 輸出值:

在這里插入圖片描述

Ki 越大,積分項權重越大,穩態誤差消失越快,但系統滯后性也會隨之增加,如下圖的紅框所示:

在這里插入圖片描述

滯后性是實際值靠近目標值的增長速度慢,這樣的現象會導致一些需要高平衡性的項目,帶來較大的影響。

1.3.3.微分項(Differential)

微分項的輸出值取決于當前時刻誤差變化的斜率,與當前時刻附近誤差變化的趨勢有關。當誤差急劇變化時,微分項會負反饋輸出相反的作用力,阻礙誤差急劇變化,斜率一定程度上反映了誤差未來的變化趨勢,這使得微分項具有 “預測未來,提前調控”的特性。微分項給系統增加阻尼,可以有效防止系統超調,尤其是慣性比較大的系統,下面式子是含有比例項、積分項和微分項的 PID 輸出值:

在這里插入圖片描述

Kd 越大,微分項權重越大,系統阻尼越大,但系統卡頓現象也會隨之增加,如果微分項過大,就會導致比例項和積分項讓被控對象啟動,微分項卻阻止它們。

在這里插入圖片描述

上圖的紅線是目標值,藍線是實際值,綠線是輸出值,當實際值斜率很大的時候,說明變化速度很快,輸出值就要反向輸出,讓實際值的速度減緩。

1.4.連續形式與離散形式的 PID 公式

1.4.1.連續形式

在這里插入圖片描述

為了更好的在程序上實現 PID 式子,將積分符號和求導,變成簡單的加、減、乘、除形式,因此該式子變為離散形式。

1.4.2.離散形式

比例項不變,積分項原本的意思就是從 0 時刻開始到 t 時刻的誤差之和,離散之后就是相當于求曲線下面的面積,如下圖所示:每個調控周期為 T,一共有 8 個 T,每個時刻的誤差乘以調控周期 T,最后相加。j 是相當于 for 循環里面的變量 i,用于遍歷。
在這里插入圖片描述

微分項就是在某點求導,離散之后,就是求當前時刻和上一個時刻連線的斜率,如下圖的紅線,求 5T 時刻的斜率,離散之后還需知道上一次的 4T 時的誤差,兩點相減之后再除以調控周期 T 即可求得:

在這里插入圖片描述

經過以上變化,得出離散的 PID 式子:

在這里插入圖片描述

上面式子需要考慮調控周期 T,如果將調控周期 T 直接并入 Ki 和 Kd,即可獲得下面式子:

在這里插入圖片描述

該式子只需考慮當前誤差(error(k)),歷史誤差(error(j)),上一個誤差(error(k-1))三個誤差,其中 Kp、Ki 和 Kd 由用戶自己設置。

1.5.位置式 PID 與增量式 PID

1.5.1.位置式

位置式 PID 由連續形式 PID 直接離散得到,每次計算得到的是全量的輸出值,可以直接給被控對象,例如,閥門控制項目,位置式 PID 每次都會反饋完整的閥門狀態。

1.5.2.增量式

增量式 PID 由位置式 PID 推導得到,每次計算得到的是輸出值的增量,如果直接給被控對象,則需要被控對象內部有積分功能;增量式 PID 也可在控制器內進行積分,然后輸出積分后的結果,此時增量式 PID 與位置式 PID 整體功能沒有區別,下面是增量式 PID 的推導:

在這里插入圖片描述

當 k = k - 1,再讓 out(k) - out(k-1),得到增量式,增量式需要用到當前時刻的誤差、上一次的誤差和上上次的誤差:

在這里插入圖片描述

1.6.程序實現

先確定一個調控周期 T,本次實驗選擇軟件定時器設置中斷,每中斷 一次就是一次調控周期,這樣設計,主函數能夠運行更多的代碼,提高了 CPU 利用率,下面是大致代碼實現:

int main(void)
{Timer_Init();while(1){}
}void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){//每隔時間T,程序執行到該中斷服務函數//在該中斷服務函數里執行PID調控TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}
  • 位置式
float Target, Actual, Out;			//目標值、實際值、輸出值
float Kp =, Ki =, Kd =;	//比例項、積分項、微分項
float Error0, Error1, ErrorInt;		//本次誤差、上次誤差、誤差積分int main(void)
{Timer_Init();while(1){//用戶在此處根據需求寫入PID控制器的目標值Target =;}
}void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){//每隔時間T,程序執行到該中斷服務函數//在該中斷服務函數里執行PID調控//獲取實際值Actual = 讀取傳感器();//獲取本次誤差和上次誤差Error1 = Error0;Error0 = Target - Actual;//誤差積分(累加)ErrorInt += Error0;//PID計算	Out = Kp * Error0 + Ki * ErrorInt + Kd * (Error0 - Error1);//輸出限幅if(Out > 上限){Out = 上限;}if(Out < 下限){Out = 下限;}//執行控制輸出至被控對象(Out);TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}
  • 增量式:增量式不需要積分累加,但是需要知道上上次的誤差,該程序是在控制器內進行積分,也就是輸出的 Out 值已經累加好的
float Target, Actual, Out;			//目標值、實際值、輸出值
float Kp =, Ki =, Kd =;	//比例項、積分項、微分項
float Error0, Error1, Error2;		//本次誤差、上次誤差、上上次誤差int main(void)
{Timer_Init();while(1){//用戶在此處根據需求寫入PID控制器的目標值Target =;}
}void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){//每隔時間T,程序執行到該中斷服務函數//在該中斷服務函數里執行PID調控//獲取實際值Actual = 讀取傳感器();//獲取本次誤差、上次誤差和上上次的誤差Error2 = Error1;Error1 = Error0;Error0 = Target - Actual;//誤差積分(累加)ErrorInt += Error0;//PID計算	Out = Kp * (Error0 - Error1) + Ki * Error0 + Kd * (Error0 - 2 * Error1 + Error2);//輸出限幅if(Out > 上限){Out = 上限;}if(Out < 下限){Out = 下限;}//執行控制輸出至被控對象(Out);TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

二、實驗

2.1.位置式PID定速控制

實驗目的:使用電位器旋鈕修改 Kp、Ki、Kd 和 Target,修改 Kp、Ki、Kd 使得實際值盡量貼合目標值。下面是代碼實現:

int main(void)
{/*模塊初始化*/OLED_Init();		//OLED初始化Key_Init();			//非阻塞式按鍵初始化Motor_Init();		//電機初始化Encoder_Init();		//編碼器初始化RP_Init();			//電位器旋鈕初始化Serial_Init();		//串口初始化,波特率9600Timer_Init();		//定時器初始化,定時中斷時間1ms/*OLED打印一個標題*/OLED_Printf(0, 0, OLED_8X16, "Speed Control");OLED_Update();while (1){/*電位器旋鈕修改Kp、Ki、Kd和目標值*//*RP_GetValue函數返回電位器旋鈕的AD值,范圍:0~4095*//* 除4095.0可以把AD值歸一化,再乘上一個系數,可以調整到一個合適的范圍*/Kp = RP_GetValue(1) / 4095.0 * 2;				//修改Kp,調整范圍:0~2Ki = RP_GetValue(2) / 4095.0 * 2;				//修改Ki,調整范圍:0~2Kd = RP_GetValue(3) / 4095.0 * 2;				//修改Kd,調整范圍:0~2Target = RP_GetValue(4) / 4095.0 * 300 - 150;	//修改目標值,調整范圍:-150~150/*OLED顯示*/OLED_Printf(0, 16, OLED_8X16, "Kp:%4.2f", Kp);			//顯示KpOLED_Printf(0, 32, OLED_8X16, "Ki:%4.2f", Ki);			//顯示KiOLED_Printf(0, 48, OLED_8X16, "Kd:%4.2f", Kd);			//顯示KdOLED_Printf(64, 16, OLED_8X16, "Tar:%+04.0f", Target);	//顯示目標值OLED_Printf(64, 32, OLED_8X16, "Act:%+04.0f", Actual);	//顯示實際值OLED_Printf(64, 48, OLED_8X16, "Out:%+04.0f", Out);		//顯示輸出值OLED_Update();	//OLED更新,調用顯示函數后必須調用此函數更新,否則顯示的內容不會更新到OLED上Serial_Printf("%f,%f,%f\r\n", Target, Actual, Out);		//串口打印目標值、實際值和輸出值//配合SerialPlot繪圖軟件,可以顯示數據的波形}
}void TIM1_UP_IRQHandler(void)
{/*定義靜態變量(默認初值為0,函數退出后保留值和存儲空間)*/static uint16_t Count;		//用于計次分頻if (TIM_GetITStatus(TIM1, TIM_IT_Update) == SET){/*每隔1ms,程序執行到這里一次*/Key_Tick();				//調用按鍵的Tick函數/*計次分頻*/Count ++;				//計次自增if (Count >= 40)		//如果計次40次,則if成立,即if每隔40ms進一次{Count = 0;			//計次清零,便于下次計次/*獲取實際速度值*//*Encoder_Get函數,可以獲取兩次讀取編碼器的計次值增量*//*此值正比于速度,所以可以表示速度,但它的單位并不是速度的標準單位*//*此處每隔40ms獲取一次計次值增量,電機旋轉一周的計次值增量約為408*//*因此如果想轉換為標準單位,比如轉/秒*//*則可將此句代碼改成Actual = Encoder_Get() / 408.0 / 0.04;*/Actual = Encoder_Get();/*獲取本次誤差和上次誤差*/Error1 = Error0;			//獲取上次誤差Error0 = Target - Actual;	//獲取本次誤差,目標值減實際值,即為誤差值/*誤差積分(累加)*//*如果Ki不為0,才進行誤差積分,這樣做的目的是便于調試*//*因為在調試時,我們可能先把Ki設置為0,這時積分項無作用,誤差消除不了,誤差積分會積累到很大的值*//*后續一旦Ki不為0,那么因為誤差積分已經積累到很大的值了,這就導致積分項瘋狂輸出,不利于調試*/if (Ki != 0)				//如果Ki不為0{ErrorInt += Error0;		//進行誤差積分}else						//否則{ErrorInt = 0;			//誤差積分直接歸0}/*PID計算*//*使用位置式PID公式,計算得到輸出值*/Out = Kp * Error0 + Ki * ErrorInt + Kd * (Error0 - Error1);/*輸出限幅*/if (Out > 100) {Out = 100;}		//限制輸出值最大為100if (Out < -100) {Out = -100;}	//限制輸出值最小為100/*執行控制*//*輸出值給到電機PWM*//*因為此函數的輸入范圍是-100~100,所以上面輸出限幅,需要給Out值限定在-100~100*/Motor_SetPWM(Out);}TIM_ClearITPendingBit(TIM1, TIM_IT_Update);}
}

2.2.增量式 PID 定速控制

本次實驗和位置式定速控制的實驗目的一樣,下面是中斷服務函數代碼:

float Target, Actual, Out;			//目標值,實際值,輸出值
float Kp, Ki, Kd;					//比例項,積分項,微分項的權重
float Error0, Error1, Error2;		//本次誤差,上次誤差,上上次誤差void TIM1_UP_IRQHandler(void)
{/*定義靜態變量(默認初值為0,函數退出后保留值和存儲空間)*/static uint16_t Count;		//用于計次分頻if (TIM_GetITStatus(TIM1, TIM_IT_Update) == SET){/*每隔1ms,程序執行到這里一次*/Key_Tick();				//調用按鍵的Tick函數/*計次分頻*/Count ++;				//計次自增if (Count >= 40)		//如果計次40次,則if成立,即if每隔40ms進一次{Count = 0;			//計次清零,便于下次計次/*獲取實際速度值*//*Encoder_Get函數,可以獲取兩次讀取編碼器的計次值增量*//*此值正比于速度,所以可以表示速度,但它的單位并不是速度的標準單位*//*此處每隔40ms獲取一次計次值增量,電機旋轉一周的計次值增量約為408*//*因此如果想轉換為標準單位,比如轉/秒*//*則可將此句代碼改成Actual = Encoder_Get() / 408.0 / 0.04;*/Actual = Encoder_Get();/*獲取本次誤差、上次誤差和上上次誤差*/Error2 = Error1;			//獲取上上次誤差Error1 = Error0;			//獲取上次誤差Error0 = Target - Actual;	//獲取本次誤差,目標值減實際值,即為誤差值/*PID計算*//*使用增量式PID公式,計算得到輸出值*/Out += Kp * (Error0 - Error1) + Ki * Error0+ Kd * (Error0 - 2 * Error1 + Error2);/*輸出限幅*/if (Out > 100) {Out = 100;}		//限制輸出值最大為100if (Out < -100) {Out = -100;}	//限制輸出值最小為100/*執行控制*//*輸出值給到電機PWM*//*因為此函數的輸入范圍是-100~100,所以上面輸出限幅,需要給Out值限定在-100~100*/Motor_SetPWM(Out);}TIM_ClearITPendingBit(TIM1, TIM_IT_Update);}
}

增量式的現象和位置式的現象看起來是一樣的,但是在增量式的實驗中,當 PID 三個值確定之后,設定一個目標值,再讓實際值跟上目標值,最后把 PID 三個值調整為 0,這個時候還有輸出值,是因為增量式在程序里式疊加的,如果 PID 三項都為 0,輸出值還保留著上一次的輸出值,只是不再有變化而已,如下圖所示:

在這里插入圖片描述

2.3.位置式 PID 定位置控制

實驗目的:使用電位器旋鈕修改 Kp、Ki、Kd 和 Target,使得實際值轉動到目標值,本次實驗代碼與位置式定速控制實驗代碼一樣,只在中斷服務函數里面修改實際值 += 獲取編碼器速度,因為 += 就是積分表達,速度的積分就是位置。

Actual += Encoder_Get();

有下面圖可以看出,純比例項調節也可以做到盡可能貼合目標值了,綠色的輸出線在實際值與目標值變化的時候才有波動,當實際值和目標值重合時輸出值為 0。
在這里插入圖片描述

2.4.增量式 PID 定位置控制

實驗目的:使用電位器旋鈕修改 Kp、Ki、Kd 和 Target,使得實際值轉動到目標值,本次實驗代碼與增量式定速控制實驗代碼一樣,只在中斷服務函數里面修改實際值 += 獲取編碼器速度。

Actual += Encoder_Get();

如果將 Ki 調到 0 并快速扭動改變目標值,就會出現實際值與目標值嚴重偏移的現象:

在這里插入圖片描述

增量式定位置控制非常依賴這個積分項,沒有積分項的增量式 PID,容易出現實際值和目標值偏移的問題,或者,如果上一次的輸出值式錯誤的,同時后續的 Error 變化很小,則純比例項調節很難糾正這個錯誤。此外,增量式的純比例項調節,需要借助上一次的輸出值,這使得即使是純比例項調節,也會有一點積分項的特性,因此,增量式 PID 最好不要給 Ki 為 0。

三、算法改進

以下修改均在位置式 PID 定位置控制實驗里。

3.1.積分限幅

要解決的問題:如果執行器因為卡住、斷電、損壞等原因不能消除誤差,則誤差積分會無限制加大,進而達到深度飽和狀態,此時 PID 控制器會持續輸出最大的調控力,即使后續執行器恢復正常,PID 控制器在短時間內也會維持最大的調控力,直到誤差積分從深度飽和狀態退出。例如,用手指抵住正在旋轉的轉盤,過一段時間再放手,得到的波形如下圖所示:
在這里插入圖片描述
抵住的時間越久,積分項累積的輸出值就越大,恢復到穩定輸出值的時間就越久。積分限幅實現思路:對誤差積分或積分項輸出進行判斷,如果幅值超過指定閾值,則進行限制,具體操作如下:

	/*誤差積分(累加)*/ErrorInt += Error0;/*積分限幅*/if (ErrorInt > 500) {ErrorInt = 500;}		//限制誤差積分最大為500if (ErrorInt < -500) {ErrorInt = -500;}		//限制誤差積分最小為-500Out = Kp * Error0 + Ki * ErrorInt + Kd * (Error0 - Error1);

3.2.積分分離

積分項作用一般位于調控后期,用來消除持續的誤差,調控前期一般誤差較大且不需要積分項作用,如果此時仍然進行積分,則調控進行到后期時,積分項可能已經累積了過大的調控力,這會導致超調。如下圖所示,定位置控制實驗里,需要實際值盡可能貼近目標值,當目標值改變過快時,就會出現下面情況,積分項一開始正方向累計輸出值過多,因此需要負方向輸出抵消,拿實例來說明,轉盤轉動過快,出現轉過目標位置的情況,因此需要往回轉動回到目標位置。

在這里插入圖片描述
在這里插入圖片描述
積分分離實現思路:對誤差大小進行判斷,如果誤差絕對值小于指定閾值,則加入積分項作用,反之,則直接將誤差積分清零或不加入積分項作用,下面實現積分分離,在獲得本次誤差和上次誤差下面加上一下代碼:

	/*誤差積分+積分分離*/if (fabs(Error0) < 50)		//如果當前誤差值小于指定的閾值{ErrorInt += Error0;		//才進行正常的誤差積分}else						//否則,即當前誤差值過大{ErrorInt = 0;			//此時不進行積分,同時把誤差積分直接清零}Out = Kp * Error0 + Ki * ErrorInt + Kd * (Error0 - Error1);

閾值 50,是通過多次實驗得出,如果超過閾值,積分項完全消失,就不會出現超過目標值的情況;如果閾值設定的太大,實驗現象的效果不是很好。

3.3.變速積分

如果積分分離閾值沒有設定好,被控對象正好在閾值之外停下來,則此時控制器完全沒有積分作用,誤差不能消除,如下圖所示:

在這里插入圖片描述

這時可以通過調大閾值的方法來解決,也可以通過變速積分來解決。變速積分簡單來說只是在積分和不積分之間加了緩沖區,誤差值越大,積分越弱,誤差值越小,積分越強,如下圖所示:

在這里插入圖片描述

代碼實現如下所示:

/*定義一個系數C,表示積分的速度,C的值與誤差絕對值大小呈反比,誤差絕對值越大,積分速度越慢*/float C = 1 / (0.2 * fabs(Error0) + 1);		//根據公式計算得到系數C/*誤差積分*/ErrorInt += C * Error0;		//積分的速度由C確定,C的取值范圍是0~1Out = Kp * Error0 + Ki * ErrorInt + Kd * (Error0 - Error1);

3.4.微分先行

普通 PID 的微分項對誤差進行微分,當目標值大幅度跳變時,誤差也會瞬間大幅度跳變,這會導致微分項突然輸出一個很大的調控力,如果系統的目標值頻繁大幅度切換,則此時的微分項不利于系統穩定。微分項本來就是一個阻尼的作用,當 Kp、Ki 為 0 時,Kd 無論調成多少,按理來說都不會產生輸出值,但是現實卻是 Kp、Ki 為 0 時,也會有輸出值,如下圖所示:

在這里插入圖片描述
在這里插入圖片描述

微分先行實現思路:將對誤差的微分替換為對實際值的微分。下面是將微分項修改之后的式子:

在這里插入圖片描述

為什么需要加一個負號,因為是對實際值求微分,實際值在上升過程中需要一個反方向的輸出才能平緩的到達目標值;而對誤差求微分,誤差逐漸變小的時候,斜率本來就是負的,因此對實際值求微分,需要添加一個負號。

在這里插入圖片描述

下面是實現微分先行的代碼:

	float DifOut, Actual1;				//定義一個微分項輸出(方便查看波形),上次實際值Actual1 = Actual;			//獲取上次實際值Actual += Encoder_Get();	//獲取本次實際值/*獲取本次誤差和上次誤差*/Error1 = Error0;			//獲取上次誤差Error0 = Target - Actual;	//獲取本次誤差,目標值減實際值,即為誤差值/*PID計算,先得到微分項*/DifOut = - Kd * (Actual - Actual1);		//這一句是微分先行的微分項計算公式//計算結果要取負,因為實際值的變化趨勢和誤差變化趨勢相反Out = Kp * Error0 + Ki * ErrorInt + DifOut;

3.5.不完全微分

傳感器獲取的實際值經常會受到噪聲干擾,而 PID 控制器中的微分項對噪聲最為敏感,這些噪聲干擾可能會導致微分項輸出抖動,進而影響系統性能,如下圖所示,下圖是噪聲對 PID 三項的影響:

  • 對 P 項的影響:P 項只取決于當前誤差,影響較小
  • 對 I 項的影響:原本是綠色波形下面的面積,受到噪聲的影響之后變成紅色波形下面的面積了,但是 I 項面積有正負抵消的作用,影響也不是很大
  • 對 D 項的影響:原本綠色的某一時刻的斜率是正的,受到噪聲影響之后變成負的,因此受到噪聲影響最大的是 D 項

在這里插入圖片描述

不完全微分實現思路:可以給 PID 三項都加上濾波器,這樣會導致系統響應較慢,產生一定的滯后性;只給微分項加入一階慣性單元(低通濾波器),這是對輸出值反應速度較高的做法。如下圖所示:

在這里插入圖片描述

下面式子是不完全微分 PID 的微分項輸出,上面的是算出當前的 D 項輸出:

在這里插入圖片描述
在這里插入圖片描述
其中加號左邊是本次的 D 項輸入,右邊是上一次的 D 項輸出,由 a 來解決那一邊的比重較高,該式子整體的意思是本次算的 D 項輸入和上次輸出的 dout 取平均值。

下面是代碼實現:

	/*模擬給實際值添加一些噪聲干擾*/Actual += rand() % 41 - 20;		//使用隨機數生成函數rand生成隨機數,并將結果限定在-20~20的范圍內/*定義一個濾波強度系數a,a的取值范圍是0~1,值越大表示濾波作用越強*/float a = 0.9;DifOut = (1 - a) * Kd * (Error0 - Error1) + a * DifOut;		//這一句是不完全微分的微分項計算公式Out = Kp * Error0 + Ki * ErrorInt + DifOut;

不完全微分的微分項計算公式中,等號左邊是當前的微分項輸出,右邊的是上一次的微分項輸出。

3.6.輸出偏移

對于一些啟動需要一定力度的執行器,若輸出值較小,執行器可能完全無動作,這可能會引起調控誤差,同時會降低系統響應速度。例如,啟動一個電機,當 PWM 為 2% 時,無法使得電機轉動,因此引起了調控誤差,如下圖所示:

在這里插入圖片描述

圖中,目標值和實際值相差為 10,差值還是比較大的,可以用 Ki 來減小,但是當轉盤停下來了,輸出值還是不為 0,這是因為 P 項還有一點輸出值來驅動轉盤,但是這一點輸出值不足以使得轉盤轉動。輸出偏移實現思路:若輸出值為0,則正常輸出0,不進行調控;若輸出值非0,則給輸出值加一個固定偏移,跳過執行器無動作的階段。下面是代碼實現:

	/*輸出偏移*/if (Out > 0)		//如果輸出為正{Out += 6;		//則直接給輸出值加上一個固定偏移}else if (Out < 0)	//如果輸出為負{Out -= 6;		//則直接給輸出值減去一個固定偏移}else				//輸出為0{Out = 0;		//輸出0}

在代碼中,固定偏移量是在現實實驗中嘗試出來的,多次嘗試,看出輸出值為多少時,轉盤才會啟動。

3.7.輸入死區

在某些系統中,輸入的目標值或實際值有微小的噪聲波動,或者系統有一定的滯后,這些情況可能會導致執行器在誤差很小時頻繁調控,不能最終穩定下來,特別是加上輸出偏移的式子,輸出值很難達到 0。解決方法是設置一個輸入死區:若誤差絕對值小于一個限度,則固定輸出 0,不進行調控,也就是容忍一定的誤差存在。當誤差小于設定的值,輸出值為 0;若大于設定的值,則有輸出值。下面是代碼實現:

	if (fabs(Error0) < 5)		//如果誤差絕對值比較小{Out = 0;				//則直接輸出0,不進行調控}else						//否則,即誤差絕對值比較大,下面進行正常的PID調控{/*誤差積分(累加)*//*如果Ki不為0,才進行誤差積分,這樣做的目的是便于調試*//*因為在調試時,我們可能先把Ki設置為0,這時積分項無作用,誤差消除不了,誤差積分會積累到很大的值*//*后續一旦Ki不為0,那么因為誤差積分已經積累到很大的值了,這就導致積分項瘋狂輸出,不利于調試*/if (Ki != 0)				//如果Ki不為0{ErrorInt += Error0;		//進行誤差積分}else						//否則{ErrorInt = 0;			//誤差積分直接歸0}/*PID計算*//*使用位置式PID公式,計算得到輸出值*/Out = Kp * Error0 + Ki * ErrorInt + Kd * (Error0 - Error1);/*輸出偏移*/if (Out > 0)		//如果輸出為正{Out += 6;		//則直接給輸出值加上一個固定偏移}else if (Out < 0)	//如果輸出為負{Out -= 6;		//則直接給輸出值減去一個固定偏移}else				//輸出為0{Out = 0;		//輸出0}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/918924.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/918924.shtml
英文地址,請注明出處:http://en.pswp.cn/news/918924.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MyBatis 動態數據源切換在 Spring Boot 環境下的實現方案

第一章 需求背景與技術選型1.1 多數據源場景概述在大型企業級應用中&#xff0c;單一數據庫往往無法滿足高并發和多業務線的需求&#xff0c;因此需要引入 多數據源 的架構設計。常見的多數據源場景包括&#xff1a;讀寫分離、多租戶、分庫分表以及數據源負載均衡等。讀寫分離&…

PCA降維理論詳解

文章目錄一、什么是PCA&#xff1f;二、為什么需要降維&#xff1f;三、PCA的數學原理與詳細推導視角一&#xff1a;最大化投影方差&#xff08;Maximizing Variance&#xff09;視角二&#xff1a;最小化重構誤差&#xff08;Minimizing Reconstruction Error&#xff09;四、…

Android RxJava變換操作符詳解

RxJava作為響應式編程在Android開發中的利器&#xff0c;其強大的變換操作符能夠幫助我們優雅地處理數據流。本文將深入講解RxJava中最常用的變換操作符及其實際應用場景。一、RxJava變換操作符概述變換操作符(Transformation Operators)用于對Observable發射的數據序列進行變換…

開源數據發現平臺:Amundsen 快速上手指南

Amundsen 是一個數據發現和元數據引擎&#xff0c;旨在提高數據分析師、數據科學家和工程師與數據交互時的生產力。目前&#xff0c;它通過索引數據資源&#xff08;表格、儀表板、數據流等&#xff09;并基于使用模式&#xff08;例如&#xff0c;查詢頻率高的表格會優先于查詢…

【密碼學實戰】國密SM2算法介紹及加解密/簽名代碼實現示例

引言 在信息安全領域&#xff0c;密碼算法是數據保護的核心基石。2010 年&#xff0c;中國國家密碼管理局發布了 SM2 橢圓曲線公鑰密碼算法&#xff0c;作為國產密碼標準的核心成員&#xff0c;它憑借高效安全的特性&#xff0c;逐步替代 RSA 等國際算法&#xff0c;廣泛應用于…

QT開發中如何加載第三方dll文件

文章目錄&#x1f527; 一、隱式加載&#xff08;靜態鏈接&#xff09;操作步驟&#xff1a;?? 二、顯式加載&#xff08;動態鏈接&#xff0c;推薦使用QLibrary&#xff09;操作步驟&#xff1a;&#x1f4bb; 三、直接調用Windows API&#xff08;僅Windows&#xff09;??…

后端學習資料 持續更新中

數據庫&#xff1a; 該網址包含&#xff1a;圖解MySql&#xff0c; 看明白誰也問不倒你~ 圖解計算機網絡、操作系統、計算機組成、MySQL、Redis&#xff0c;讓天下沒有難懂的八股文&#xff01;https://xiaolincoding.com/

《嵌入式Linux應用編程(六):并發編程基礎:多進程exec函數族及多線程基礎》

一、exec函數族在一個進程里面執行另一個文件本質&#xff1a;將文本區的指令代碼替換成exec要執行的指令#include <unistd.h>參數&#xff1a;path:要執行的可執行文件的路徑和名稱arg:執行該可執行文件時需要傳遞的參數NULL&#xff1a;參數傳遞結束標志 返回值&#x…

【121頁PPT】智慧方案智慧綜合體智能化設計方案(附下載方式)

篇幅所限&#xff0c;本文只提供部分資料內容&#xff0c;完整資料請看下面鏈接 https://download.csdn.net/download/2501_92808859/91654007 資料解讀&#xff1a;【121頁PPT】智慧方案智慧綜合體智能化設計方案 詳細資料請看本解讀文章的最后內容 一、項目概述與智能化總…

Linux網絡基礎(一)

目錄 計算機網絡背景 網絡發展 初識 "協議" 網絡協議初識 協議分層 軟件分層的好處 打電話例子 OSI七層模型 TCP/IP五層(或四層)模型 參考資料 再識協議 為什么要有 TCP/IP 協議&#xff1f; 什么是 TCP/IP 協議&#xff1f; TCP/IP 協議與操作系統的關系(宏觀上&…

MySQL多表查詢案例

多表查詢本文介紹了多表查詢中的表關系概念和操作方法。主要內容包括&#xff1a;1.三種表關系類型&#xff08;一對多、多對多、一對一&#xff09;及其實現方式&#xff1b;2.多表查詢的四種連接方式&#xff08;內連接、左外連接、右外連接、自連接&#xff09;及語法&#…

Dify 從入門到精通(第 36/100 篇):Dify 的插件生態擴展

Dify 從入門到精通&#xff08;第 36/100 篇&#xff09;&#xff1a;Dify 的插件生態擴展 Dify 入門到精通系列文章目錄 第一篇《Dify 究竟是什么&#xff1f;真能開啟低代碼 AI 應用開發的未來&#xff1f;》介紹了 Dify 的定位與優勢第二篇《Dify 的核心組件&#xff1a;從…

【已解決】在Spring Boot工程中,若未識別到resources/db文件夾下的SQL文件

在Spring Boot工程中&#xff0c;若未識別到resources/db文件夾下的SQL文件&#xff0c;通常與資源路徑配置、構建工具設置或代碼加載方式有關。以下是逐步排查和解決方案&#xff1a;??1. 確認SQL文件存放路徑??Spring Boot默認從類路徑&#xff08;classpath:&#xff09…

【Java】網絡編程(4)

1. 再談 UDP 報文長度&#xff1a;也是 2 個字節&#xff0c; 0 - 65535&#xff0c;也就是 64 kb。這表示一個 UDP 數據包一次最多只能傳輸 64 kb 的數據校驗和&#xff1a;驗證數據是否在傳輸過程中發生修改。數據在傳輸過程中可能受到信號干擾&#xff0c;發生 “比特翻轉”…

QT(事件)

一、事件前言事件是QT的三大機制之一&#xff0c;一定程度上信號和槽也屬于事件的一種 QT中的事件指哪些&#xff1a;窗口關閉&#xff0c;窗口顯示&#xff0c;敲擊鍵盤&#xff0c;點擊鼠標左鍵、鼠標右鍵、鼠標滾輪&#xff0c;文件拖放等等1、事件循環QT中的所有事件&#…

基于 Vue2+Quill 的富文本編輯器全方案:功能實現與樣式優化

在 Web 開發中&#xff0c;富文本編輯器是內容管理系統、博客平臺等應用的核心組件。本文將詳細介紹如何基于 Vue 和 Quill 構建一個功能完善、樣式精美的富文本編輯器&#xff0c;重點解決字體字號選項冗長、樣式不美觀及功能完整性問題&#xff0c;提供可直接部署使用的完整方…

C#內嵌字符串格式化輸出

內嵌字符串格式輸出 double speedOfLight 299792.458;System.Globalization.CultureInfo.CurrentCulture System.Globalization.CultureInfo.GetCultureInfo("nl-NL"); string messageInCurrentCulture $"The speed of light is {speedOfLight:N3} km/s.&quo…

ThreeJS程序化生成城市大場景底座(性能測試)

一、簡介基于矢量geojson數據構建建筑、植被、道路等&#xff0c;實現城市場景底座。涉及渲染的性能優化無非就是眾所周知的那些事兒。視錐剔除、mesh合并、減少draw call、四叉樹、八叉樹、數據壓縮、WebWorker、著色器優化等。下面是對東莞市數十萬建筑以及海量3D樹的渲染測試…

?電風扇離線語音芯片方案設計與應用場景:基于 8 腳 MCU 與 WTK6900P 的創新融合

?電風扇離線語音芯片方案設計與應用場景&#xff1a;基于 8 腳 MCU 與 WTK6900P 的創新融合一、引言在智能家居領域蓬勃發展的當下&#xff0c;用戶對于家電產品的智能化和便捷性需求日益增長。傳統的電風扇控制方式&#xff0c;如按鍵操作或遙控器控制&#xff0c;在某些場景…

(第四篇)spring cloud之Consul注冊中心

目錄 一、介紹 二、安裝 三、整合代碼使用 1、創建服務提供者8006 2、創建服務消費者80 3、Eureka、zookeeper和consul的異同點 一、介紹 Consul 是一套開源的分布式服務發現和配置管理系統&#xff0c;由 HashiCorp 公司用 Go 語言開發。它提供了微服務系統中的服務治理…