python实现PID温控算法的示例代码

 更新时间:2024年01月05日 16:48:20   作者:天才小C  
PID算法是一种常用的控制算法,用于调节和稳定控制系统的输出,这篇文章主要为大家详细介绍了如何使用Python实现pid温控算法,需要的可以参考下

PID算法介绍

PID算法是一种常用的控制算法,用于调节和稳定控制系统的输出。

PID代表比例(Proportional)、积分(Integral)和微分(Derivative)

比例(Proportional):比例控制是根据当前误差的大小来产生输出的一部分。误差是指期望值与实际值之间的差异。比例控制通过将误差乘以一个比例常数来产生输出,该输出与误差成正比。比例控制的作用是使系统更快地响应误差,但可能会导致系统产生超调或震荡。

积分(Integral):积分控制是根据误差的累积来产生输出的一部分。积分控制通过将误差累积起来,并乘以一个积分常数来产生输出,该输出与误差的积分成正比。积分控制的作用是消除系统的稳态误差,即系统在长时间内无法达到期望值的情况。

微分(Derivative):微分控制是根据误差的变化率来产生输出的一部分。微分控制通过将误差的变化率乘以一个微分常数来产生输出,该输出与误差的微分成正比。微分控制的作用是抑制系统的过冲和震荡,使系统更加稳定。

PID算法通过将这三个部分的输出相加,得到最终的控制输出。每个部分的权重可以通过调整相应的常数来控制。PID算法的目标是使系统的输出尽可能接近期望值,并在系统受到扰动时能够快速恢复到期望状态。

PID算法广泛应用于工业控制、自动化系统、机器人控制、温度控制等领域。它是一种简单而有效的控制算法,可以根据具体的系统和需求进行调整和优化。

PID参数作用

P参数控制器的输出是与偏差(误差)成比例的,即控制器输出随着系统的偏差增加而增加。P参数的作用是限制系统的上升时间和稳定性,但过大的P值会导致震荡和不稳定的转移函数。

I参数控制器的输出是与偏差的积分成比例的,即控制器输出随着时间的累积而增加。

I参数的作用是消除系统的静态误差,即系统的偏差将在时间推移中逐渐消失,但过大的I值会导致超调和系统不稳定。

D参数控制器的输出是偏差的微分与时间成比例的,即控制器输出随着偏差的变化率的增加而增加。作用是降低系统的超调和减少震荡,但过大的D值可能导致噪声的放大或没有响应。

三个参数的综合作用是控制系统的响应速度(上升时间)、稳定性和精度。 调整PID控制器的参数可以帮助控制系统达到更高的响应速度和精度,同时保持系统的稳定性。通常,通过试验和调整这些参数,可以根据控制系统需求得到最佳的控制响应。

简单来说就是:

  • P <—> 比例控制<—>对当前状态的处理<—>提高响应速度,过大则无静差
  • I <—> 微分控制<—>对过去状态的处理<—>用于减小静差
  • D <—> 积分控制<—>对将来状态的预测<—>用于抑制震荡

位置式PID

位置式PID是当前系统的实际位置,与你想要达到的预期位置的偏差,进行PID控制

因为有误差积分 ∑e(i) 一直累加,也就是当前的输出u(k)与过去的所有状态都有关系,用到了误差的累加值;

输出的u(k)对应的是执行机构的实际位置,一旦控制输出出错(控制对象的当前的状态值出现问题 ),u(k)的大幅变化会引起系统的大幅变化

并且位置式PID在积分项达到饱和时,误差仍然会在积分作用下继续累积,一旦误差开始反向变化,系统需要一定时间从饱和区退出,所以在u(k)达到最大和最小时,要停止积分作用,并且要有积分限幅和输出限幅

所以在使用位置式PID时,一般我们直接使用PD控制,而位置式 PID 适用于执行机构不带积分部件的对象,如舵机和平衡小车的直立和温控系统的控制

增量式PID

增量式PID(Incremental PID)是PID控制算法的一种变体,与传统的位置式PID(Positional PID)相对应。增量式PID算法通过计算当前时刻的控制量与上一时刻的控制量之差,来得到增量控制量,从而实现对系统的控制。

在增量式PID中,控制器的输出是一个增量值,而不是一个绝对值。增量控制量表示了控制器输出的变化量,可以直接应用于系统中,而无需考虑系统的初始状态。

增量式PID相对于位置式PID的优点是:

不受系统初始状态的影响:增量式PID只关注控制量的变化,而不需要考虑系统的初始状态。这使得增量式PID在系统启动时更加稳定。减少积分饱和问题:位置式PID中的积分项可能会导致积分饱和问题,而增量式PID通过增量控制量的计算,可以减少积分饱和的发生。

然而,增量式PID也存在一些限制和注意事项:

对控制器的输出限制要求较高:增量式PID的输出是控制量的增量,因此需要确保控制器的输出范围足够大,以避免输出限制问题。对采样周期要求较高:增量式PID对采样周期的要求较高,需要保证采样周期足够小,以减小误差的累积。

PID离散化公式

PID算法公式

PID算法公式如下:

