• PID库与PID基本优化(一)


    本系列旨在以我自己写的PID lib为例,讲一下PID的几点基本优化,PID的基本原理网上有很多资料,因此本系列将不会涉及PID的基本实现原理,在这里特别推荐Matlab tech talk的PID教程:https://ww2.mathworks.cn/videos/series/understanding-pid-control.html。

    由于笔者大一在读,还没有学习自动控制原理等课程,因此本系列将不会从自控原理角度展开,相反的,本系列将试图从“直觉”展开,通过直观的描述让大家从直觉上感受并理解PID的一些包括微分先行、积分分离等基础的优化。

    由于笔者水平有限,文中难免存在一些不足和错误之处,诚请各位批评指正。

    (一)中主要讲解代码结构与代码使用,算法有关内容于(二)开始讲解

    1 代码结构

    该PID lib全部代码详见:https://github.com/CharlesW1970/PID_Library

    1.1 PID结构体与有关枚举

    该lib通过pid结构体保存于pid运算有关的参数数据,通过枚举表示其他有关量:

    //PID结构体
    typedef struct _PID_TypeDef
    {
        float Target;
        float LastNoneZeroTarget;
        float Kp;
        float Ki;
        float Kd;
    
        float Measure;
        float Last_Measure;
        float Err;
        float Last_Err;
    
        float Pout;
        float Iout; //Iout = ITerm_0 + ITerm_1 +....+ ITerm_n
        float Dout;
        float ITerm; //ITerm = Err * Ki
    
        float Output;
        float Last_Output;
    
        float MaxOut;
        float IntegralLimit;
        float DeadBand;
        float ScalarA; //变积分公式参数
        float ScalarB; //ITerm = Err*((A-abs(err)+B)/A)  when B<|err|<A+B
    
        uint8_t Improve; //用于使能优化
    
        PID_ErrorHandler_t ERRORHandler;
    
        void (*PID_param_init)(
            struct _PID_TypeDef *pid,
            uint16_t maxOut,
            uint16_t integralLimit,
            float deadband,
            float Kp,
            float ki,
            float kd,
            float A,
            float B,
            uint8_t improve);
    
        void (*PID_reset)(
            struct _PID_TypeDef *pid,
            float Kp,
            float ki,
            float kd);
    } PID_TypeDef;
    
    //PID优化功能枚举
    typedef enum pid_Improvement_e
    {
        NONE = 0X00,                        //无
        Integral_Limit = 0x01,              //积分限幅
        Derivative_On_Measurement = 0x02,   //微分先行
        Trapezoid_Intergral = 0x04,         //梯形积分
        Proportional_On_Measurement = 0x08, //该系列不涉及
        OutputFilter = 0x10,                //输出滤波
        ChangingIntegralRate = 0x20,        //变积分
        ErrorHandle = 0x80,                 //异常处理
    } PID_Improvement_e;
    
    //异常情况枚举,这里只写了电机堵转保护一种
    typedef enum errorType_e
    {
        PID_ERROR_NONE = 0x00U,
        Motor_Blocked = 0x01U
    } ErrorType_e;
    
    //异常情况结构体
    typedef struct
    {
        uint64_t ERRORCount;
        ErrorType_e ERRORType;
    } PID_ErrorHandler_t;
    

    1.2 PID初始化

    在使用之前需要先调用PID_Init函数进行参数初始化和函数连接

    void PID_Init(
        PID_TypeDef *pid,
        uint16_t max_out,
        uint16_t intergral_limit,
        float deadband,
    
        float kp,
        float Ki,
        float Kd,
    
        float A,
        float B,
    
        uint8_t improve)
    {
        pid->PID_param_init = f_PID_param_init;
        pid->PID_reset = f_PID_reset; //连接Kp Ki Kd参数重设函数
        pid->PID_param_init(pid, max_out, intergral_limit, deadband,
                            kp, Ki, Kd, A, B, improve); //连接并调用参数初始化函数
    }
    
    static void f_PID_param_init(
        PID_TypeDef *pid,
        uint16_t max_out,
        uint16_t intergral_limit,
        float deadband,
    
        float kp,
        float Ki,
        float Kd,
    
        float Changing_Integral_A,
        float Changing_Integral_B,
    
        uint8_t improve)
    {
        //参数初始化
        pid->DeadBand = deadband;
        pid->IntegralLimit = intergral_limit;
        pid->MaxOut = max_out;
        pid->Target = 0;
    
        pid->Kp = kp;
        pid->Ki = Ki;
        pid->Kd = Kd;
        pid->ITerm = 0;
    
        pid->ScalarA = Changing_Integral_A;
        pid->ScalarB = Changing_Integral_B;
    
        pid->Improve = improve;
    
        //异常处理初始化
        pid->ERRORHandler.ERRORCount = 0;
        pid->ERRORHandler.ERRORType = PID_ERROR_NONE;
    
        pid->Output = 0;
    }
    

    1.3 PID 计算

    PID_Calculate函数与网上大多数代码大体结构相同,只是添加了不同的优化函数,具体优化在各函数(如:f_PID_ErrorHandle、f_Trapezoid_Intergral)中实现,PID_Calculate函数具体代码如下:

    float PID_Calculate(PID_TypeDef *pid, float measure, float target)
    {
        if (pid->Improve & ErrorHandle) 
        {
            //异常处理
            f_PID_ErrorHandle(pid);
            if (pid->ERRORHandler.ERRORType != PID_ERROR_NONE)
            {
                //电机堵转保护
                pid->Output = 0;
                return 0; 
            }
        }
    
        //误差更新
        pid->Measure = measure;
        pid->Target = target;
        pid->Err = pid->Target - pid->Measure;
    
        //死区内进行计算
        if (ABS(pid->Err) > pid->DeadBand)
        {
            //计算比例、微分输出与该周期积分项结果
            pid->Pout = pid->Kp * pid->Err;
            pid->ITerm = pid->Ki * pid->Err;
            pid->Dout = pid->Kd * (pid->Err - pid->Last_Err);
    
            //判断是否使能梯形积分
            if (pid->Improve & Trapezoid_Intergral)
                f_Trapezoid_Intergral(pid);
            //判断是否使能变积分
            if (pid->Improve & ChangingIntegralRate)
                f_Changing_Integral_Rate(pid);
            //判断是否使能积分限幅
            if (pid->Improve & Integral_Limit)
                f_Integral_Limit(pid);
            //判断是否使能微分先行
            if (pid->Improve & Derivative_On_Measurement)
                f_Derivative_On_Measurement(pid);
    
            //计算积分输出
            pid->Iout += pid->ITerm;
    
            //计算pid总输出
            pid->Output = pid->Pout + pid->Iout + pid->Dout;
    
            //判断是否使能输出滤波
            if (pid->Improve & OutputFilter)
                f_OutputFilter(pid);
    
            //输出限幅
            f_Output_limit(pid);
        }
        //数据保存供下一周期调用
        pid->Last_Measure = pid->Measure;
        pid->Last_Output = pid->Output;
        pid->Last_Err = pid->Err;
    
        return pid->Output;
    }
    

    2 如何使用

    这里给出以发布在GitHub上的示例,具体不在详细讲解

    //pid函数连接
    PID_Init(&PID_Example, 9600, 5000, 3, 1, 5, 0.3, 0.3, 100, 100, 
       		 ErrorHandle | Integral_Limit | OutputFilter);
    
    //修改kp ki kd
    PID_Example.PID_reset(&PID_Example, 3, 1, 0);
    
    //计算
    PID_Calculate(&PID_Example, measure, target);
    

    该篇对该lib结构和使用就讲到这里,下一篇将会开始算法讲解。

  • 相关阅读:
    VS2010 配置驱动开发环境
    C函数调用与入栈顺序
    Ecshop后台流量分析地区分布的地名全是乱码
    使用.net程序发送邮件代码
    齐博系统出现此文件不可写:cache/label_cache/index_0_8_0_0_1_6539c.php
    UCHOME中链接前多了link.php?url=,如何去除
    discuz7.2 修改数据调用中日期格式
    php设置和获取cookie
    删除数据库所有存储过程的SQL语句
    docker搭建skywalking 8.7简明笔记 海口
  • 原文地址:https://www.cnblogs.com/HongxiWong/p/12404424.html
Copyright © 2020-2023  润新知