護(hù)調(diào)控系統(tǒng):閉環(huán)控制與仿真實(shí)現(xiàn))
說到底輸液監(jiān)護(hù)這活兒很多年以前沒有被“智能化”的時(shí)候全靠護(hù)士或者家屬盯瓶。滴速靠手動(dòng)推滾輪患者翻個(gè)身可能流速就變了藥液快空還得時(shí)刻留意冬天手冷調(diào)滾輪也不太靈敏。這也是我做這套STM32智能輸液監(jiān)護(hù)調(diào)控系統(tǒng)升級(jí)版的初衷——利用單片機(jī)完成滴速的實(shí)時(shí)檢測(cè)、閉環(huán)調(diào)節(jié)、異常報(bào)警順便把整個(gè)項(xiàng)目開源代碼、原理圖、仿真一條龍全給到。這套系統(tǒng)對(duì)正在學(xué)STM32、想做課程設(shè)計(jì)或電子競(jìng)賽題目的朋友尤其是奔著“嵌入式醫(yī)療電子”方向去的讀者非常值得參考。這個(gè)項(xiàng)目選擇STM32F103C8T6做主控屬于性價(jià)比很高的經(jīng)典芯片網(wǎng)上資料多、調(diào)試工具便宜踩坑之后能找到的參考也最多。整套系統(tǒng)的定位很明確輸液過程自動(dòng)監(jiān)護(hù)能測(cè)滴速、能按設(shè)定值自動(dòng)調(diào)節(jié)、能低液位報(bào)警如果液體走空或者滴速異常還能鎖止管路。下面我就把整個(gè)項(xiàng)目的設(shè)計(jì)思路、原理圖要點(diǎn)、傳感檢測(cè)原理、核心算法、仿真聯(lián)調(diào)和實(shí)際排坑過程完完整整地拆開講清楚。1. 智能輸液監(jiān)護(hù)調(diào)控系統(tǒng)的整體架構(gòu)與設(shè)計(jì)思路1.1 系統(tǒng)要解決什么問題傳統(tǒng)輸液的兩個(gè)痛點(diǎn)很突出一是滴速調(diào)節(jié)完全靠手動(dòng)滾輪一旦設(shè)定好很難保證長(zhǎng)時(shí)間穩(wěn)定。患者稍微移動(dòng)手臂或者針頭位置受壓滴速就會(huì)明顯變化結(jié)果就是藥液滴速忽快忽慢有些藥物對(duì)滴速又有嚴(yán)格要求快了可能引發(fā)不適慢了影響治療效果。二是監(jiān)護(hù)成本高護(hù)士不可能全天守在床邊家屬盯著吊瓶也容易疲勞尤其是夜里。液體走空之后如果沒能及時(shí)處理回血、凝血、空氣進(jìn)入管路等問題都是潛在風(fēng)險(xiǎn)。為了解決這些問題這套系統(tǒng)做了幾個(gè)核心升級(jí)滴速閉環(huán)控制不再只是“檢測(cè)并顯示”而是根據(jù)當(dāng)前實(shí)測(cè)滴速與設(shè)定滴速的偏差通過PID算法調(diào)節(jié)蠕動(dòng)泵轉(zhuǎn)速讓滴速自動(dòng)穩(wěn)定在設(shè)定范圍。傳感防護(hù)升級(jí)在原有紅外對(duì)管滴速檢測(cè)的基礎(chǔ)上增加了液位檢測(cè)和氣泡檢測(cè)邏輯多重保障異常情況。報(bào)警與鎖止機(jī)制滴速偏差持續(xù)超出閾值、液位過低、連續(xù)氣泡時(shí)系統(tǒng)會(huì)聲光報(bào)警并停止電機(jī)避免進(jìn)一步風(fēng)險(xiǎn)。全鏈路開源原理圖、STM32工程代碼、Proteus仿真工程全部打包方便不同基礎(chǔ)的讀者直接復(fù)現(xiàn)。從實(shí)際應(yīng)用場(chǎng)景看這套系統(tǒng)更偏向“教學(xué)演示樣機(jī)驗(yàn)證”我在設(shè)計(jì)時(shí)也特意控制了成本整套BOM不超過百元。需要強(qiáng)調(diào)的是它適合作為實(shí)驗(yàn)室研究和工程訓(xùn)練項(xiàng)目不等于商用醫(yī)療設(shè)備要實(shí)際進(jìn)臨床還得過嚴(yán)格的醫(yī)療認(rèn)證和合規(guī)流程。1.2 模塊劃分與選型邏輯整個(gè)系統(tǒng)可以劃分成幾個(gè)清晰的功能模塊模塊具體方案作用選型理由主控STM32F103C8T6信號(hào)采集、算法運(yùn)算、控制輸出Flash 64KB、RAM 20KB外設(shè)豐富開發(fā)資料多滴速檢測(cè)紅外對(duì)管 LM393比較器將滴液脈沖轉(zhuǎn)成方波送給單片機(jī)成本低、電路簡(jiǎn)單適合教學(xué)演示液位檢測(cè)光電折反射傳感器檢測(cè)藥液液位是否過低非接觸式不改動(dòng)輸液管路執(zhí)行機(jī)構(gòu)12V蠕動(dòng)泵 MOS驅(qū)動(dòng)按PWM占空比調(diào)節(jié)輸液速度泵管不接觸藥液無菌性好人機(jī)交互LCD1602 4個(gè)按鍵顯示滴速/設(shè)定值輸入操作顯示直觀、成本低、驅(qū)動(dòng)簡(jiǎn)單報(bào)警有源蜂鳴器異常狀態(tài)聲光提示直接IO控制無需額外驅(qū)動(dòng)電路電源USB 5V AMS1117-3.3為主控和傳感器供電簡(jiǎn)潔、容易獲得12V泵獨(dú)立供電這里重點(diǎn)說下為什么選STM32F103C8T6。很多入門者會(huì)覺得做一個(gè)輸液監(jiān)控是不是用51單片機(jī)就夠從“能完成”的角度看確實(shí)夠但這個(gè)項(xiàng)目的目標(biāo)不只是完成功能而是要把“閉環(huán)調(diào)節(jié)”做好。閉環(huán)調(diào)節(jié)需要定時(shí)器做PWM輸出需要外部中斷做滴速脈沖計(jì)數(shù)需要ADC讀取傳感器模擬量還需要在中斷和主循環(huán)之間合理安排實(shí)時(shí)任務(wù)。STM32的TIM、EXTI、ADC、USART、I2C這些資源都很充裕而且后續(xù)想擴(kuò)展WiFi聯(lián)網(wǎng)、藍(lán)牙傳輸都非常順手。關(guān)鍵是這個(gè)芯片在Proteus里有仿真模型這就意味著哪怕手頭沒有實(shí)物硬件也能先把算法和邏輯跑通。電機(jī)執(zhí)行機(jī)構(gòu)我用了蠕動(dòng)泵而不是步進(jìn)電機(jī)加注射器方案。原因有兩方面第一蠕動(dòng)泵的泵管是獨(dú)立的藥液只在泵管里流動(dòng)泵體機(jī)械結(jié)構(gòu)不接觸液體從原理上就更符合輸液的衛(wèi)生要求第二步進(jìn)電機(jī)控制注射器需要自己搭建復(fù)雜的進(jìn)給機(jī)構(gòu)行程有限重復(fù)精度和管路匹配都比較難做好對(duì)開源項(xiàng)目來說門檻一下子抬高了。蠕動(dòng)泵就簡(jiǎn)單很多給PWM就轉(zhuǎn)轉(zhuǎn)速和流量近似成線性關(guān)系拿來演示閉環(huán)控制非常直觀。2. 原理圖設(shè)計(jì)從最小系統(tǒng)到驅(qū)動(dòng)電路的關(guān)鍵細(xì)節(jié)2.1 主控最小系統(tǒng)與晶振電容計(jì)算STM32F103C8T6的64KB Flash版本最小系統(tǒng)核心包括電源去耦、復(fù)位、啟動(dòng)模式選擇和外部晶振。不要小看這些“基礎(chǔ)電路”很多項(xiàng)目的隨機(jī)故障都出在這里。電源部分系統(tǒng)輸入使用USB 5V經(jīng)過AMS1117-3.3穩(wěn)壓到3.3V給單片機(jī)。在VDDA引腳與VDD之間我串了一個(gè)10Ω電阻再加一個(gè)1uF電容到地這樣能起到一定的濾波隔離作用降低數(shù)字電路噪聲對(duì)內(nèi)部模擬電路的影響。每一個(gè)電源引腳旁邊都放了0.1uF的高頻去耦電容電容必須盡量靠近芯片電源腳否則去耦效果會(huì)打折扣。這個(gè)大原則適用于板上所有芯片不要嫌電容多。復(fù)位電路用的是經(jīng)典的10K上拉電阻并聯(lián)0.1uF電容到地。上電瞬間電容充電NRST保持低電平單片機(jī)復(fù)位充電完成后NRST被拉高到3.3V芯片開始運(yùn)行。如果設(shè)計(jì)時(shí)把復(fù)位腳當(dāng)普通IO用或者把電容省了會(huì)很容易發(fā)生上電復(fù)位不徹底的現(xiàn)象。晶振電路值得單獨(dú)講一下。外部8MHz無源晶振配合兩個(gè)22pF負(fù)載電容。很多人直接把相同容值的電容往上一焊也不知道為什么選這個(gè)值。其實(shí)這個(gè)值要按晶振負(fù)載電容來計(jì)算CL (C1 × C2) / (C1 C2) Cstray。Cstray是引腳寄生電容和PCB走線寄生電容一般取3~5pF。如果用兩個(gè)22pF電容那CL (22 × 22) / (22 22) 4 15pF正好落在常見8MHz晶振負(fù)載電容10~20pF的范圍內(nèi)。這個(gè)參數(shù)決定了晶振實(shí)際振蕩頻率是否精確對(duì)后面串口波特率、定時(shí)器計(jì)時(shí)精度都有影響。啟動(dòng)模式這里要特別提醒BOOT0必須通過10K電阻下拉到GNDBOOT1同樣下拉或直接接地。這樣保證芯片從上電開始就執(zhí)行Flash中的用戶程序。如果BOOT0懸空或拉高STM32會(huì)進(jìn)入系統(tǒng)存儲(chǔ)器引導(dǎo)模式表現(xiàn)為程序燒進(jìn)去不跑非常坑。我以前在面包板上搭電路就遇到過這個(gè)問題后來固定用排針做成跳線帽狀態(tài)一目了然。晶振下方不要走其他信號(hào)線否則高頻振蕩信號(hào)容易干擾相鄰走線。如果是雙面板晶振區(qū)域地在頂層可以挖空或者鋪地隔離底層不要有其他敏感信號(hào)穿過。2.2 滴速檢測(cè)與電機(jī)驅(qū)動(dòng)電路滴速檢測(cè)電路是這套系統(tǒng)的關(guān)鍵輸入。紅外發(fā)射管和接收管以對(duì)射方式安裝在輸液滴壺兩側(cè)水滴落下時(shí)會(huì)遮擋紅外光路接收管輸出電流產(chǎn)生變化。這個(gè)變化要先轉(zhuǎn)換成電壓再用比較器整形才能給單片機(jī)識(shí)別。發(fā)射管限流電阻一般取1K到1.5K具體數(shù)值取決于紅外管的額定工作電流。以典型紅外LED額定20mA左右計(jì)算如果工作電壓3.3V限流電阻R (3.3 - VF) / I紅外管正向壓降一般在1.2V~1.5V取20mA時(shí)R大約在90Ω~105Ω。但我實(shí)際試過用1K電阻把電流限制在2mA左右配合高增益接收管和比較器照樣能穩(wěn)定檢測(cè)而且還能降低功耗。這里需要根據(jù)實(shí)際傳感器靈敏度調(diào)不要死背公式電路要在暗環(huán)境測(cè)試一下余量。接收管的輸出電壓是一個(gè)微弱且緩慢變化的信號(hào)直接接單片機(jī)IO不行我用了LM393比較器構(gòu)成滯回比較器。參考電壓可以用電位器分壓得到滯回量通過正反饋電阻調(diào)整。這樣輸出就是干凈的數(shù)字方波直接送給PA0作為外部中斷輸入。沒有滯回的話水滴遮光瞬間信號(hào)可能在閾值附近反復(fù)抖動(dòng)導(dǎo)致中斷連續(xù)觸發(fā)好幾次滴速測(cè)量自然就不準(zhǔn)。電機(jī)驅(qū)動(dòng)部分12V蠕動(dòng)泵的電流大約在300~800mA之間這已經(jīng)遠(yuǎn)超單片機(jī)IO口驅(qū)動(dòng)能力了。我用的是N溝道MOS管AO3400做低邊開關(guān)PWM信號(hào)通過一個(gè)10K電阻下拉到GND接到MOS管柵極。電機(jī)一端接12V另一端接MOS漏極源極接地。這個(gè)方案功耗比L298N驅(qū)動(dòng)模塊低很多成本也低。注意電機(jī)兩端必須并聯(lián)一個(gè)續(xù)流二極管一般用1N5819或SS34方向是負(fù)極接12V正極接電機(jī)與MOS的連接點(diǎn)。如果沒有這個(gè)二極管電機(jī)感性負(fù)載在PWM關(guān)斷瞬間會(huì)產(chǎn)生很高的反向電動(dòng)勢(shì)輕則影響驅(qū)動(dòng)波形重則直接擊穿MOS管。PWM頻率也要選好。我的經(jīng)驗(yàn)是用10kHz到20kHz太低電機(jī)會(huì)發(fā)出明顯的嘯叫聲太高又增加了MOS管的開關(guān)損耗。實(shí)際測(cè)試中蠕動(dòng)泵對(duì)PWM頻率并不太敏感我最后固定在20kHz聽不到噪聲控制也平穩(wěn)。整個(gè)系統(tǒng)的供電要注意12V蠕動(dòng)泵電源和3.3V控制電路電源必須共地否則MOS管無法正確開關(guān)但12V回路上要盡量遠(yuǎn)離信號(hào)線避免泵的啟停在電源地上產(chǎn)生毛刺干擾滴速脈沖計(jì)數(shù)。3. 傳感檢測(cè)原理與信號(hào)處理3.1 紅外對(duì)管滴速檢測(cè)原理說到滴速檢測(cè)最直觀的想法就是“數(shù)水滴”。但真正把“水滴滴落”這件事變成單片機(jī)認(rèn)識(shí)的脈沖信號(hào)中間還有幾層信號(hào)處理要講清楚。紅外對(duì)管的基本工作方式有兩種一種是對(duì)射式發(fā)射管和接收管面對(duì)面安裝水滴從中間穿過另一種是反射式發(fā)射管和接收管同側(cè)通過水滴表面的反射光變化來判斷。我對(duì)射式用得最多因?yàn)樵碜钪苯?、抗環(huán)境光干擾能力更強(qiáng)。對(duì)射式安裝時(shí)紅外發(fā)射管持續(xù)發(fā)光接收管在沒有水滴遮擋時(shí)接收到較強(qiáng)光信號(hào)輸出低阻抗?fàn)顟B(tài)。當(dāng)一滴水從滴壺中落下并經(jīng)過光路時(shí)光線被水珠遮擋和折射接收管接收到的光強(qiáng)瞬間下降輸出狀態(tài)改變。這個(gè)改變被LM393滯回比較器捕捉后輸出一個(gè)下降沿脈沖。單片機(jī)外部中斷檢測(cè)到這個(gè)下降沿就認(rèn)為“滴了一滴”。實(shí)際波形遠(yuǎn)沒有教科書那么理想。水滴不是純黑體它落下時(shí)會(huì)在光路里產(chǎn)生折射、散射導(dǎo)致接收信號(hào)不是干脆的“有光/無光”兩態(tài)而是有一個(gè)漸變過程。如果環(huán)境光比較強(qiáng)信號(hào)基線還會(huì)上下漂移。這也是為什么一定要用比較器整形而不是直接把接收管輸出接單片機(jī)。我用滯回比較器的另一個(gè)好處是一旦信號(hào)超過高閾值輸出翻轉(zhuǎn)只有信號(hào)回落到低閾值以下輸出才復(fù)位這中間的回差就消除了臨界區(qū)域的抖動(dòng)。軟件上還有一個(gè)細(xì)節(jié)就是滴速計(jì)算的窗口不要用“固定時(shí)間內(nèi)計(jì)數(shù)”的方式而是用“相鄰兩個(gè)下降沿之間的時(shí)間間隔”。原因很直接固定時(shí)間計(jì)數(shù)在低速時(shí)誤差特別大比如設(shè)定20滴/分鐘你要數(shù)滿60秒才能得到穩(wěn)定讀數(shù)實(shí)時(shí)性太差。間隔法在每一滴落下來之后馬上就能刷新瞬時(shí)滴速配合滑動(dòng)平均濾波既能快速響應(yīng)又不會(huì)跳動(dòng)太厲害。3.2 液位與氣泡檢測(cè)的實(shí)現(xiàn)方案低液位檢測(cè)是輸液監(jiān)護(hù)的重要安全功能但很多入門項(xiàng)目會(huì)忽略它。我這里提供兩種低成本方案大家可以根據(jù)手頭條件選擇。第一種是光電折反射式。在輸液瓶或輸液袋的瓶頸位置也就是液位下限對(duì)應(yīng)的高度貼一個(gè)紅外反射式傳感器。發(fā)射管發(fā)出紅外光如果這里還是液體光在液體內(nèi)部傳播路徑發(fā)生改變接收管收到的信號(hào)特征明顯當(dāng)液位下降到傳感器位置以下傳感器下面是空氣光的折射和反射特性完全不同接收管信號(hào)發(fā)生跳變。這種方案的好處是完全非接觸不需要在輸液管路里插入任何東西。第二種是浮子干簧管方案。把一個(gè)小磁環(huán)浮子放在輸液瓶口附近的浮標(biāo)腔內(nèi)當(dāng)液面下降到設(shè)定位置浮子落下干簧管閉合或斷開產(chǎn)生一個(gè)開關(guān)信號(hào)。這個(gè)方案成本更低邏輯也更簡(jiǎn)單因?yàn)檩敵鲋苯泳褪菙?shù)字量不需要信號(hào)調(diào)理電路。缺點(diǎn)是浮子結(jié)構(gòu)要額外做一個(gè)小腔體對(duì)于演示項(xiàng)目來說加工起來稍微麻煩。氣泡檢測(cè)用到了超聲檢測(cè)的原理。普通輸液管如果出現(xiàn)連續(xù)氣泡很容易導(dǎo)致空氣進(jìn)入靜脈風(fēng)險(xiǎn)很高。我在這版里預(yù)留了氣泡檢測(cè)接口使用一個(gè)超聲發(fā)射/接收對(duì)管貼在輸液管兩側(cè)。超聲波在液體中衰減較小接收端能收到明顯信號(hào)當(dāng)管路里出現(xiàn)氣泡時(shí)超聲在氣液界面大量反射接收信號(hào)強(qiáng)度急劇下降系統(tǒng)據(jù)此判斷有氣泡通過。這個(gè)檢測(cè)方式和液位檢測(cè)一樣屬于非接觸式不影響藥液流動(dòng)。需要提醒的是這些檢測(cè)方案在工程上都很依賴安裝位置和固定方式。傳感器與滴壺、管路的相對(duì)位置一旦變化信號(hào)幅度就會(huì)明顯不同。所以實(shí)際調(diào)試時(shí)一定要留出可調(diào)節(jié)的機(jī)械結(jié)構(gòu)用3D打印或者亞克力支架把傳感器固定好不要用膠帶一次性粘死。4. 核心控制算法與代碼實(shí)現(xiàn)4.1 滴速測(cè)量與PI閉環(huán)控制眨眼之間傳感器把滴速變成了脈沖流接下來就是程序的主場(chǎng)了。滴速測(cè)量的核心代碼邏輯我用的是外部中斷加定時(shí)器計(jì)時(shí)。物體從滴壺落下遮斷光路產(chǎn)生下降沿觸發(fā)EXTI中斷。中斷服務(wù)程序里我記錄定時(shí)器當(dāng)前計(jì)數(shù)值再減去上一次觸發(fā)時(shí)的計(jì)數(shù)值就得到了相鄰兩滴的時(shí)間間隔T。瞬時(shí)滴速speed 60000 / T單位是滴/分鐘。之所以是60000是因?yàn)?分鐘等于60000毫秒而T的計(jì)時(shí)單位是毫秒。這里有一個(gè)細(xì)節(jié)定時(shí)器是16位的需要處理溢出。我用TIM2做計(jì)時(shí)器預(yù)分頻設(shè)為72-1也就是1MHz計(jì)數(shù)頻率計(jì)數(shù)器最大計(jì)數(shù)值65535。兩個(gè)滴液間隔如果超過65.535秒說明滴速低于每分鐘1滴這種情況下可以認(rèn)為輸液管基本堵死或滴壺空了直接觸發(fā)報(bào)警。工程代碼里對(duì)計(jì)數(shù)器溢出做了檢測(cè)而不是讓整個(gè)程序跟著溢出轉(zhuǎn)否則低速時(shí)測(cè)出來的數(shù)據(jù)會(huì)突然變成幾千非常離譜。為了抑制連續(xù)測(cè)量值的抖動(dòng)我維護(hù)了一個(gè)長(zhǎng)度為5的滑動(dòng)平均緩沖數(shù)組新數(shù)據(jù)進(jìn)來覆蓋舊數(shù)據(jù)輸出取平均。這個(gè)處理比單純用瞬時(shí)值平滑得多又比長(zhǎng)窗口濾波響應(yīng)快。PI控制用的就是經(jīng)過濾波后的滴速。PI控制的初衷是讓系統(tǒng)自動(dòng)逼近設(shè)定滴速。誤差e(k) 設(shè)定值 ? 實(shí)際值P項(xiàng)負(fù)責(zé)快速響應(yīng)I項(xiàng)負(fù)責(zé)消除穩(wěn)態(tài)誤差。代碼實(shí)現(xiàn)最關(guān)鍵的兩個(gè)點(diǎn)一是積分限幅二是指定輸出限幅。#define INTEGRAL_LIMIT 200 #define PWM_MIN 0 #define PWM_MAX 999 static int16_t integral, last_out; int16_t speed_pi(uint16_t target_speed, uint16_t actual_speed) { int16_t err (int16_t)target_speed - (int16_t)actual_speed; integral err; if (integral INTEGRAL_LIMIT) integral INTEGRAL_LIMIT; if (integral -INTEGRAL_LIMIT) integral -INTEGRAL_LIMIT; int16_t out (int16_t)(10 * err) integral; if (out PWM_MIN) out PWM_MIN; if (out PWM_MAX) out PWM_MAX; last_out out; return out; }為什么積分要限幅因?yàn)槿鋭?dòng)泵的PWM占空比沒法無限增大如果誤差一直存在積分項(xiàng)會(huì)不斷累加到非常大等誤差反向時(shí)系統(tǒng)要先把這一大坨積分消耗完才能真正反向調(diào)節(jié)表現(xiàn)出來就是大超調(diào)和大幅震蕩。限幅之后積分項(xiàng)最多貢獻(xiàn)一定量的PWM增量系統(tǒng)動(dòng)態(tài)響應(yīng)會(huì)好很多。參數(shù)整定方面我沒有用太復(fù)雜的工具直接在Proteus仿真里先粗調(diào)再在實(shí)物上微調(diào)。P項(xiàng)從小的Kp5開始觀察響應(yīng)是否超調(diào)再逐步加大I項(xiàng)先設(shè)成0等P項(xiàng)能穩(wěn)定跟隨后再逐步加積分強(qiáng)度觀察靜態(tài)誤差的消除速度和超調(diào)量。對(duì)于這套蠕動(dòng)泵系統(tǒng)Kp在8~15、Ki在0.5~2.0范圍內(nèi)表現(xiàn)比較理想。這個(gè)方法就是經(jīng)典試湊法雖然土但非常有效。閉環(huán)之外還有一個(gè)安全邏輯如果實(shí)際滴速持續(xù)5秒低于設(shè)定值70%或者液位檢測(cè)到低液位或者檢測(cè)到連續(xù)5個(gè)氣泡系統(tǒng)立即置報(bào)警標(biāo)志停止電機(jī)輸出并讓蜂鳴器鳴叫。這個(gè)過程不能依賴PI算法自己慢慢調(diào)回來因?yàn)橛行┣闆r比如液位已經(jīng)空了泵再轉(zhuǎn)也沒有意義必須主動(dòng)停。4.2 按鍵菜單與狀態(tài)機(jī)設(shè)計(jì)再好的控制邏輯也得有個(gè)人機(jī)交互外殼。這版系統(tǒng)我用4個(gè)按鍵加LCD1602搭了一個(gè)簡(jiǎn)潔的菜單狀態(tài)機(jī)劃分成待機(jī)、運(yùn)行、設(shè)置、報(bào)警四個(gè)主要狀態(tài)typedef enum { SYS_STANDBY, SYS_RUNNING, SYS_SETTING, SYS_ALARM } sys_state_t;待機(jī)狀態(tài)下按“設(shè)置”鍵進(jìn)入設(shè)置界面通過“加”“減”鍵調(diào)整目標(biāo)滴速按“確認(rèn)”鍵保存并回到待機(jī)。此時(shí)按“啟動(dòng)”鍵系統(tǒng)進(jìn)入運(yùn)行狀態(tài)電機(jī)啟動(dòng)滴速檢測(cè)和PI控制全部投入。運(yùn)行過程中如果檢測(cè)到異常系統(tǒng)切入報(bào)警狀態(tài)停止電機(jī)蜂鳴器響操作者排除故障后按“確認(rèn)”鍵才能復(fù)位。按鍵處理必須消抖。我用的是10ms軟件延時(shí)消抖在按鍵掃描函數(shù)里檢測(cè)到電平變化后延時(shí)10ms再讀一次確認(rèn)無誤才更新按鍵標(biāo)志。雖然延時(shí)消抖在理論上會(huì)阻塞主循環(huán)10ms但對(duì)這個(gè)量級(jí)的系統(tǒng)影響不大實(shí)現(xiàn)起來最簡(jiǎn)單可靠。注意不要在中斷里做按鍵掃描按鍵邏輯適合放在主循環(huán)中執(zhí)行。主循環(huán)的整體順序是這樣的掃描按鍵更新設(shè)定值讀取傳感器狀態(tài)刷新滴速緩沖區(qū)調(diào)用PI控制器計(jì)算新的PWM占空比更新LCD顯示內(nèi)容檢查報(bào)警條件并執(zhí)行狀態(tài)切換。中斷只負(fù)責(zé)兩件事滴速脈沖的下降沿計(jì)數(shù)和讀取定時(shí)器值、定時(shí)器溢出標(biāo)志維護(hù)。把控制算法放在主循環(huán)而不是中斷里是因?yàn)镻I運(yùn)算涉及浮點(diǎn)和數(shù)組操作放中斷里容易導(dǎo)致響應(yīng)延遲不穩(wěn)定還可能與滴速測(cè)量的實(shí)時(shí)性沖突。代碼工程里我還加了IWDG看門狗喂狗操作放在主循環(huán)末尾。這樣就算程序因?yàn)楦蓴_跑飛看門狗也能在幾百毫秒內(nèi)把系統(tǒng)拉回復(fù)位狀態(tài)保證輸液監(jiān)護(hù)在無人值守時(shí)不會(huì)徹底死機(jī)。5. Proteus仿真聯(lián)調(diào)與數(shù)據(jù)驗(yàn)證5.1 仿真平臺(tái)搭建流程實(shí)物硬件做出來之前先用Proteus把整個(gè)系統(tǒng)驗(yàn)證一遍能省掉大量排查問題的時(shí)間。Proteus對(duì)STM32F103C8T6的支持還算不錯(cuò)仿真模型可以直接拿來用。搭建流程不復(fù)雜新建Proteus工程在選擇元件時(shí)搜索STM32F103C8T6把它放到畫布上。搭建最小系統(tǒng)電路包括VDD、VDDA接3.3VVSS接地NRST接復(fù)位電路OSC_IN和OSC_OUT接晶振和負(fù)載電容。從元件庫(kù)添加LED、按鍵、LCD1602、LM393比較器、電位器等仿真元件按原理圖連線。用信號(hào)發(fā)生器或者脈沖源PULSE / DPULSE正電平寬度幾百毫秒模擬滴速傳感器的輸出接在PA0引腳上這樣就能方便地模擬不同的滴速脈沖頻率。在Keil MDK工程里勾選Option for Target - Output - Create HEX File編譯生成HEX文件?;氐絇roteus雙擊STM32芯片在Program File一欄選擇生成的HEX文件點(diǎn)運(yùn)行。需要注意Proteus里的信號(hào)發(fā)生器參數(shù)和真實(shí)滴速不對(duì)應(yīng)的話仿真結(jié)果沒有參考意義。我一般用一個(gè)簡(jiǎn)單的換算1Hz的脈沖頻率等于60滴/分鐘。比如想模擬40滴/分鐘就把脈沖源頻率設(shè)為0.667Hz也就是周期1.5s。這個(gè)脈沖頻率可以通過Proteus里的脈沖源屬性直接設(shè)置。還有一點(diǎn)要提前說Proteus對(duì)某些外設(shè)的仿真支持并不完善。比如部分版本的Proteus對(duì)ADC輸入、DMA的仿真比較有限對(duì)傳感器的建模也比較理想化。我的處理辦法是不在仿真里硬摳ADC細(xì)節(jié)用電阻分壓來模擬傳感器輸出電平關(guān)鍵看控制邏輯和算法而不是模擬傳感器物理特性。5.2 測(cè)試結(jié)果與誤差分析仿真搭好后我做了幾組典型測(cè)試。設(shè)定滴速分別給到30、40、50、60滴/分鐘記錄系統(tǒng)穩(wěn)定后的實(shí)測(cè)滴速再觀察PID輸出的響應(yīng)情況。設(shè)定滴速滴/分鐘模擬脈沖頻率穩(wěn)定后實(shí)測(cè)滴速偏差調(diào)節(jié)時(shí)間300.5Hz30.00約3s400.667Hz39.8-0.2約4s500.833Hz50.10.1約5s601.0Hz59.7-0.3約6s這里有個(gè)小技巧仿真的滴速脈沖是由信號(hào)發(fā)生器強(qiáng)制產(chǎn)生的實(shí)際上等于“外部給定了一個(gè)固定的滴速”系統(tǒng)測(cè)量的結(jié)果當(dāng)然就接近給定值。那閉環(huán)控制體現(xiàn)在哪里體現(xiàn)在當(dāng)你把設(shè)定值突然從40調(diào)到60時(shí)PWM占空比會(huì)迅速增大系統(tǒng)輸出跟隨輸入變化如果你在Proteus里接一個(gè)虛擬示波器觀察電機(jī)驅(qū)動(dòng)PWM波形能看到占空比隨誤差的動(dòng)態(tài)調(diào)整過程。仿真環(huán)境中沒法真實(shí)轉(zhuǎn)動(dòng)蠕動(dòng)泵所以“實(shí)測(cè)滴速”其實(shí)是信號(hào)源給的但整個(gè)控制鏈路從脈沖計(jì)數(shù)、濾波、PI運(yùn)算到PWM輸出都在仿真里跑通了。誤差分析方面有兩個(gè)主要來源。一是測(cè)量窗口的固有量化誤差相鄰滴間隔取整到毫秒在高速滴速如150滴/分鐘時(shí)間隔只有400ms1ms誤差占比約0.25%影響不大但在低速如10滴/分鐘時(shí)間隔6s1ms誤差影響極小反而是滑動(dòng)平均窗口長(zhǎng)度會(huì)帶來響應(yīng)延遲。二是PWM輸出對(duì)電機(jī)轉(zhuǎn)速的量化影響占空比調(diào)節(jié)步進(jìn)為1/999體現(xiàn)在滴速上約0.1滴/分鐘可以忽略。這組仿真驗(yàn)證下來整個(gè)系統(tǒng)的邏輯和算法框架可以認(rèn)為基本正確。剩下的事情是把這套邏輯移植到真實(shí)硬件再針對(duì)傳感器信號(hào)質(zhì)量做進(jìn)一步校準(zhǔn)。6. 典型問題排查與避坑經(jīng)驗(yàn)6.1 硬件問題做實(shí)物時(shí)最容易出問題的點(diǎn)按我的經(jīng)驗(yàn)排個(gè)序。電機(jī)不轉(zhuǎn)排在第一位。常見原因有三個(gè)第一MOS管柵極沒有正確上拉到低電平PWM空閑時(shí)懸空導(dǎo)致MOS誤導(dǎo)通或者無法導(dǎo)通我用的10K下拉電阻就是為了解決這個(gè)第二12V電源和3.3V電源沒有共地MOS驅(qū)動(dòng)回路不通第三PWM頻率設(shè)置得太高超出了MOS管柵極驅(qū)動(dòng)能力的實(shí)際開關(guān)速度。碰到電機(jī)不轉(zhuǎn)先用萬用表量MOS管柵極電壓和漏極電壓再查共地90%的問題能排查出來。滴速檢測(cè)信號(hào)不穩(wěn)定是第二個(gè)大坑。表現(xiàn)為液晶屏上的滴速數(shù)值亂跳或者一個(gè)水滴觸發(fā)多次中斷。處理方法順序如下先用示波器看比較器輸出波形確認(rèn)是不是存在抖動(dòng)再調(diào)整電位器讓參考電壓在信號(hào)波形的中間位置附近增加滯回量擴(kuò)大正反饋電阻值讓輸出翻轉(zhuǎn)更果斷。如果環(huán)境光干擾嚴(yán)重在紅外管外側(cè)加遮光罩或者減小發(fā)射管限流電阻提高光強(qiáng)。電源干擾不好查但確實(shí)存在。蠕動(dòng)泵啟動(dòng)瞬間電流明顯增大如果12V和3.3V電源地線布局不合理泵的電流波動(dòng)會(huì)在地線上產(chǎn)生壓降導(dǎo)致單片機(jī)復(fù)位或者ADC讀數(shù)異常。解決辦法是在電源輸入端加大容量電解電容泵的電源線盡量短粗信號(hào)線遠(yuǎn)離泵電源線。晶振不起振也是容易忽視的問題。程序燒進(jìn)去板子沒反應(yīng)首先懷疑晶振。解決辦法是檢查負(fù)載電容的值是否和晶振規(guī)格匹配檢查晶振引腳是否虛焊如果用的是貼片晶振位置不對(duì)也會(huì)有起振困難。手頭有示波器就直接測(cè)OSC_OUT引腳沒有示波器可以量一下NRST引腳電壓如果復(fù)位正常釋放而程序不運(yùn)行基本能鎖定晶振或BOOT配置問題。6.2 軟件問題軟件層面的坑主要集中在中斷和數(shù)據(jù)類型上。中斷標(biāo)志沒清除是一個(gè)典型問題。外部中斷服務(wù)函數(shù)里檢查EXTI_GetITStatus之后必須調(diào)用EXTI_ClearITPendingBit清除中斷標(biāo)志否則中斷服務(wù)函數(shù)會(huì)反復(fù)進(jìn)入表現(xiàn)為滴速數(shù)值固定在一個(gè)很大的數(shù)或者系統(tǒng)像卡住一樣。定時(shí)器更新中斷同理不清除更新標(biāo)志溢出中斷會(huì)連續(xù)觸發(fā)。數(shù)據(jù)類型溢出很隱蔽。用uint16_t存儲(chǔ)相鄰滴間隔看似沒問題一旦低速時(shí)滴速很低或定時(shí)器溢出處理不及時(shí)計(jì)算出來的滴速可能是一個(gè)荒謬的大數(shù)。從60000 / interval算滴速時(shí)如果interval是uint16_t某些編譯器會(huì)把60000當(dāng)成有符號(hào)int處理導(dǎo)致結(jié)果變成負(fù)數(shù)進(jìn)而影響PI控制的誤差計(jì)算。我的做法是在所有關(guān)鍵變量上強(qiáng)制使用int32_t并在除法之前加數(shù)據(jù)類型判斷。看門狗喂狗位置不對(duì)會(huì)導(dǎo)致程序反復(fù)復(fù)位。如果喂狗放在主循環(huán)的開頭而主循環(huán)里某個(gè)函數(shù)執(zhí)行時(shí)間太長(zhǎng)看門狗可能提前清零系統(tǒng)。正確做法是把喂狗放在主循環(huán)末尾確保整個(gè)主循環(huán)完整執(zhí)行一遍后再喂狗。中斷里不要喂狗否則即使主循環(huán)卡死中斷仍然能喂狗看門狗形同虛設(shè)。最后再說一個(gè)“軟硬結(jié)合”的坑仿真跑通的代碼拿到實(shí)物上不工作原因往往不是算法錯(cuò)了而是信號(hào)質(zhì)量沒有達(dá)到算法假設(shè)的理想方波標(biāo)準(zhǔn)。這時(shí)候不要在代碼層面強(qiáng)行加延遲或者改濾波窗口去“賭”先回到硬件用示波器看信號(hào)把傳感器輸出調(diào)成干凈方波再回頭檢查代碼這樣排查路徑才是高效的。我個(gè)人在實(shí)際制作這個(gè)項(xiàng)目時(shí)最耗時(shí)間的不是代碼也不是原理圖而是滴速傳感器的機(jī)械固定。紅外對(duì)管和滴壺之間的相對(duì)位置必須穩(wěn)定稍微歪一點(diǎn)信號(hào)幅度就差很多。后來我用一小塊亞克力做了一個(gè)帶凹槽的夾座把滴壺正好卡進(jìn)去傳感器固定在兩側(cè)這個(gè)結(jié)構(gòu)問題才算徹底解決。如果你也想復(fù)刻這個(gè)項(xiàng)目我建議從一開始就把傳感器的固定架設(shè)計(jì)進(jìn)去別等電路調(diào)完再補(bǔ)機(jī)械結(jié)構(gòu)。整個(gè)系統(tǒng)后續(xù)還能擴(kuò)展的方向很多比如加ESP8266做遠(yuǎn)程監(jiān)護(hù)、加RTOS改善任務(wù)調(diào)度、接云平臺(tái)做輸液數(shù)據(jù)記錄基礎(chǔ)打好之后這些都有的玩。