#include <stdio.h>
#include <stdlib.h>
// 定义PID结构体
typedef struct
{
float SetSpeed;
float ActualSpeed;
float err;
float integral;
float vo_out; //控制器输出
float err_last;
float Kp;
float Ki;
float Kd;
float limit_min; // 输出限制最小值
float limit_max; // 输出限制最大值
float windup_guard; // 积分饱和保护值
} PID;
// 函数声明
void PID_init(PID *pid);
float PID_realize(PID *pid, float Setparameter);
// 函数定义
/*
void PID_init(PID *pid,float Kp, float Ki, float Kd,
float vo_out_limit_min, float vo_out_limit_max)
*/
void PID_init(PID *pid)
{
// 初始化PID参数
pid->Kp=0.1;
pid->Ki=0.1;
pid->Kd=0.1;
pid->SetSpeed = 0.0;
pid->ActualSpeed = 0.0;
pid->vo_out = 0.0;
pid->integral = 0.0;
pid->err = 0.0;
pid->err_last = 0.0;
pid->limit_min = -100;//积分保护最小设置
pid->limit_max = 100;//积分保护最大设置
pid->windup_guard = (pid->limit_max - pid->limit_min) * 0.1; // 设置积分饱和保护值
}
// 积分饱和处理:
void PID_IntegralWindup(PID *pid)
{
//将两个参数在结构体中提前定义好:
//积分限制,反计算饱和
double vo_out_limit_min; // 输出限制最小值
double vo_out_limit_max; // 输出限制最大值
if (pid->vo_out >= pid->limit_max) //当输出达到积分保护最大值时
{
if (pid->err > 0)
{
pid->integral += pid->err;//积分误差累计正偏差
}
}
else if (pid->vo_out <= pid->limit_min)//当输出小于积分保护最小值时
{
if (pid->err < 0)
{
pid->integral += pid->err;//积分误差累计正偏差
}
}
else //此时输出值在设置的最大和最小值区间内
{
pid->integral += pid->err;//正常计算积分误差
}
}
//积分环节处理:
//积分分离、积分限制、反计算饱和
float PID_realize(PID *pid, float Setparameter)
{
int index;//标志位,在index = 1时执行积分的稳态误差积累
pid->SetSpeed = Setparameter;
pid->err = pid->SetSpeed - pid->ActualSpeed;
PID_IntegralWindup(pid); //反计算抗饱和处理
//积分分离,设置误差阈值
//当误差较大时,放弃积分项的运算,当误差较小,计算积分误差积累
if(abs(pid->err) > 200)
{
index = 0;
}
else
{
index = 1;
pid->integral = pid->integral + pid->err; // 积分误差积累
}
//pid->integral = pid->integral + pid->err; // 积分误差积累
pid->vo_out = pid->Kp * pid->err + pid->Ki * pid->integral +
pid->Kd * (pid->err - pid->err_last);//PID输出计算
//积分饱和处理:计算PID控制器的输出
if (pid->vo_out > pid->limit_max)
{
pid->vo_out = pid->limit_max; // 限制输出最大值
}
else if (pid->vo_out < pid->limit_min)
{
pid->vo_out = pid->limit_min; // 限制输出最小值
}
pid->err_last = pid->err; // 误差更新
//float浮点数--数据类型
pid->ActualSpeed = pid->vo_out*2.0; // 将输出值赋给实际值,此前是1.0,现在设置为2.0做补偿;
return pid->ActualSpeed;
}
int main()
{
PID myPID; // 创建PID控制器实例
// 初始化PID参数
PID_init(&myPID);
float Setparameter;//传感器获取的参数值(温、湿、气压?)
printf("Enter the set parameter: ");
scanf("%f", &Setparameter); // 读取用户输入的目标参数
int count= 0 ;
while(count<1000)
{
myPID.ActualSpeed = PID_realize(&myPID, Setparameter); // 调用PID控制函数
if(count >= 1)//只显示了最后200次的计算结果
{
printf("count is: %d, actual_speed is: %f\n", count, myPID.ActualSpeed);
}
count++;
}
return 0;
}
上述代码存在一定问题:对于输入值( ≤ 200)时,整数可以较好的达到,而当输入数据到小数后1、2位则会出现波动,无法在最后精确达到所输入的数值。
运行结果如下: