#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; // 积分饱和保护值float derivative_filter; // 微分项的滤波系数float err_pre_last;} 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 = 600;//积分保护最大设置pid->windup_guard = (pid->limit_max - pid->limit_min) * 0.1; // 设置积分饱和保护值pid->err_pre_last = 0.0;//微分项的滤波器系数设置pid->derivative_filter = 0.05f;}// 积分饱和处理:
//积分环节处理:
//积分分离、积分限制、反计算饱和
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) > 100){index = 0;//当误差的绝对值大于所设置的阈值时,标志位置零,但积分项仍然会进行误差累计}else{index = 1;pid->integral = pid->integral + pid->err ; // 积分误差积累//不需要在此时进行积分误差积累的抵消效果?}// 不完全微分://测微分变化趋势,减小对噪声高频信号噪声的放大程度:float derivative = (pid->err - pid->err_last) - (pid->err_last - pid->err_pre_last);float filtered_derivative = pid->derivative_filter * derivative +(1 - pid->derivative_filter) *(pid->err - 2 * pid->err_last + pid->err_pre_last);//pid->integral = pid->integral + pid->err; // 积分误差积累//PID输出计算
// pid->vo_out = pid->Kp * pid->err + pid->Ki * pid->integral +
// pid->Kd * (pid->err - pid->err_last);pid->vo_out = pid->Kp * pid->err + pid->Ki * pid->integral +pid->Kd * filtered_derivative;//积分饱和处理:计算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; // 本次误差更新pid->err_pre_last = pid->err_last; // 上一次的误差更新//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;
}
在上述代码中,相比之前的PID运算过程,本次在程序中添加并实现了PID算法的不完全微分过程,通过计算本次、上次以及上上次的误差,计算三者之间的差值来计算微分系数,并自行设置了滤波器系数来消除高频噪声所带来的误差,计算结果相比之前有所提升。通过修改设置的误差阈值和滤波器系数,可以一定程度上提高PID计算结果的精度。
运行结果如下(参数分别设为了1102.39和30.9):