PID学习笔记:模拟加热系统的PID控制

Posted fanchenxinok

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PID学习笔记:模拟加热系统的PID控制相关的知识,希望对你有一定的参考价值。

自己大学专业就是学自动化的,现在干嵌入式开发,太久了把本科学的东西都还给老师了,惭愧。项目需要用到PID算法,现在重新温习下。

关于PID的原理、公式及怎么理解PID控制过程的文章一大堆就不再赘述了,推荐这篇文章:https://zhuanlan.zhihu.com/p/39573490

 一、本篇文章的目的是编程实现模拟加热系统的温度控制。 先给出PID公式:

 

其中: P[t], I[t], D[t]分别表示t时刻P(比例)、I(积分)、D(微分)的值 

            E[t]为目标值和t时刻采样值的偏差

            T为采样时间间隔

            为微分低通滤波时间常数

            Kp、Ki、Kd分别为比例、积分、微分系数

            OUT[t]为t时刻PID的输出值 

假设目标值是S,t时刻的采样值是M[t],那么

推导: 

因此微分项也可以表示为:

 

系统框图:

  

关于PID离散化的公式是怎么推导出来的,参考文章:https://blog.csdn.net/qq_27158179/article/details/82912600 

 二、Python代码实现:

# coding=gbk
from math import pi
from matplotlib import pyplot as plt
from PIL import Image,ImageDraw
import numpy as np

DEFAULT_PID_KP = 4.0
DEFAULT_PID_KI = 0.5
DEFAULT_PID_KD = 0.25
DEFAULT_PID_TAU = 0.02

PID_OUT_LIM_MIN = -240.0 #PID输出最小值限制
PID_OUT_LIM_MAX = 240.0  #PID输出最大值限制

PID_LIM_MIN_INT = -5.0  #PID积分值最小限制
PID_LIM_MAX_INT = 5     #PID积分值最大限制

SAMPLE_TIME = 0.01

SET_TEMP = 60   #目标温度

class PidController(object):
    #定义属于类的成员,每个实例都共享该变量
    Kp = DEFAULT_PID_KP
    Ki = DEFAULT_PID_KI
    Kd = DEFAULT_PID_KD

    # kp为比例系数,ki为积分系数,kd为微分系数, smp_t为采样间隔单位秒
    def __init__(self, kp, ki, kd, smp_t):
        self.__integrator = 0.0
        self.__prevError = 0.0
        self.__prevMeasurement = 0.0   # 加__ 表示私有变量,只能内部成员才能访问
        self.__differentiator = 0.0
        self.__out = 0.0
        self.Kp = kp
        self.Ki = ki
        self.Kd = kd
        self.T = smp_t
        self.tau = DEFAULT_PID_TAU
        self.limMaxInt = PID_LIM_MAX_INT
        self.limMinInt = PID_LIM_MIN_INT
        self.limMaxOut = PID_OUT_LIM_MAX
        self.limMinOut = PID_OUT_LIM_MIN
    
    def PidController_Update(self, setpoint, measurement):
        error = setpoint - measurement  # 计算目标值和实际检测值的差值
        #print(setpoint, measurement)
        proportional = self.Kp * error  # 计算比例P
        self.__integrator = self.__integrator + 0.5 * self.Ki * self.T * (error + self.__prevError) # 计算积分I
        if self.__integrator > self.limMaxInt:
            self.__integrator = self.limMaxInt;
        elif self.__integrator < self.limMinInt:
            self.__integrator = self.limMinInt;
        #计算微分D
        self.__differentiator = -(2.0 * self.Kd * (measurement - self.__prevMeasurement) + (2.0 * self.tau - self.T) * self.__differentiator) / (2.0 * self.tau + self.T);
        #计算PID输出
        self.__out = proportional + self.__integrator + self.__differentiator
        if self.__out > self.limMaxOut:
            self.__out = self.limMaxOut
        elif self.__out < self.limMinOut:
            self.__out = self.limMinOut
        self.__prevError = error
        self.__prevMeasurement = measurement
        #print('P = ', round(proportional,6), '\\t', 'I = ', round(self.__integrator,6), '\\t', 'D = ', round(self.__differentiator,6))

    def PidController_GetPidOut(self):
        return self.__out

#模拟加热系统
def Calc_Measurement(pwm, preMeasurement):
    alpha = 0.02
    m = (SAMPLE_TIME * pwm + preMeasurement) / (1.0 + alpha * SAMPLE_TIME) - SAMPLE_TIME * 0.2 #SAMPLE_TIME * 0.2为散热部分
    return m

def PID_Run(kp, ki, kd):
    
    t_array = []    #保存时间轴的值
    m_array = []    #保存经过PID控制后系统输出值
    o_array = []    #保存PID控制量的值
    h_array = []    #保存固定PID输入量对应系统输出值
    w_array = []    #保存PID控制量对应的PWM值

    pid_ctrl = PidController(kp, ki, kd, SAMPLE_TIME)
    
    t = 0.0
    preMeasurement = 0.0
    set_point = SET_TEMP #目标值

    tmp = 0.0
    pwm = 0    # 0% 到 100%
    while t < 10.0:
        t_array.append(t)
        pid_out = pid_ctrl.PidController_GetPidOut()
        #用pid out来计算应该加热的pwm
        pwm = int(pid_out+0.5)  #取整
        if pwm >= 100:
            pwm = 100
        elif pwm <= 0:
            pwm = 0
        #用pwm控制加热系统,模拟重新获取温度值
        newMeasurement = Calc_Measurement(pwm, preMeasurement)
        pid_ctrl.PidController_Update(set_point, newMeasurement)
        preMeasurement = newMeasurement
        print('%0.6f\\t%0.6f\\t%0.6f' % (t, newMeasurement, pid_ctrl.PidController_GetPidOut()))
        t = t + SAMPLE_TIME
        pid_out = pid_ctrl.PidController_GetPidOut() #重新计算后的pid out
        o_array.append(pid_out)
        m_array.append(newMeasurement)
        w_array.append(pwm)

        #给定固定的pwm=100%输入,看看Calc_Measurement函数的曲线
        out = Calc_Measurement(100, tmp)
        h_array.append(out)
        tmp = out
    
    fig, axs = plt.subplots(2,2)
    plt.suptitle('Kp=%0.2f,Ki=%0.2f,Kd=%0.2f' % (pid_ctrl.Kp, pid_ctrl.Ki, pid_ctrl.Kd))
    fig.subplots_adjust(hspace=0.5, wspace=0.5) #设置子图间距
    axs[0,0].set_title('measurement')
    axs[0,0].grid(True)
    axs[0,0].plot(t_array, m_array, c='b', linewidth=2)
    axs[0,1].set_title('PID out')
    axs[0,1].grid(True)
    axs[0,1].plot(t_array, o_array, c='r', linewidth=2)
    axs[1,0].set_title('PWM')
    axs[1,0].grid(True)
    axs[1,0].plot(t_array, w_array, c='m', linewidth=2)
    axs[1,1].set_title('heat simulate')
    axs[1,1].grid(True)
    axs[1,1].plot(t_array, h_array, c='k', linewidth=2)
    plt.show()
    #plt.savefig('figure.png', dpi=200) #保存就不能show,否则图片没东西
    #plt.pause(1)
    plt.close()


if __name__ == '__main__':
    Kp = DEFAULT_PID_KP
    Ki = DEFAULT_PID_KI
    Kd = DEFAULT_PID_KD
    PID_Run(Kp, Ki, Kd)

    '''
    Kp_step = 0.5
    Ki_step = 0.1
    Kd_step = 0.08
    #Kp变化
    for i in range(10):
        PID_Run(Kp, Ki, Kd)
        Kp = Kp + Kp_step
    #Ki变化
    Kp = DEFAULT_PID_KP
    for i in range(10):
        PID_Run(Kp, Ki, Kd)
        Ki = Ki + Ki_step
    #Kd变化
    Ki = DEFAULT_PID_KI
    for i in range(10):
        PID_Run(Kp, Ki, Kd)
        Kd = Kd + Kd_step
    '''

三、仿真结果:

 

 说明:程序默认设置参数Kp = 4.0, Ki = 0.5, Kd = 0.25。目标温度是60度。measurement曲线是模拟PID控制后的采样温度值。PID out曲线是PID输出值,PWM为根据PID out计算系统的加热量。通过PWM控制加热功率,比如PWM为100%就是加足马力加热,PWM为0则不加热。heat simulate曲线为固定PWM输入的情况下的加热曲线。

以上是关于PID学习笔记:模拟加热系统的PID控制的主要内容,如果未能解决你的问题,请参考以下文章

PID学习笔记:模拟加热系统的PID控制

PID通俗理解

基于模糊PID控制的电加热炉温度控制系统设计

记录下PID整定的过程

记录下PID整定的过程

串级控制PID 炉温控制