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控制的主要内容,如果未能解决你的问题,请参考以下文章