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;
-
}
</div>
</div>
</article># 欢迎使用Markdown编辑器写博客