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í)際值之間的差異。比例控制通過將誤差乘以一個比例常數(shù)來產(chǎn)生輸出,該輸出與誤差成正比。比例控制的作用是使系統(tǒng)更快地響應(yīng)誤差,但可能會導(dǎo)致系統(tǒng)產(chǎn)生超調(diào)或震蕩。
積分(Integral):積分控制是根據(jù)誤差的累積來產(chǎn)生輸出的一部分。積分控制通過將誤差累積起來,并乘以一個積分常數(shù)來產(chǎn)生輸出,該輸出與誤差的積分成正比。積分控制的作用是消除系統(tǒng)的穩(wěn)態(tài)誤差,即系統(tǒng)在長時間內(nèi)無法達(dá)到期望值的情況。
微分(Derivative):微分控制是根據(jù)誤差的變化率來產(chǎn)生輸出的一部分。微分控制通過將誤差的變化率乘以一個微分常數(shù)來產(chǎn)生輸出,該輸出與誤差的微分成正比。微分控制的作用是抑制系統(tǒng)的過沖和震蕩,使系統(tǒng)更加穩(wěn)定。
PID算法通過將這三個部分的輸出相加,得到最終的控制輸出。每個部分的權(quán)重可以通過調(diào)整相應(yīng)的常數(shù)來控制。PID算法的目標(biāo)是使系統(tǒng)的輸出盡可能接近期望值,并在系統(tǒng)受到擾動時能夠快速恢復(fù)到期望狀態(tài)。
PID算法廣泛應(yīng)用于工業(yè)控制、自動化系統(tǒng)、機(jī)器人控制、溫度控制等領(lǐng)域。它是一種簡單而有效的控制算法,可以根據(jù)具體的系統(tǒng)和需求進(jìn)行調(diào)整和優(yōu)化。
PID參數(shù)作用
P參數(shù)控制器的輸出是與偏差(誤差)成比例的,即控制器輸出隨著系統(tǒng)的偏差增加而增加。P參數(shù)的作用是限制系統(tǒng)的上升時間和穩(wěn)定性,但過大的P值會導(dǎo)致震蕩和不穩(wěn)定的轉(zhuǎn)移函數(shù)。
I參數(shù)控制器的輸出是與偏差的積分成比例的,即控制器輸出隨著時間的累積而增加。
I參數(shù)的作用是消除系統(tǒng)的靜態(tài)誤差,即系統(tǒng)的偏差將在時間推移中逐漸消失,但過大的I值會導(dǎo)致超調(diào)和系統(tǒng)不穩(wěn)定。
D參數(shù)控制器的輸出是偏差的微分與時間成比例的,即控制器輸出隨著偏差的變化率的增加而增加。作用是降低系統(tǒng)的超調(diào)和減少震蕩,但過大的D值可能導(dǎo)致噪聲的放大或沒有響應(yīng)。
三個參數(shù)的綜合作用是控制系統(tǒng)的響應(yīng)速度(上升時間)、穩(wěn)定性和精度。 調(diào)整PID控制器的參數(shù)可以幫助控制系統(tǒng)達(dá)到更高的響應(yīng)速度和精度,同時保持系統(tǒng)的穩(wěn)定性。通常,通過試驗(yàn)和調(diào)整這些參數(shù),可以根據(jù)控制系統(tǒng)需求得到最佳的控制響應(yīng)。
簡單來說就是:
- P <—> 比例控制<—>對當(dāng)前狀態(tài)的處理<—>提高響應(yīng)速度,過大則無靜差
- I <—> 微分控制<—>對過去狀態(tài)的處理<—>用于減小靜差
- D <—> 積分控制<—>對將來狀態(tài)的預(yù)測<—>用于抑制震蕩
位置式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)對應(yīng)的是執(zhí)行機(jī)構(gòu)的實(shí)際位置,一旦控制輸出出錯(控制對象的當(dāng)前的狀態(tài)值出現(xiàn)問題 ),u(k)的大幅變化會引起系統(tǒng)的大幅變化
并且位置式PID在積分項(xiàng)達(dá)到飽和時,誤差仍然會在積分作用下繼續(xù)累積,一旦誤差開始反向變化,系統(tǒng)需要一定時間從飽和區(qū)退出,所以在u(k)達(dá)到最大和最小時,要停止積分作用,并且要有積分限幅和輸出限幅
所以在使用位置式PID時,一般我們直接使用PD控制,而位置式 PID 適用于執(zhí)行機(jī)構(gòu)不帶積分部件的對象,如舵機(jī)和平衡小車的直立和溫控系統(tǒng)的控制
增量式PID
增量式PID(Incremental PID)是PID控制算法的一種變體,與傳統(tǒng)的位置式PID(Positional PID)相對應(yīng)。增量式PID算法通過計(jì)算當(dāng)前時刻的控制量與上一時刻的控制量之差,來得到增量控制量,從而實(shí)現(xiàn)對系統(tǒng)的控制。
在增量式PID中,控制器的輸出是一個增量值,而不是一個絕對值。增量控制量表示了控制器輸出的變化量,可以直接應(yīng)用于系統(tǒng)中,而無需考慮系統(tǒng)的初始狀態(tài)。
增量式PID相對于位置式PID的優(yōu)點(diǎn)是:
不受系統(tǒng)初始狀態(tài)的影響:增量式PID只關(guān)注控制量的變化,而不需要考慮系統(tǒng)的初始狀態(tài)。這使得增量式PID在系統(tǒng)啟動時更加穩(wěn)定。減少積分飽和問題:位置式PID中的積分項(xiàng)可能會導(dǎo)致積分飽和問題,而增量式PID通過增量控制量的計(jì)算,可以減少積分飽和的發(fā)生。
然而,增量式PID也存在一些限制和注意事項(xiàng):
對控制器的輸出限制要求較高:增量式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))
參數(shù)整定口訣
參數(shù)整定尋最佳,從大到小順次查。
先是比例后積分,最后再把微分加。
曲線振蕩很頻繁,比例度盤要放大。
曲線漂浮繞大彎,比例度盤往小扳。
曲線偏離回復(fù)慢,積分時間往下降。
曲線波動周期長,積分時間再加長。
理想曲線兩個波,調(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溫控算法的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解Python的迭代器、生成器以及相關(guān)的itertools包
這篇文章主要介紹了詳解Python的迭代器、生成器以及相關(guān)的itertools包,Iterators、Generators是Python的高級特性,亦是Python學(xué)習(xí)當(dāng)中必會的基本知識,需要的朋友可以參考下2015-04-04python unichr函數(shù)知識點(diǎn)總結(jié)
在本篇文章里小編給大家整理的是一篇關(guān)于python unichr函數(shù)的知識點(diǎn)總結(jié)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2020-12-12Python中實(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來檢測文件的變化等,實(shí)現(xiàn)起來充分體現(xiàn)了Python作為動態(tài)語言的靈活性,強(qiáng)烈推薦!需要的朋友可以參考下2015-04-04使用Python操作Elasticsearch數(shù)據(jù)索引的教程
這篇文章主要介紹了使用Python操作Elasticsearch數(shù)據(jù)索引的教程,Elasticsearch處理數(shù)據(jù)索引非常高效,要的朋友可以參考下2015-04-04Python數(shù)據(jù)處理的三個實(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ì),對大家學(xué)習(xí)或者使用Python3具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05python算法與數(shù)據(jù)結(jié)構(gòu)之單鏈表的實(shí)現(xiàn)代碼
鏈表是一種物理存儲單元上非連續(xù)、非順序的存儲結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接次序?qū)崿F(xiàn)的。這篇文章主要介紹了python算法與數(shù)據(jù)結(jié)構(gòu)之單鏈表的實(shí)現(xiàn)代碼,需要的朋友可以參考下2019-06-06