PWM (k) =PWM(k-1)+Kp*(T(k)-T(k-1))+Ki*(T(k)-Ttarget)+Kd*(T(k)-2*T(k-1)+T(k-2))

参数整定口诀

参数整定寻最佳,从大到小顺次查。

先是比例后积分,最后再把微分加。

曲线振荡很频繁,比例度盘要放大。

曲线漂浮绕大弯,比例度盘往小扳。

曲线偏离回复慢,积分时间往下降。

曲线波动周期长,积分时间再加长。

理想曲线两个波,调节过程高质量。

调试效果图

最终效果图

调试代码

from cProfile import label
import time
from turtle import width
import numpy as np
import matplotlib.pyplot as plt
from subprocess import PIPE, Popen, DEVNULL
 
from numpy import append
 
 
def run(cmd, retype="r"):
    '''run System Command and Return Command Stdout Object'''
    try:
        with Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, encoding="utf-8") as f:
            Ret_Type = {"r": f.stdout.read, "rl": f.stdout.readline, "rls" : f.stdout.readlines, "rc": f.wait}
            if retype == 're':
                return f.stdout.read() + f.stderr.read()
            return Ret_Type[retype]()
    except Exception as e:
        print("\033[31mExecute Err:%s\033[0m"%e)
 
 
class DeltaPid(object):
    '''
        PID calculate
        pwm = pre_pwm + kp*(err-pre_ee) + ki*err + kd*(err-2*pre_err+pre_pre_ee)
    '''
    def __init__(self, target_temp, max_pwm, min_pwm, p, i, d):
        self.max_pwm = max_pwm
        self.min_pwm = min_pwm
        self.k_p = p
        self.k_i = i
        self.k_d = d
        self.target_temp = target_temp
        self._pre_temp = target_temp
        self._pre_pre_temp = target_temp - 1
 
    def calculate(self, cur_temp, pwm_in):
        # pwm = pre_pwm + kp*(err-pre_ee) + ki*err + kd*(err-2*pre_err+pre_pre_ee)
        pwm_out = 0
        p_change = self.k_p * (cur_temp - self._pre_temp)
        i_change = self.k_i * (cur_temp - self.target_temp)
        d_change = self.k_d * (cur_temp - 2 * self._pre_temp + self._pre_pre_temp)
        print(f"p:{p_change} i:{i_change} d:{d_change}")
 
        delta_output = p_change + i_change + d_change
        print(f"p+i+d output={delta_output}")
 
        pwm_out = delta_output + pwm_in
        print(f"calculate pwm={pwm_out}")
        self._pre_pre_temp = self._pre_temp
        self._pre_temp = cur_temp
 
        pwm_out = self.max_pwm if pwm_out > self.max_pwm else (self.min_pwm if pwm_out < self.min_pwm else pwm_out )
        print(f"actual output pwm={pwm_out}")
 
        return pwm_out
 
 
class Pwm(object):
    '''
        function1: set and get fan and heater pwm
        function2: get socket temp
    '''
    def __init__(self, path):
        self.path = f"{path}"
        self.fan_en = []
        self.heater_en = []
        self.fan_pwm = []
        self.heater_pwm = []
        self.temp = []
        for i in range(1, 5):
            self.fan_en.append(f"{self.path}fan{i}_en")
            self.heater_en.append(f"{self.path}heater{i}_en")
            self.fan_pwm.append(f"{self.path}fan{i}_pwm")
            self.heater_pwm.append(f"{self.path}heater{i}_pwm")
            self.temp.append(f"{self.path}temp{i}")
 
    def en_fan(self, index):
        cmd = f"echo 100 > {self.fan_en[index - 1]}"
        run(cmd)
 
    def en_heater(self, index):
        cmd = f"echo 100 > {self.heater_en[index - 1]}"
        run(cmd)
 
    def get_temp(self, index):
        cmd = f"cat {self.temp[index - 1]}"
        return run(cmd).replace('\n', '')
 
    def get_fan_pwm(self, index):
        cmd = f"cat {self.fan_pwm[index - 1]}"
        return run(cmd).replace('\n', '')
 
    def set_fan_pwm(self, pwm, index):
        cmd = f"echo {pwm} > {self.fan_pwm[index - 1]}"
        run(cmd)
 
    def get_heater_pwm(self, index):
        cmd = f"cat {self.heater_pwm[index - 1]}"
        return run(cmd).replace('\n', '')
 
    def set_heater_pwm(self, pwm, index):
        cmd = f"echo {pwm} > {self.heater_pwm[index - 1]}"
        run(cmd)
 
 
def filter(index, limit):
    usb_path = "/sys/dev/char/USB0/USB/"
    pwm = Pwm(usb_path)
    temp = []
    for i in range(0, 7):
        temp_old = int(pwm.get_temp(index))
        temp_new = int(pwm.get_temp(index))
        print(temp_old, temp_new)
        if abs(temp_old - temp_new) < limit: 
            temp.append(temp_old)
            temp.append(temp_new)
    print(temp)
    if not temp:
        return int(pwm.get_temp(index))
    return int(sum(temp)/len(temp))
 
 
