摘自:https://blog.csdn.net/u014100311/article/details/81204208
額外參考:http://www.stmcu.org.cn/module/forum/thread-600738-1-1.html
https://www.cnblogs.com/steed/p/7808303.html
1. PID算法原理
- 如果偏差爲0,則比例環節不起作用;
- 積分環節主要是用來消除靜差,即系統穩定後輸出值和設定值之間的差值;
- 微分環節則反映了偏差信號的變化規律,根據偏差信號的變化趨勢來進行超前調節,從而增加系統快速性。
2. 不同類型PID算法
2.1 位置式PID:
2.2 增量式PID:
2.3 積分分離PID:
普通PID中,引入積分環節的目的主要是爲了消除靜差,提高控制精度,但是在啓動、結束或大幅增減設定時,短時間內系統輸出有很大的偏差,會造成PID運算的積分積累,可能導致控制量超過執行機構允許的極限控制量,從而引起較大的超調甚至振盪。
爲了克服這一問題,引入了積分分離的概念,其基本思路是當被控量與設定值偏差較大時,取消積分作用;當控制量接近給定值時,引入積分控制以消除靜差,提高精度。
2.4 抗積分飽和PID:
積分飽和現象是指如果系統存在一個方向的偏差,PID控制器的輸出由於積分作用不斷累加而加大,從而導致執行機構達到極限位置,若控制器輸出繼續增大,執行器開度不可能再增大,此時計算機輸出控制量超過了正常運行範圍而進入飽和區。一旦系統出現反向偏差,u(k)逐漸從飽和區退出。進入飽和區越深則退出飽和區時間越長,在這段時間裏,執行機構仍停留在極限位置而不隨偏差反向而立即做出相應改變,系統就像失控一樣,造成控制系統惡化。
防止積分飽和的方法之一就是抗積分飽和法,該方法思路是在計算u(k)時,先判斷上一時刻控制量u(k-1)是否超過了極限,如果是,則只累加反向偏差,從而避免控制量長時間停留在飽和區。
3. C語言實現
-
#include "stdio.h"
-
#include<stdlib.h>
-
//定義PID結構體
-
struct _pid{
-
float SetSpeed;
-
float ActualSpeed;
-
float err;
-
float err_last;
-
float Kp,Ki,Kd;
-
//位置式pid
-
float voltage;
-
float integral;
-
//增量式pid
-
float err_next;
-
//抗積分飽和PID
-
float umax;
-
float umin;
-
}pid;
-
//初始化變量(基於位置式PID)
-
void PID_init() {
-
printf("PID_init begin\n");
-
pid.SetSpeed = 0.0;
-
pid.ActualSpeed = 0.0;
-
pid.err = 0.0;
-
pid.err_last = 0.0;
-
pid.voltage = 0.0;
-
pid.integral = 0.0;
-
pid.Kp = 0.2;
-
pid.Ki = 0.015;
-
pid.Kd = 0.2;
-
printf("PID_init end \n");
-
}
-
//編寫控制算法
-
//位置式PID
-
float positional_PID_realize(float speed) {
-
pid.SetSpeed = speed;
-
pid.err = pid.SetSpeed - pid.ActualSpeed;
-
pid.integral += pid.err;
-
pid.voltage = pid.Kp*pid.err + pid.Ki*pid.integral + pid.Kd*(pid.err - pid.err_last);
-
pid.err_last = pid.err;
-
pid.ActualSpeed = pid.voltage*1.0;
-
return pid.ActualSpeed;
-
}
-
//增量式PID
-
float incremental_PID_realize(float speed) {
-
pid.err_next = 0.0;
-
pid.SetSpeed = speed;
-
pid.err = pid.SetSpeed - pid.ActualSpeed;
-
float incrementSpeed = pid.Kp*(pid.err - pid.err_next) + pid.Ki*pid.err + pid.Kd*(pid.err - 2 * pid.err_next + pid.err_last);
-
pid.ActualSpeed += incrementSpeed;
-
pid.err_last = pid.err_next;
-
pid.err_next = pid.err;
-
return pid.ActualSpeed;
-
}
-
//積分分離PID
-
float IntegralSeparatio_PID_realize(float speed) {
-
int index;
-
pid.Kp = 0.2;
-
pid.Ki = 0.04;
-
pid.Kd = 0.2;
-
pid.SetSpeed = speed;
-
pid.err = pid.SetSpeed - pid.ActualSpeed;
-
if (abs(pid.err) > 200) {
-
index = 0;
-
}
-
else {
-
index = 1;
-
pid.integral += pid.err;
-
}
-
pid.voltage = pid.Kp*pid.err + index*pid.Ki*pid.integral + pid.Kd*(pid.err - pid.err_last);
-
pid.err_last = pid.err;
-
pid.ActualSpeed = pid.voltage*1.0;
-
return pid.ActualSpeed;
-
}
-
//抗飽和PID
-
float anti_windup_PID_realize(float speed) {
-
pid.Kp = 0.2;
-
pid.Ki = 0.1;
-
pid.Kd = 0.2;
-
pid.umax = 400;
-
pid.umin = -200;
-
int index;
-
pid.SetSpeed = speed;
-
pid.err = pid.SetSpeed - pid.ActualSpeed;
-
if (pid.ActualSpeed > pid.umax) {
-
if (abs(pid.err) > 200)
-
{
-
index = 0;
-
}
-
else {
-
index = 1;
-
if (pid.err < 0)
-
{
-
pid.integral += pid.err;
-
}
-
}
-
}
-
else if (pid.ActualSpeed < pid.umin) {
-
if (abs(pid.err) > 200)
-
{
-
index = 0;
-
}
-
else {
-
index = 1;
-
if (pid.err > 0)
-
{
-
pid.integral += pid.err;
-
}
-
}
-
}
-
else {
-
if (abs(pid.err) > 200)
-
{
-
index = 0;
-
}
-
else {
-
index = 1;
-
pid.integral += pid.err;
-
}
-
}
-
pid.voltage = pid.Kp*pid.err + index*pid.Ki*pid.integral + pid.Kd*(pid.err - pid.err_last);
-
pid.err_last = pid.err;
-
pid.ActualSpeed = pid.voltage*1.0;
-
return pid.ActualSpeed;
-
}
-
//測試代碼
-
int main() {
-
printf("system begin\n");
-
PID_init();
-
int count = 0;
-
while (count < 500)
-
{
-
float speed = anti_windup_PID_realize(200.0);
-
printf("%f\n", speed);
-
count++;
-
}
-
system("pause");
-
return 0;
-
}
- 1