python實(shí)現(xiàn)PID溫控算法的示例代碼
PID算法介紹
PID算法是一種常用的控制算法,用于調(diào)節(jié)和穩(wěn)定控制系統(tǒng)的輸出。
PID代表比例(Proportional)、積分(Integral)和微分(Derivative)
比例(Proportional):比例控制是根據(jù)當(dāng)前誤差的大小來產(chǎn)生輸出的一部分。誤差是指期望值與實(shí)際值之間的差異。比例控制通過將誤差乘以一個(gè)比例常數(shù)來產(chǎn)生輸出,該輸出與誤差成正比。比例控制的作用是使系統(tǒng)更快地響應(yīng)誤差,但可能會(huì)導(dǎo)致系統(tǒng)產(chǎn)生超調(diào)或震蕩。
積分(Integral):積分控制是根據(jù)誤差的累積來產(chǎn)生輸出的一部分。積分控制通過將誤差累積起來,并乘以一個(gè)積分常數(shù)來產(chǎn)生輸出,該輸出與誤差的積分成正比。積分控制的作用是消除系統(tǒng)的穩(wěn)態(tài)誤差,即系統(tǒng)在長(zhǎng)時(shí)間內(nèi)無法達(dá)到期望值的情況。
微分(Derivative):微分控制是根據(jù)誤差的變化率來產(chǎn)生輸出的一部分。微分控制通過將誤差的變化率乘以一個(gè)微分常數(shù)來產(chǎn)生輸出,該輸出與誤差的微分成正比。微分控制的作用是抑制系統(tǒng)的過沖和震蕩,使系統(tǒng)更加穩(wěn)定。
PID算法通過將這三個(gè)部分的輸出相加,得到最終的控制輸出。每個(gè)部分的權(quán)重可以通過調(diào)整相應(yīng)的常數(shù)來控制。PID算法的目標(biāo)是使系統(tǒng)的輸出盡可能接近期望值,并在系統(tǒng)受到擾動(dòng)時(shí)能夠快速恢復(fù)到期望狀態(tài)。
PID算法廣泛應(yīng)用于工業(yè)控制、自動(dòng)化系統(tǒng)、機(jī)器人控制、溫度控制等領(lǐng)域。它是一種簡(jiǎn)單而有效的控制算法,可以根據(jù)具體的系統(tǒng)和需求進(jìn)行調(diào)整和優(yōu)化。
PID參數(shù)作用
P參數(shù)控制器的輸出是與偏差(誤差)成比例的,即控制器輸出隨著系統(tǒng)的偏差增加而增加。P參數(shù)的作用是限制系統(tǒng)的上升時(shí)間和穩(wěn)定性,但過大的P值會(huì)導(dǎo)致震蕩和不穩(wěn)定的轉(zhuǎn)移函數(shù)。
I參數(shù)控制器的輸出是與偏差的積分成比例的,即控制器輸出隨著時(shí)間的累積而增加。
I參數(shù)的作用是消除系統(tǒng)的靜態(tài)誤差,即系統(tǒng)的偏差將在時(shí)間推移中逐漸消失,但過大的I值會(huì)導(dǎo)致超調(diào)和系統(tǒng)不穩(wěn)定。
D參數(shù)控制器的輸出是偏差的微分與時(shí)間成比例的,即控制器輸出隨著偏差的變化率的增加而增加。作用是降低系統(tǒng)的超調(diào)和減少震蕩,但過大的D值可能導(dǎo)致噪聲的放大或沒有響應(yīng)。
三個(gè)參數(shù)的綜合作用是控制系統(tǒng)的響應(yīng)速度(上升時(shí)間)、穩(wěn)定性和精度。 調(diào)整PID控制器的參數(shù)可以幫助控制系統(tǒng)達(dá)到更高的響應(yīng)速度和精度,同時(shí)保持系統(tǒng)的穩(wěn)定性。通常,通過試驗(yàn)和調(diào)整這些參數(shù),可以根據(jù)控制系統(tǒng)需求得到最佳的控制響應(yīng)。
簡(jiǎn)單來說就是:
- P <—> 比例控制<—>對(duì)當(dāng)前狀態(tài)的處理<—>提高響應(yīng)速度,過大則無靜差
- I <—> 微分控制<—>對(duì)過去狀態(tài)的處理<—>用于減小靜差
- D <—> 積分控制<—>對(duì)將來狀態(tài)的預(yù)測(cè)<—>用于抑制震蕩
位置式PID
位置式PID是當(dāng)前系統(tǒng)的實(shí)際位置,與你想要達(dá)到的預(yù)期位置的偏差,進(jìn)行PID控制
因?yàn)橛姓`差積分 ∑e(i) 一直累加,也就是當(dāng)前的輸出u(k)與過去的所有狀態(tài)都有關(guān)系,用到了誤差的累加值;
輸出的u(k)對(duì)應(yīng)的是執(zhí)行機(jī)構(gòu)的實(shí)際位置,一旦控制輸出出錯(cuò)(控制對(duì)象的當(dāng)前的狀態(tài)值出現(xiàn)問題 ),u(k)的大幅變化會(huì)引起系統(tǒng)的大幅變化
并且位置式PID在積分項(xiàng)達(dá)到飽和時(shí),誤差仍然會(huì)在積分作用下繼續(xù)累積,一旦誤差開始反向變化,系統(tǒng)需要一定時(shí)間從飽和區(qū)退出,所以在u(k)達(dá)到最大和最小時(shí),要停止積分作用,并且要有積分限幅和輸出限幅
所以在使用位置式PID時(shí),一般我們直接使用PD控制,而位置式 PID 適用于執(zhí)行機(jī)構(gòu)不帶積分部件的對(duì)象,如舵機(jī)和平衡小車的直立和溫控系統(tǒng)的控制
增量式PID
增量式PID(Incremental PID)是PID控制算法的一種變體,與傳統(tǒng)的位置式PID(Positional PID)相對(duì)應(yīng)。增量式PID算法通過計(jì)算當(dāng)前時(shí)刻的控制量與上一時(shí)刻的控制量之差,來得到增量控制量,從而實(shí)現(xiàn)對(duì)系統(tǒng)的控制。
在增量式PID中,控制器的輸出是一個(gè)增量值,而不是一個(gè)絕對(duì)值。增量控制量表示了控制器輸出的變化量,可以直接應(yīng)用于系統(tǒng)中,而無需考慮系統(tǒng)的初始狀態(tài)。
增量式PID相對(duì)于位置式PID的優(yōu)點(diǎn)是:
不受系統(tǒng)初始狀態(tài)的影響:增量式PID只關(guān)注控制量的變化,而不需要考慮系統(tǒng)的初始狀態(tài)。這使得增量式PID在系統(tǒng)啟動(dòng)時(shí)更加穩(wěn)定。減少積分飽和問題:位置式PID中的積分項(xiàng)可能會(huì)導(dǎo)致積分飽和問題,而增量式PID通過增量控制量的計(jì)算,可以減少積分飽和的發(fā)生。
然而,增量式PID也存在一些限制和注意事項(xiàng):
對(duì)控制器的輸出限制要求較高:增量式PID的輸出是控制量的增量,因此需要確??刂破鞯妮敵龇秶銐虼螅员苊廨敵鱿拗茊栴}。對(duì)采樣周期要求較高:增量式PID對(duì)采樣周期的要求較高,需要保證采樣周期足夠小,以減小誤差的累積。
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))
參數(shù)整定口訣
參數(shù)整定尋最佳,從大到小順次查。
先是比例后積分,最后再把微分加。
曲線振蕩很頻繁,比例度盤要放大。
曲線漂浮繞大彎,比例度盤往小扳。
曲線偏離回復(fù)慢,積分時(shí)間往下降。
曲線波動(dòng)周期長(zhǎng),積分時(shí)間再加長(zhǎng)。
理想曲線兩個(gè)波,調(diào)節(jié)過程高質(zhì)量。
調(diào)試效果圖


最終效果圖

調(diào)試代碼
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實(shí)現(xiàn)PID溫控算法的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于python PID溫控算法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解Python的迭代器、生成器以及相關(guān)的itertools包
這篇文章主要介紹了詳解Python的迭代器、生成器以及相關(guān)的itertools包,Iterators、Generators是Python的高級(jí)特性,亦是Python學(xué)習(xí)當(dāng)中必會(huì)的基本知識(shí),需要的朋友可以參考下2015-04-04
python unichr函數(shù)知識(shí)點(diǎn)總結(jié)
在本篇文章里小編給大家整理的是一篇關(guān)于python unichr函數(shù)的知識(shí)點(diǎn)總結(jié)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2020-12-12
Python中實(shí)現(xiàn)字符串類型與字典類型相互轉(zhuǎn)換的方法
這篇文章主要介紹了Python中實(shí)現(xiàn)字符串類型與字典類型相互轉(zhuǎn)換的方法,非常實(shí)用,需要的朋友可以參考下2014-08-08
用Python實(shí)現(xiàn)服務(wù)器中只重載被修改的進(jìn)程的方法
這篇文章主要介紹了用Python實(shí)現(xiàn)服務(wù)器中只重載被修改的進(jìn)程的方法,包括用watchdog來檢測(cè)文件的變化等,實(shí)現(xiàn)起來充分體現(xiàn)了Python作為動(dòng)態(tài)語(yǔ)言的靈活性,強(qiáng)烈推薦!需要的朋友可以參考下2015-04-04
使用Python操作Elasticsearch數(shù)據(jù)索引的教程
這篇文章主要介紹了使用Python操作Elasticsearch數(shù)據(jù)索引的教程,Elasticsearch處理數(shù)據(jù)索引非常高效,要的朋友可以參考下2015-04-04
Python數(shù)據(jù)處理的三個(gè)實(shí)用技巧分享
數(shù)據(jù)處理無所不在,掌握常用技巧,事半功倍。這篇文章將使用Pandas開展數(shù)據(jù)處理分析,總結(jié)其中常用、好用的數(shù)據(jù)分析技巧,感興趣的可以學(xué)習(xí)一下2022-04-04
使用Python3內(nèi)置文檔高效學(xué)習(xí)以及官方中文文檔
這篇文章主要給大家介紹了關(guān)于使用Python3內(nèi)置文檔高效學(xué)習(xí)以及官方中文文檔的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Python3具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
python算法與數(shù)據(jù)結(jié)構(gòu)之單鏈表的實(shí)現(xiàn)代碼
鏈表是一種物理存儲(chǔ)單元上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接次序?qū)崿F(xiàn)的。這篇文章主要介紹了python算法與數(shù)據(jù)結(jié)構(gòu)之單鏈表的實(shí)現(xiàn)代碼,需要的朋友可以參考下2019-06-06

