2020-09-24

万事开头难。
刚接触PID的时候调得焦头烂额,当把程序调出来后,发现其实挺简单。
其实也都是因为网上的发出来的例程说得都太复杂,或者是发的资料没全还藏了部分(个人感觉)。
闲话少说直接上资料:
先前发的一些网上的资料,程序原理与实现上主要参考了“PID控制算法的C语言实现.(绝对的好东西)”。
https://www.amobbs.com/thread-5685071-1-1.html

本次PID主要是通过固态继电器控制加热片进行加热,温度探测使用的DS18B20,稳定后在0.5度范围内(然并卵)。

程序如下:
定义结构体
typedef struct
{
    float SetSpeed;         //定义设定值
    float ActualSpeed;      //定义实际值
    float err;              //定义偏差值
    float err_next;         //定义上一个偏差值
    float err_last;         //定义上上一个的偏差值
    float Kp;               //定义比例系数
    float Ki;               //定义积分系数
    float Kd;               //定义微分系数
    float voltage;          //定义电压值(控制执行器的变量)
    float integral;         //定义积分值
    float umax;             //正极限位置
    float umin;             //负极限位置

}_PID_T;
_PID_T pid;


/*
    PID初始化
*/
void PID_init(void)
{
    pid.SetSpeed = 0.0;
    pid.ActualSpeed = 0.0;
    pid.err = 0.0;
    pid.err_last = 0.0;
    pid.err_next = 0.0;
    pid.Kp = 1.0;       //比例调节
    pid.Ki = 0.01;      //积分调节
    pid.Kd = 0.0;       //微分调节
}
/*
    取绝对值
*/
float Abs(float val)
{
    if(val<0)
    {
        val = -val;
    }
    return val;
}

/*
    PID计算
*/
float PIDCalc(float speed)
{
    UINT8 index=0;
    index = index;
//------------------------------------------------------------------------------------------------------------//
//积分分离式
    pid.SetSpeed = speed;
    pid.ActualSpeed = (float)DS18B20Temp()/10;
    pid.err = pid.SetSpeed - pid.ActualSpeed;
    if(Abs(pid.err)>3)      //温度差值超过3度,不加入积分环节
    {
        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;
    if(pid.voltage>50)
    {//设置一个PWM量程上限值
        pid.voltage = 50;
    }
    return pid.ActualSpeed;
}

定时器5中进行固态继电器的控制 
void TIM5_IRQHandler(void)
{
    static UINT8 heatTime=0;
        if(TIM5->SR&TIM_IT_Update)//溢出中断
        {
                TIM5->SR &= ~TIM_IT_Update ;//清除中断标志位 
                if(pid.ActualSpeed<40)
                {//调试时超过40度关闭加热,避免烧坏加热片
            if(pid.voltage>0)
                    {
                if(++heatTime>50)
                {
                    heatTime = 0;
                }
                ((heatTime*10)<(UINT8)(pid.voltage*10))?(HEAT_CTL = ON_OP):(HEAT_CTL = OFF_OP);
            }
            else
            {
                heatTime = 0;
                HEAT_CTL = OFF_OP;
            }

                }
        else
        {
            HEAT_CTL = OFF_OP;
        }
        }                                               
}

下面这个函数是在主函数里面调用的,我这里是250ms进行一次PID。
void DebugOut(void)
{
    UINT16 cnt=0;
    cnt = cnt;
    if(timerPara.timeDebug > 250)
    {
        timerPara.timeDebug = 0;
        if(PidEn)
        {
            PIDCalc(35.0);
        }
        else
        {
            pid.ActualSpeed = (float)DS18B20Temp()/10;
            pid.voltage=0;
        }
        printf("\r\nTEMPER=%f, U(t)=%f", pid.ActualSpeed,pid.voltage);
    }
}

这次调试主要觉得有两个难点:
1、程序算法怎么实现。参考上面的“PID控制算法的C语言实现.(绝对的好东西)”,基本就明白怎么写了,或者抄一下,网上的源码很多。
2、PID输出怎么用,U(t)是什么?算法程序有了,但是怎么拿来控制加热片进行加热?我反正是在网上找了好多圈,有写PID的基本都没说到这点,都只是说程序怎么实现,或者就是一句话,输出就是PWM。
说的其实也没错,只是不够明白。这里说说我的做法:
给输出设置一个上限,或者把输出当做PWM量程,比如50等分,实测中,实际温度与设置温度越来越靠近,PID的输出值会越来越小。那这个值就可以拿来做PWM方波的高电平,用于启动加热(参考定时器里的做法)。
3、参数的整定。网上的做法也很简单,一个参数一个参数调整,单个参数按一个方向进行调整。
a、把微积分的值设置为0,只调节比例参数,我这里调节到加热的时候不过冲,但是加热会比较慢。
b、调节积分参数,让稳定值会比较趋近到设置值。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值