PID控制算法

文章目录

    • 1 位式控制算法
      • 1.1 位式控制算法框图
      • 1.2 位式控制算法的特点
    • 2 比例、积分、微分算法
      • 2.1 PID算法框图
      • 2.2 比例控制
      • 2.3 积分控制
      • 2.4 微分控制
    • 3 PID算法
      • 3.1 PID算法的数学模型
      • 3.2 Sk的处理
      • 3.3 Dk的处理
      • 3.4 单片机中的PID算法表达式--位置式PID
      • 3.5 PID代码实现
    • 4 增量式PID--计算出控制量的增加值

1 位式控制算法

1.1 位式控制算法框图

PID控制算法_第1张图片

首先,用户可以通过按键、滑动电阻或者其他方式输入一个控制信号Sv给控制算法, 然后控制算法会结合输入信号Sv和输出采集回来的信号Pv来计算输出量out,然后由out信号控制执行部件,最后由执行部件作用到具体的控制对象上。最后我们要从被控制对象上采集需要控制的变量(可以是温度、湿度等等)反馈给控制算法做修正。

我们以一个水温控制系统为例来说明位式控制算法的原理。
用户设置部分输入一个温度值,比如我们希望控制水温为80℃,则Sv = 80;执行部件可以是一个继电器控制的加热丝,控制对象就是水箱里的水,传感器是一个温度传感器。

1.2 位式控制算法的特点

  1. 位式控制算法输出信号只有H、L两种状态。
  2. 算法输出信号out的依据(二位式):
    当 Pv > Sv ==> out = L (关断继电器,停止加热)
    当Pv ≤ Sv ==>out = H (打开继电器,开始加热)
  3. 控制简单,只是判断当前系统值和设定值的差值来调节。仅仅考虑了控制对象当前的状态值。
  4. 误差较大,因为被控制对象具有惯性,永远不可能有被控制对象状态值等于设定值这种情况。举例来说:由于热惯性的存在,当我们检测到温度达到设定温度时,关闭继电器,停止加热。虽然此时,加热丝不工作,但是由于热惯性的存在,导致温度还会继续上升;在开启加热时,也会出现类似的情况,虽然已经开启了加热,但是由于热惯性的存在,温度会继续下降一定范围,然后再开始升温。

2 比例、积分、微分算法

2.1 PID算法框图

PID控制算法_第2张图片
Sv:用户设置值,目标值。
Pv:控制对象的当前值。

我们假设,从开机以来传感器所有采样点的数据序列如下:
x1,x2,x3 ... xk-2, xk-1,xk

分析采样点的数据序列,可以挖掘出如下方面的信息:

2.2 比例控制

Ek = Sv - Xk

  • >0:当前未达标
  • =0:正好达标
  • <0:当前已超标

此时的POUT = Kp * Ek + OUT0,这就是比例控制。我们需要注意,此时的输出已经不是二位式控制方式的输出,而是PWM波的输出。另外,也需要注意到比例控制是在有误差的时候才控制,没误差的时候不参与控制。虽然,在最后加上了控制常数OUT0,但是这样做在某些情况下可能会取得适得其反的效果。

2.3 积分控制

我们可以计算出历史上每一个采样点的偏差,历史偏差序列如下:
E1、E2、E3 ... Ek-2、Ek-1、Ek

我们可以把历史上所有的偏差都给累计起来:

Sk = E1+E2+E3 +... +Ek-2+Ek-1+Ek

Sk可能有如下三种值:

  • >0:过去这段时间大多数时间未达标。
  • =0:过于这段时间大多数已达标。
  • <0:过于这段时间大多数已超标。

积分算法的公式如下:
IOUT = Kp*Sk + OUT0,我们可以看出来积分算法过于依赖于过于的值。

2.4 微分控制

将近两次的偏差相减:

Dk = Ek - Ek-1

Dk的值可能有如下三种情况:

  • >0:偏差有增大趋势
  • =0:偏差无变化
  • <0:偏差有减小趋势