def test(count=5000, target_temp = 105):
    usb_path = "/sys/dev/char/USB0/USB/"
    pwm = Pwm(usb_path)
    counts = np.arange(count)
    outputs = []
    pwms = []
    # enable fan and heater
    pwm.en_fan(1)
    pwm.en_heater(1)
 
    # initial fan and heater pwm
    pwm.set_fan_pwm(0, 1)
    pwm.set_heater_pwm(100, 1)
 
    pid = DeltaPid(target_temp, 45, 5, 10, 0.7, 0.3)
    print(f"Now temp is {pwm.get_temp(1)}")
    print("start test ...")
    print(f"set heater pwm to 100, target temp is {target_temp} ...")
 
    # set temp to (target) and keep heater in 80 pwm
    print(f"time: {time.ctime()}")
    while True:
        temp1 = filter(1, 20)
        print(f"Now temp is {temp1}")
        time.sleep(1)
        if temp1 / 10 >= (target_temp):
            print("keep heater pwm to 80 ...")
            pwm.set_fan_pwm(35, 1)
            pwm.set_heater_pwm(80, 1)
            break
            # draw
    print(f"time: {time.ctime()}")
 
    for i in counts:
        print(f"No.{i} pid adjust")
        temp1 = filter(1, 20)
        now_fan_pwm = int(pwm.get_fan_pwm(1))
        pwms.append(now_fan_pwm)
        print(f"temp={temp1}C , fan pwm={now_fan_pwm}")
        now_pwm = pid.calculate(int(temp1) / 10, now_fan_pwm)
        pwm.set_fan_pwm(int(now_pwm), 1)
        time.sleep(1)
        outputs.append(int(temp1) / 10)
 
    print('Done')
 
    # draw
    plt.figure()
    plt.axhline(target_temp, c='red', label = "target_temp")
    plt.axhline(target_temp-3, c='yellow', label = "target_temp_min")
    plt.axhline(target_temp+3, c='red', label = "target_temp_max")
    plt.plot(counts, np.array(outputs), 'b.')
    plt.ylim(0, 130)
    plt.plot(counts, outputs, label = "temp")
    plt.plot(counts, pwms, label = "pwm")
    plt.title("PID")
    plt.xlabel('count')
    plt.ylabel('temperature')
    plt.legend()
    plt.tick_params(axis='both', width=1, length=5)
    plt.xticks(fontsize=13)
    plt.yticks(fontsize=13)
    plt.show()
 
 
if __name__ == "__main__":
    test()

以上就是python实现PID温控算法的示例代码的详细内容,更多关于python PID温控算法的资料请关注脚本之家其它相关文章!

相关文章

  • python类定义的讲解

    python类定义的讲解

    python是怎么定义类的,看了下面的文章大家就会了,不用多说,开始学习。
    2013-11-11
  • Python使用装饰器进行django开发实例代码

    Python使用装饰器进行django开发实例代码

    这篇文章主要介绍了Python使用装饰器进行django开发实例代码,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-02-02
  • Python 串口通信的实现

    Python 串口通信的实现

    这篇文章主要介绍了Python的串口通信的相关资料,帮助大家更好的理解和学习python,感兴趣的朋友可以了解下
    2020-09-09
  • 解决Pytorch 加载训练好的模型 遇到的error问题

    解决Pytorch 加载训练好的模型 遇到的error问题

    今天小编就为大家分享一篇解决Pytorch 加载训练好的模型 遇到的error问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-01-01
  • Python与shell的3种交互方式介绍

    Python与shell的3种交互方式介绍

    这篇文章主要介绍了Python与shell的3种交互方式介绍,本文讲解了os.system、os.popen、subprocess模块等3种方法,需要的朋友可以参考下
    2015-04-04
  • python接口自动化使用requests库发送http请求

    python接口自动化使用requests库发送http请求

    这篇文章主要介绍了python接口自动化使用requests库发送http请求,HTTP协议 ,一个基于TCP/IP通信协议来传递数据,包括html文件、图像、结果等,即是一个客户端和服务器端请求和应答的标准
    2022-08-08
  • Python字典删除键值对和元素的四种方法(小结)

    Python字典删除键值对和元素的四种方法(小结)

    删除列表或者字符串元素的方法不止一种,同样,删除字典元素的方法也不止一种,本文主要介绍python中删除字典元素的四种方法:1、使用del语句;2、使用clear();3、使用pop();4、使用popitem()。感兴趣的可以了解一下
    2021-12-12
  • Python安装第三方库及常见问题处理方法汇总

    Python安装第三方库及常见问题处理方法汇总

    本文给大家汇总介绍了Python安装第三方库及常见问题处理方法,非常的简单使用,有需要的小伙伴可以参考下
    2016-09-09
  • 关于numpy数组轴的使用详解

    关于numpy数组轴的使用详解

    今天小编就为大家分享一篇关于numpy数组轴的使用详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • python目标检测SSD算法训练部分源码详解

    python目标检测SSD算法训练部分源码详解

    这篇文章主要为大家介绍了python目标检测SSD算法训练部分源码详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05

最新评论