《PID控制算法的C语言实现》学习笔记

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

//定义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;
}



你可能感兴趣的:(控制)