PID控制算法(五)

复制代码
#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位则会出现波动,无法在最后精确达到所输入的数值。

运行结果如下:

相关推荐
一隅论数智11 小时前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
倒头就睡的小比特11 小时前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!11 小时前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
XiHongShi201611 小时前
STM32F407 RTC定时器例程,建议保存
stm32·单片机·学习
爱吃苹果的日记本12 小时前
离散数学第六课
学习·离散数学
猎头南楼12 小时前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
AI职业加油站13 小时前
AI智能体应用工程师证书:政策红利下的职业新风口
大数据·运维·人工智能·学习·职场发展
深圳老胡13 小时前
STM32F407 控制 L6470 步进电机驱动 —— 控制过程简介
笔记·stm32·单片机·嵌入式硬件·代码规范
旖旎夜光13 小时前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控