DOUT = Kp * Dk + OUT0,我们称之为微分控制。我们可以看出来,微分控制只有在偏差变化趋势比较大的时候才有控制,因此单独的微分控制是没法使用的。


3 PID算法

3.1 PID算法的数学模型

PIDout = Pout + Iout + Dout
= (KpEk + out0) + (KpSk + out0) + (KpDk + out0)
= Kp
(Ek + Sk + Dk) + out0

3.2 Sk的处理

Sk =
T:采样周期(计算周期)。
Ti:积分常数(积分时间)。

3.3 Dk的处理

在这里插入图片描述
Td:微分常数。

3.4 单片机中的PID算法表达式–位置式PID

在这里插入图片描述

3.5 PID代码实现

static u16 UpdatePvCnt = 0;

typedef struct
{
	float Sv;	//用户设定值
	float Pv;	//当前值

	float Kp;	//比例系数
	float T;	//PID计算周期--采样周期
	float Ti;	//积分常数
	float Td;	//微分常数

	float Ek;	//本次偏差
	float Ek_1;	//上次偏差
	float SEk;	//历史偏差之和

	float OUT0;

	float OUT;

	u16 pwmCycle;//PWM周期

	u16 Cnt1ms; //1ms计时变量
}PID_Typedef;


PID_Typedef PID;

void PID_Out(void)	//输出PID运算结果到负载--每1ms被调用1次
{
	static u16 pw = 0;

	pw++;
	if (pw >= PID.pwmCycle)
	{
		pw = 0;
	}

	if (pw < PID.OUT)
	{
		//输出控制信号
	}
	else
	{
		//停止输出控制信号
	}
}

void TimerIsr(void)	//在1ms定时器中断函数中被调用
{
	PID.Cnt1ms++;
	UpdatePvCnt++;
	PID_Out();
}

void PID_UpdatePv(void)	//更新PV值,20ms更新一次
{
	if (UpdatePvCnt < 20)
	{
		return;
	}
	UpdatePvCnt = 0;

	//PID.Pv = NEW_PV;	//更新PV值
}

void PID_Init(void)
{
	PID.Sv = 120;			//用户设定值
	PID.Kp = 30;
	PID.T = 500;			//PID计算周期,单位ms
	PID.Ti = 5000000;			//积分时间
	PID.Td = 1000;			//微分时间
	PID.OUT0 = 1;			//常数
	PID.pwmCycle = 200;	
}

void PID_Calc(void)
{
	float deltaEk = 0;
	float ti = 0;
	float ki = 0;
	float td = 0;
	float kd = 0;
	float Pout = 0;
	float Iout = 0;
	float Dout = 0;
	float out = 0;

	if (PID.Cnt1ms < PID.T)					//计算周期到
	{
		return;
	}
	PID.Cnt1ms = 0;

	PID.Ek = PID.Sv - PID.Pv;				//得到当前的偏差值
	PID.SEk += PID.Ek;						//历史偏差总和

	deltaEk = PID.Ek - PID.Ek_1;			//最近两次偏差之差

	ti = PID.T / PID.Ti;
	ki = ti * PID.Kp;

	td = PID.Td / PID.T;
	kd = PID.Kp * td;

	Pout = PID.Kp * PID.Ek;					//比例输出
	Iout = ki * PID.SEk;					//积分输出
	Dout = kd * deltaEk;					//微分输出

	out = Pout + Iout + Dout;				//计算输出结果

	PID.Ek_1 = PID.Ek;

	if (out > PID.pwmCycle)
	{
		out = PID.pwmCycle;
	}

	if (out < 0)
	{
		out = 0;
	}

	PID.OUT = out;
}

void main(void)
{
	PID_Init();

	while (1)
	{
		PID_UpdatePv();
		PID_Calc();
	}
}


4 增量式PID–计算出控制量的增加值

PID控制算法_第3张图片

你可能感兴趣的:(控制理论,所学所思所想)