業(yè)-自動(dòng)控制邏輯開發(fā):閾值觸發(fā)自動(dòng)澆水、自動(dòng)通風(fēng)、溫控聯(lián)動(dòng))
自動(dòng)控制邏輯開發(fā)閾值觸發(fā)自動(dòng)澆水、自動(dòng)通風(fēng)、溫控聯(lián)動(dòng)作者黒漂技術(shù)佬前面四篇文章都在講數(shù)據(jù)怎么采集、怎么傳輸、怎么存儲(chǔ)。這當(dāng)然重要——但說實(shí)話純「數(shù)據(jù)大屏」的項(xiàng)目農(nóng)戶看了只會(huì)說一句「好是好但能不能幫我干點(diǎn)活」本文就來回答這個(gè)問題怎么讓系統(tǒng)自動(dòng)干活。溫度高了自動(dòng)開風(fēng)機(jī)土壤干了自動(dòng)澆水光照不夠自動(dòng)補(bǔ)光——真正的智慧農(nóng)業(yè)數(shù)據(jù)不是終點(diǎn)控制才是。一、自動(dòng)化的核心價(jià)值傳統(tǒng)大棚種植最痛苦的是什么是「人不能離」。夏天中午棚內(nèi)溫度飆升到 40℃你必須沖進(jìn)去開風(fēng)機(jī)開天窗凌晨濕度驟降又得爬起來開加濕器。一個(gè)疏忽一季的收成就打了水漂。自動(dòng)化控制要解決的就是這個(gè)痛點(diǎn)。它的核心價(jià)值有三層解放人力24 小時(shí)自動(dòng)值守人不用綁在大棚里精準(zhǔn)控制傳感器采集的精度遠(yuǎn)超人的感知28.5℃ 和 30℃ 的區(qū)別你感覺不到但機(jī)器能聯(lián)動(dòng)決策溫度和濕度不是孤立問題——開風(fēng)機(jī)降溫的同時(shí)會(huì)帶走濕度系統(tǒng)能自動(dòng)計(jì)算平衡點(diǎn)人就很難做到二、控制閉環(huán)架構(gòu)自動(dòng)控制的本質(zhì)是一個(gè)「感知→決策→執(zhí)行→反饋」的閉環(huán)┌─────────┐ 傳感器數(shù)據(jù) ┌─────────────┐ 控制指令 ┌─────────┐ │ 傳感器 │ ───────────────→ │ 規(guī)則引擎 │ ──────────────→ │ 執(zhí)行器 │ │ (溫度等) │ │ (閾值判斷) │ │ (風(fēng)機(jī)等) │ └─────────┘ └─────────────┘ └─────────┘ ↑ │ │ ┌─────────┐ │ └───────────────────────│ 反饋 │←───────────────────────┘ 傳感器持續(xù)采集驗(yàn)證效果 └─────────┘ 執(zhí)行后傳感器值變化舉個(gè)具體例子溫度傳感器上報(bào) 32℃ → 規(guī)則引擎判斷超閾值 → 下發(fā)「開風(fēng)機(jī)」指令 → 風(fēng)機(jī)啟動(dòng) → 1 分鐘后溫度降到 29℃ → 規(guī)則引擎判斷達(dá)標(biāo) → 關(guān)閉風(fēng)機(jī)。這就是一個(gè)完整的控制閉環(huán)。三、閾值觸發(fā)規(guī)則設(shè)計(jì)先列一個(gè)智慧農(nóng)業(yè)的典型控制規(guī)則表控制場(chǎng)景觸發(fā)條件執(zhí)行動(dòng)作恢復(fù)條件高溫降溫溫度 30℃開風(fēng)機(jī) 開天窗溫度 28℃低溫加熱溫度 15℃關(guān)天窗 開加熱器溫度 18℃土壤干旱澆水土壤濕度 30%開水泵灌溉濕度 70%濕度過高排濕濕度 90%開風(fēng)機(jī)排濕濕度 80%濕度過低加濕濕度 60%開噴霧/加濕器濕度 70%光照不足補(bǔ)光光照 5000 Lux開補(bǔ)光燈光照 8000 LuxCO2不足補(bǔ)充CO2 300ppm開CO2發(fā)生器CO2 500ppm注意每個(gè)規(guī)則都有觸發(fā)閾值和恢復(fù)閾值而且兩者不同這叫「滯回控制」。比如溫度高于 30℃ 開風(fēng)機(jī)要等溫度降到 28℃ 才關(guān)——如果觸發(fā)和恢復(fù)用同一個(gè)閾值設(shè)備就會(huì)在邊界上瘋狂開關(guān)振蕩電機(jī)燒了都不知道為啥。四、規(guī)則引擎實(shí)現(xiàn)三種方案對(duì)比方案一定時(shí)輪詢最簡(jiǎn)單適合初期ComponentpublicclassThresholdCheckScheduler{AutowiredprivateInfluxDBServiceinfluxDBService;AutowiredprivateControlServicecontrolService;// 每30秒檢查一次閾值Scheduled(fixedDelay30000)publicvoidcheckThresholds(){// 查詢最近一次所有傳感器的讀數(shù)ListSensorReadinglatestReadingsinfluxDBService.queryLatestReadings();for(SensorReadingreading:latestReadings){ThresholdRuleruleruleConfig.getRule(reading.getGreenhouseId(),reading.getSensorType());if(rule.isExceeded(reading.getValue())){controlService.execute(reading.getGreenhouseId(),rule.getAction());}}}}優(yōu)點(diǎn)是實(shí)現(xiàn)簡(jiǎn)單缺點(diǎn)是有延遲最多 30 秒而且每條規(guī)則都要查庫(kù)傳感器多了效率低。方案二MQTT 實(shí)時(shí)消費(fèi)推薦直接在消息消費(fèi)時(shí)判斷閾值延遲最低ComponentpublicclassRealTimeThresholdHandler{AutowiredprivateControlServicecontrolService;AutowiredprivateRuleConfigruleConfig;// 防抖計(jì)數(shù)器Key greenhouseId:sensorType, Value 連續(xù)超閾值次數(shù)privatefinalMapString,IntegerexceedCountersnewConcurrentHashMap();// 冷卻記錄Key greenhouseId:action, Value 上次觸發(fā)時(shí)間privatefinalMapString,LongcooldownMapnewConcurrentHashMap();privatestaticfinalintDEBOUNCE_COUNT3;// 連續(xù)3次超閾值才觸發(fā)privatestaticfinallongCOOLDOWN_MS300_000;// 5分鐘冷卻publicvoidonSensorData(SensorDatadata){StringruleKeydata.getGreenhouseId():data.getSensorType();ThresholdRuleruleruleConfig.getRule(ruleKey);if(rulenull||!rule.isExceeded(data.getValue())){// 未超閾值重置防抖計(jì)數(shù)器exceedCounters.remove(ruleKey);return;}// 超閾值 → 防抖計(jì)數(shù) 1intcountexceedCounters.merge(ruleKey,1,Integer::sum);if(countDEBOUNCE_COUNT){return;// 還不夠次數(shù)繼續(xù)觀察}// 檢查冷卻時(shí)間避免頻繁觸發(fā)同一個(gè)設(shè)備StringactionKeydata.getGreenhouseId():rule.getAction().getName();LonglastTriggercooldownMap.get(actionKey);if(lastTrigger!nullSystem.currentTimeMillis()-lastTriggerCOOLDOWN_MS){return;}// 觸發(fā)控制cooldownMap.put(actionKey,System.currentTimeMillis());exceedCounters.remove(ruleKey);// 觸發(fā)后重置計(jì)數(shù)器controlService.execute(data.getGreenhouseId(),rule.getAction());log.info(閾值觸發(fā): 大棚{}, 傳感器{}, 當(dāng)前值{}, 閾值{}, 動(dòng)作{},data.getGreenhouseId(),data.getSensorType(),data.getValue(),rule.getThreshold(),rule.getAction().getName());}}這里有兩個(gè)關(guān)鍵的保護(hù)機(jī)制防抖Debounce傳感器可能因?yàn)殡姶鸥蓴_等原因偶爾讀到一個(gè)異常值比如溫度突然跳到 50℃ 又瞬間回來。如果讀到一次異常就觸發(fā)控制就可能誤開風(fēng)機(jī)。解決方案是「連續(xù) N 次超閾值才觸發(fā)」——連續(xù) 3 次如果是 5 秒一次采集就是 15 秒內(nèi)持續(xù)超標(biāo)才算真的超標(biāo)。冷卻Cooldown控制操作執(zhí)行后環(huán)境變化需要時(shí)間。比如開水泵灌溉后土壤濕度不會(huì)立刻從 25% 跳到 60%。如果不加冷卻接下來的 5 分鐘內(nèi)系統(tǒng)會(huì)反復(fù)檢測(cè)到「濕度 30%需要澆水」。冷卻機(jī)制規(guī)定「同一設(shè)備同一動(dòng)作 5 分鐘內(nèi)不再重復(fù)觸發(fā)」避免控制指令滿天飛。方案三EMQX 規(guī)則引擎Broker 內(nèi)置如果你用的是 EMQX 企業(yè)版它的規(guī)則引擎可以直接在 Broker 側(cè)做簡(jiǎn)單的閾值判斷-- EMQX 規(guī)則引擎 SQLSELECTpayload.temperatureastemp,payload.humidityashum,topicFROMagriculture///sensor/#WHEREpayload.temperature30超閾值后可以直接觸發(fā) HTTP 回調(diào)到你的控制服務(wù)。這個(gè)方案的好處是延遲極低處理發(fā)生在 Broker 內(nèi)部適合對(duì)實(shí)時(shí)性要求極高的場(chǎng)景。缺點(diǎn)是邏輯不能太復(fù)雜而且依賴 EMQX 企業(yè)版。推薦組合拳方案二 方案三。EMQX 規(guī)則引擎做第一層快速過濾對(duì)超出明顯安全范圍的數(shù)據(jù)直接觸發(fā)告警SpringBoot 消費(fèi)者做第二層精細(xì)化判斷考慮防抖、冷卻、聯(lián)動(dòng)邏輯。五、聯(lián)動(dòng)控制別顧此失彼溫室環(huán)境是相互耦合的——你不能孤立地控制某一個(gè)參數(shù)。舉個(gè)典型場(chǎng)景溫度 33℃超了、濕度 55%也低了。如果只看溫度系統(tǒng)開風(fēng)機(jī)降溫——結(jié)果風(fēng)機(jī)一吹濕度從 55% 降到了 40%掉進(jìn)更低的坑里。正確的聯(lián)動(dòng)做法是publicclassLinkedControlLogic{publicvoidhandleTemperatureHigh(SensorDatadata){doubletempdata.getTemperature();doublehumiditydata.getHumidity();// 開風(fēng)機(jī)降溫controlService.sendCommand(data.getGreenhouseId(),fan_on);// 聯(lián)動(dòng)如果溫度高且濕度低同時(shí)開啟加濕器if(humidity60){log.info(聯(lián)動(dòng)降溫同時(shí)加濕當(dāng)前濕度{}%,humidity);controlService.sendCommand(data.getGreenhouseId(),humidifier_on);}// 聯(lián)動(dòng)開風(fēng)機(jī)前檢查天窗是否已開DeviceStatusskylightdeviceService.getStatus(data.getGreenhouseId(),skylight);if(!skylight.isOpen()){controlService.sendCommand(data.getGreenhouseId(),skylight_open);}}}聯(lián)動(dòng)邏輯的核心是「不要只看一個(gè)變量」。在做任何控制決策時(shí)都要檢查組關(guān)聯(lián)參數(shù)是否在安全范圍內(nèi)。六、手動(dòng)/自動(dòng)模式切換自動(dòng)控制再聰明也得給人留個(gè)「剎車」。有時(shí)候農(nóng)戶要進(jìn)棚作業(yè)、要噴藥、要采摘——這些場(chǎng)景下自動(dòng)控制可能會(huì)幫倒忙。Topic 設(shè)計(jì)agriculture/{farmId}/{greenhouseId}/mode Payload: {mode: auto} 或 {mode: manual}控制服務(wù)在執(zhí)行前必須檢查當(dāng)前模式publicvoidexecute(StringgreenhouseId,ControlActionaction){// 先查模式StringmoderedisService.get(greenhouse:mode:greenhouseId);if(manual.equals(mode)){log.info(手動(dòng)模式跳過自動(dòng)控制: 大棚{}, 動(dòng)作{},greenhouseId,action.getName());return;}// 自動(dòng)模式正常執(zhí)行mqttCommandService.sendCommand(greenhouseId,action);// 記錄控制日志controlLogService.record(ControlLog.builder().greenhouseId(greenhouseId).action(action.getName()).mode(auto).triggerCondition(action.getTriggerCondition()).threshold(action.getThreshold()).currentValue(action.getCurrentValue()).timestamp(System.currentTimeMillis()).result(success).build());}手動(dòng)模式優(yōu)先級(jí)高于自動(dòng)——這是鐵律。機(jī)器可以輔助人但不能凌駕于人。七、控制日志審計(jì)比控制還重要每執(zhí)行一次控制操作都要留下完整的日志記錄。不是為了形式主義而是有實(shí)際價(jià)值CREATETABLEcontrol_log(idBIGINTPRIMARYKEYAUTO_INCREMENT,greenhouse_idVARCHAR(50)NOTNULL,actionVARCHAR(50)NOTNULLCOMMENTfan_on/humidifier_on/pump_on等,modeVARCHAR(10)NOTNULLCOMMENTauto/manual,trigger_conditionVARCHAR(200)COMMENT觸發(fā)條件描述,threshold_valueDECIMAL(10,2)COMMENT閾值,current_valueDECIMAL(10,2)COMMENT觸發(fā)時(shí)的傳感器值,execute_timeDATETIMENOTNULL,resultVARCHAR(20)NOTNULLCOMMENTsuccess/fail,fail_reasonVARCHAR(500)COMMENT失敗原因,INDEXidx_time(execute_time),INDEXidx_greenhouse(greenhouse_id,execute_time));這些日志的用途事后排查哪天作物狀態(tài)不對(duì)可以回溯「當(dāng)時(shí)系統(tǒng)干了什么」閾值優(yōu)化統(tǒng)計(jì)分析觸發(fā)頻率——如果降溫風(fēng)機(jī)一天開了 50 次說明你閾值設(shè)得有問題設(shè)備健康度某水泵觸發(fā)頻率突然翻倍可能是傳感器失準(zhǔn)或管路泄漏的預(yù)警信號(hào)總結(jié)自動(dòng)控制的五條軍規(guī)滯回控制觸發(fā)和恢復(fù)用不同閾值避免設(shè)備振蕩防抖 冷卻連續(xù) N 次超標(biāo)才觸發(fā)觸發(fā)后 N 分鐘內(nèi)不重復(fù)聯(lián)動(dòng)判斷做任何控制前檢查關(guān)聯(lián)參數(shù)手動(dòng)優(yōu)先手動(dòng)模式一開自動(dòng)全部讓路控制必記日志沒有審計(jì)的控制是定時(shí)炸彈數(shù)據(jù)采集做得再好最終產(chǎn)生價(jià)值的還是「控制」。畢竟農(nóng)戶要的不是漂亮的溫濕度曲線而是好收成。