《優(yōu)秀的IC/FPGA開源項(xiàng)目》是新開的系列,旨在介紹單一項(xiàng)目,會(huì)比《優(yōu)秀的 Verilog/FPGA開源項(xiàng)目》內(nèi)容介紹更加詳細(xì),包括但不限于綜合、上板測試等。兩者相輔相成,互補(bǔ)互充~
作為一名工程師,在項(xiàng)目實(shí)施階段多多少少會(huì)遇到需要使用控制理論的應(yīng)用程序。
一種非常常用的算法是比例積分微分控制器(proportional-integral-derivative control)或 PID 控制器。PID 算法用于控制各種應(yīng)用中溫度、壓力、電機(jī)位置和流量等變量。我經(jīng)常看到的一個(gè)地方是高端圖像處理系統(tǒng)(制冷型紅外),為了減少圖像中的噪點(diǎn)。它使用熱電冷卻器或其他冷卻系統(tǒng)來冷卻圖像傳感器。對(duì)于高端成像,較低的噪聲可以帶來更好的圖像。
介紹
PID 控制算法實(shí)現(xiàn)起來并不難,因?yàn)樗恍枰臃ā⒊朔ā⒊ê蜏p法(dog)。但是,一旦算法實(shí)施,確保 PID 回路穩(wěn)定的三個(gè)系數(shù)可能需要一點(diǎn)額外的時(shí)間來獲取。
PID 主要使用三個(gè)術(shù)語。
比例(Proportional) -測量期望值和測量值之間的差異。比例值是當(dāng)前位置的量度。
積分(Integral) -會(huì)隨著時(shí)間的推移對(duì)誤差進(jìn)行積分。積分項(xiàng)是誤差的歷史累積值。隨著誤差的消除,積分項(xiàng)停止增長。
導(dǎo)數(shù)(Derivative) -計(jì)算變化率并預(yù)測誤差的未來趨勢。
每個(gè)術(shù)語還具有相關(guān)的增益 KI、KP 或 KD,可以幫助我們調(diào)整 PID 控制器算法的行為。D 項(xiàng)不是必須的,而且簡單的情況下我們基本不使用,使用 PI 控制器也很常見。
PID經(jīng)常使用浮點(diǎn)數(shù)來實(shí)現(xiàn)。因此,我們可以使用諸如 VHDL Fixed/Float 之類的庫在 RTL 中實(shí)現(xiàn)。或者,我們可以使用HLS來實(shí)現(xiàn) PID,因?yàn)閲鴥?nèi)應(yīng)用VHDL較少,所以我們今天的實(shí)例是使用HLS構(gòu)建我們的PID算法。使用HLS能夠使用浮點(diǎn)或任意精度的定點(diǎn)數(shù)。HLS還能通過#pragma 快速的為IP添加通用控制接口(AXI)。
在純 FPGA 實(shí)現(xiàn)類似系統(tǒng)時(shí)候,我們需要添加軟核來控制IP。在較小的 Zynq-7000 SoC FPGA(7007、7010、7020 等)中則可以通過硬核控制IP。或者,如果我們設(shè)計(jì)中不想使用處理器,那我們可以設(shè)計(jì)傳統(tǒng)的矢量接口即可。
源碼設(shè)計(jì)
PID 的實(shí)際源代碼非常簡單,如下所示。
#include"pid.h" staticdata_typeerror_prev=0; staticdata_typei_prev=0; data_typePID(data_typeset_point,data_typeKP,data_typeKI,data_typeKD,data_typesample,data_typets,data_typepmax) { #pragmaHLSINTERFACEmode=s_axiliteport=return #pragmaHLSINTERFACEmode=s_axiliteport=sample #pragmaHLSINTERFACEmode=s_axiliteport=KD #pragmaHLSINTERFACEmode=s_axiliteport=KI #pragmaHLSINTERFACEmode=s_axiliteport=KP #pragmaHLSINTERFACEmode=s_axiliteport=set_point #pragmaHLSINTERFACEmode=s_axiliteport=ts #pragmaHLSINTERFACEmode=s_axiliteport=pmax data_typeerror,i,d,p; data_typetemp; data_typeop; error=set_point-sample; p=error*KP; i=i_prev+(error*ts*KI); d=KD*((error-error_prev)/ts); op=p+i+d; error_prev=error; if(op>pmax){ i_prev=i_prev; op=pmax; }else{ i_prev=i; } returnop; }
已將previous error和previous integral聲明為全局靜態(tài)變量,以確保它們在迭代時(shí)候其值保持不變。
在算法方面,用戶可以在應(yīng)用程序運(yùn)行時(shí)動(dòng)態(tài)加載 KP、KI、KID、Ts 和 Pmax。我們可以輕松地添加積分值或使用附加寄存器重新啟動(dòng)控制器。這將使 PID 可以用于多個(gè)實(shí)現(xiàn)。
為了測試和配置 PID,測試文件羅列了一系列溫度值,這些溫度都遠(yuǎn)高于預(yù)期的目標(biāo)設(shè)定點(diǎn),并確保達(dá)到設(shè)定點(diǎn)。此示例中的 PID 設(shè)計(jì)用于提供功率(以瓦特為單位)維持光學(xué)床的溫度。在這種情況下,我們需要加熱而不是降低溫度。
#include"pid.h" #include#defineiterations40 intmain(void) { data_typeset_point=-80.0; data_typesample[iterations]={-90.000,-88.988,-87.977,-86.966,-85.955,-84.946,-83.936,-82.928,-81.920,-80.912,-80.283,-79.926,-79.784,-79.774,-79.829,-79.898,-79.955,-79.993,-80.011,-80.017,-80.016,-80.010,-80.005,-80.002,-80.000,-79.999,-79.999,-79.999,-79.999,-80.000,-80.000,-80.000,-80.000,-80.000,-80.000,-80.000,-79.999,-80.000,-80.001,-80.000}; data_typekp=19.6827;//w/k data_typeki=0.7420;//w/k/s data_typekd=0.0; data_typeop; printf("testingcpp "); for(inti=0;i
在 Vitis HLS 中針對(duì)該 PID 算法進(jìn)行C 仿真和協(xié)同仿真,結(jié)果完全符合預(yù)期。
算法按照預(yù)期運(yùn)行,下一步是綜合和導(dǎo)出 IP,最后就是添加到我們的 Vivado 項(xiàng)目中。這次我們使用的是ZYNQ FPGA。
延遲性能和資源消耗
下面的完整框圖反映了添加到Vivado項(xiàng)目中情況。
框圖 總設(shè)計(jì)資源 PID 資源
構(gòu)建完成上面的Vivado項(xiàng)目,接下來就是導(dǎo)出硬件(XSA)到 Vitis 中開發(fā)驅(qū)動(dòng)。
在 Vitis 中開發(fā)驅(qū)動(dòng)時(shí)候,我重用了 HLS 仿真文件中的幾個(gè)元素。
由于我們使用的是 AXI 接口,Vitis HLS 在導(dǎo)出IP時(shí)候地為我們提供了一個(gè)可以在 Vitis 中用于驅(qū)動(dòng) IP 核的驅(qū)動(dòng)程序。但是,當(dāng)在 IP 內(nèi)核中使用浮點(diǎn)輸入時(shí),驅(qū)動(dòng)程序則期望它們?yōu)?U32。如果我們在開發(fā)驅(qū)動(dòng)時(shí)候從浮點(diǎn)數(shù)轉(zhuǎn)換為 U32,我們將失去準(zhǔn)確性。因此,解決這個(gè)問題的方法是使用指針(pointers)和強(qiáng)制轉(zhuǎn)換。
本質(zhì)上,我們將變量聲明為浮點(diǎn)數(shù),然后在函數(shù)中調(diào)用設(shè)置一個(gè)指向浮點(diǎn)變量地址的 U32 指針,并使用間接運(yùn)算符讀取該值。
XPid_Set_set_point(&pid,*((u32*)&set_point));
整個(gè)應(yīng)用程序是
#include#include"platform.h" #include"xil_printf.h" #include"xpid.h" #defineiterations40 typedeffloatdata_type; data_typeset_point=-80.0; data_typesample[iterations]={-90.000,-88.988,-87.977,-86.966,-85.955,-84.946,-83.936,-82.928,-81.920,-80.912,-80.283,-79.926,-79.784,-79.774,-79.829,-79.898,-79.955,-79.993,-80.011,-80.017,-80.016,-80.010,-80.005,-80.002,-80.000,-79.999,-79.999,-79.999,-79.999,-80.000,-80.000,-80.000,-80.000,-80.000,-80.000,-80.000,-79.999,-80.000,-80.001,-80.000}; data_typekp=19.6827;//w/k data_typeki=0.7420;//w/k/s data_typekd=0.0; data_typets=12.5; data_typepmax=40; u32op; XPidpid; intmain() { floatresult; init_platform(); disable_caches(); print("AdiuvoPIDExample "); XPid_Initialize(&pid,XPAR_XPID_0_DEVICE_ID); XPid_Set_set_point(&pid,*((u32*)&set_point)); XPid_Set_KP(&pid,*((u32*)&kp)); XPid_Set_KI(&pid,*((u32*)&ki)); XPid_Set_KD(&pid,*((u32*)&kd)); XPid_Set_ts(&pid,*((u32*)&ts)); XPid_Set_pmax(&pid,*((u32*)&pmax)); u32tst=XPid_Get_set_point(&pid); for(inti=0;i
運(yùn)行,得到以下結(jié)果。
正如預(yù)期的那樣,硬件中的實(shí)現(xiàn)與軟件的工作方式相同。
當(dāng)然,對(duì)于不同的應(yīng)用程序,我們需要重新確定可用于應(yīng)用程序的 KP、KI 和 KD 變量。
這樣做的真正美妙之處在于,因?yàn)樗怯?C 實(shí)現(xiàn)的,可維護(hù)性高,可以快速構(gòu)建一個(gè)我們需要的PID算法。
總結(jié)
雖然上面的流程很簡單,但是HLS在調(diào)整資源和速度方面還是需要一些時(shí)間,并且浪費(fèi)的資源還是比純HDL多。
最后在說一下該方式的缺點(diǎn),PID需要進(jìn)行浮點(diǎn)運(yùn)算,而FPGA則不能進(jìn)行浮點(diǎn)運(yùn)算,如果想把上面的算法在邏輯中運(yùn)行,則需要自己進(jìn)行量化,但是如果像上面例程的方式在內(nèi)核(硬核)中運(yùn)行算法,則該方式簡單且優(yōu)雅~
審核編輯:彭靜
-
控制器
+關(guān)注
關(guān)注
112文章
16361瀏覽量
178030 -
PID算法
+關(guān)注
關(guān)注
2文章
172瀏覽量
24311 -
HLS
+關(guān)注
關(guān)注
1文章
129瀏覽量
24113
原文標(biāo)題:在 FPGA 上快速構(gòu)建 PID 算法
文章出處:【微信號(hào):Open_FPGA,微信公眾號(hào):OpenFPGA】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評(píng)論請先 登錄
相關(guān)推薦
評(píng)論