PID算法

2019-07-15  本文已影响0人  恰似一碗咸鱼粥

1.比例控制

设定一个标准值S_v,传感器每次返回一个值P_v,这两个值之差构成了一个序列E_1,E_2,...,E_k,每次根据这个偏差输出一个POUT=K_pE_k+OUT_0,OUT0为偏置值,Kp为比例系数。其中偏差包括当前偏差、历史偏差和最近偏差。

2.积分控制

对每一次得到传感器的返回值E_k,返回之前所有偏差值之和S_kIOUT=K_pS_k,但是单纯的积分控制也会出现问题,如果历史的偏差值是好的,便不再管现在的偏差值到底是大是小,会导致历史情况干扰当前情况。所以与比例控制相同,我们也许增加一个偏置值IOUT=K_pS_k+OUT_0

3.微分控制

前一个时刻差值为E_{k-1},当前时刻差E_{k},对这两个时间点,将其偏差相减。D_k=E_k-E_{k-1},它可以说明我们的偏差的变化趋势。同样也会加一个偏置,D_k=E_k-E_{k-1}+OUT_0,out0用于维持。

4.参数设置

PID_{out}=K_p*(E_k+S_k+D_k)+OUT_0

(1)Sk处理

S_k=\frac{1}{T_i}*\sum_{k=0}^{n}E_k*T,T称为采样周期,即间隔一部分时间,保证上一次算出得结果已经输出,在不同的情况下,采样周期要改变。Ti称为积分常数,它是人为确定的,它越大,则PID最终输出结果越强,反之则削弱。若果Ti过小,则非常容易产生过冲,即先超过额定值,再慢慢下降到额定值。积分项在比例项失效时发生作用。

(2)Dk处理

D_k=\frac{E_k-E_{k-1}}{T}*T_d,这里的T也是采样周期,Td为微分常数。

此时新的单片机中PID算法表达式:
OUT=K_pE_k+K_p\frac{T}{T_i}S_k+K_p\frac{T_d}{T}(E_k-E_{k-1})+out_0
这种方法称为位置式PID,但是有大量历史数据累加,计数比较繁琐。

5.增量式PID

对于控制系统本身具有记忆功能,可以采用增量式PID
增量式PID即\Delta out=out_k-out_{k-1}

6.C语言PID伪代码

//pid.h
#ifndef _pid_
#define _pid_
#include "stm32f10x_conf.h"

typedef struct{
    float Sv;//用户设定值
    float Pv;//实际值

    float Kp;
    float T;//计算周期或者采样周期
    float Ti;
    float Td;

    float Ek,Ek_1;//本次偏差与上一次偏差
    float SEk;//历史偏差之和

    float OUT0;

    float C10ms;
    float pwmcycle;//pwm周期

    float OUT;//本次应该输出的值
}PID;

extern PID pid;//存放pid算法所需的数据 
void PID_calc();
#endif

//pid.c
#include "pid.h"

PID pid;//存放PID的数据

void PID_calc(){//pid计算
    float DelEk;
    float ti=pid.T/pid.Ti;
    float ki=ti*pid.Kp;

    float td=pid.Td/pid.T;
    float kd=pid.Kp*td;

    float Iout,Pout,Dout;

    if(pid.C10ms<pid.T/10){
        //计算周期没到
        return;
    }
    pid.Ek=pid.Sv-pid.Pv;//得到当前的偏差值

    pid.SEk+=pid.Ek;//历史偏差总和 

    DelEk=pid.Ek-pid.Ek_1;//最近两次的偏差之差

    Pout=pid.Kp*pid.Ek;//比例
    Iout=pid.SEk*ki;//积分
    Dout=kd*DelEk;//微分

    if(Pout+Iout+Dout+pid.OUT0>pid.pwmcycle){
        pid.OUT=pid.pwmcycle;
    }
    else if(Pout+Iout+Dout+pid.OUT0<0){
        pid.OUT=0;
    }else{
        pid.OUT=Pout+Iout+Dout+pid.OUT0;
    }

    pid.Ek_1=pid.Ek;//更新偏差 

    pid.C10ms=0;
}

对于控制输出部分:

void PID_out{
    static u16 pw;
    pw++;
    if(pw>=pid.pwmcycle){
        pw=0;
    }
    //每次dosomething为1ms
    if(pw<pid.OUT){
        //do something
    }
    else{
        //do something else
    }
}
上一篇下一篇

猜你喜欢

热点阅读