預測不是天氣報告
在傳統集中式電網中,發電端是可控的——燃煤或燃氣機組可以根據調度指令增減出力。但在分散式微電網中,發電端本身成了最大的變數:一片雲遮住太陽能板,發電量可能在三十秒內驟降 60%;一陣突如其來的強風,又可能讓風機超出安全轉速而自動切離。預測系統的任務不只是「明天大概發多少電」,而是必須以 15 分鐘為粒度,持續更新未來 48 小時的機率分佈。
現代微電網預測架構通常採用混合模型——物理模型根據衛星雲圖、風場模擬和日照角度計算基礎發電量;機器學習模型則從歷史資料中學習局部微氣候的規律,例如某個山谷的晨霧消散時間、某排建築物對午後太陽能板的遮擋效應。兩者融合後,再透過集成學習(ensemble)產出帶有不確定性區間的預測結果。實務經驗顯示,這種混合架構比起純物理模型或純資料驅動模型,能將極端天氣日的預測誤差降低約 38%。
儲能的節奏感
如果預測是眼睛,儲能就是肌肉。但儲能系統的充放電策略遠比「電多就存、電少就放」複雜得多。鋰離子電池的循環壽命直接受到充放電深度和速率的影響——頻繁地在 20% 到 100% 之間來回,會讓可用容量在三年內衰減到原來的 70%。因此,智慧調度演算法必須同時權衡三個目標:最大化可再生能源使用率、最小化電池衰退成本、以及確保緊急備轉容量。
實務上常採用模型預測控制(MPC)框架:在每個時間步,演算法會向前模擬未來 24 小時內數百種可能的發電與負載情境,選擇一條在期望成本和風險之間取得最佳平衡的充放電軌跡。然後只執行第一步的控制指令,下一個時間步再根據新的預測和實際狀態重新優化。這種「滾動優化」策略能有效應對預測誤差,避免一次錯誤的提前放電導致傍晚尖峰時段無電可用。
需量反應的社會層
微電網不只是技術系統,也是社會系統。當預測顯示傍晚尖峰將出現供電缺口時,系統可以向參與需量反應計畫的住戶發送訊號——「如果接下來兩小時將空調設定調高兩度,你可以獲得今日電價 20% 的折扣」。但住戶的行為反應並不像發電機那樣可預測:有些家庭願意配合、有些永遠不回應、有些只在特定條件下才會參與(例如家中沒有嬰幼兒或長者時)。
因此,預測模型還需要納入行為預測層:根據歷史參與模式、天氣條件、時間(平日/假日/節慶),估算每次需量反應事件的預期負載削減量與其不確定性。這種「社會物理學」維度是微電網預測中最容易被低估、卻對實際運轉影響最大的環節。一個技術上完美的日前排程,可能因為 30% 的住戶臨時退出需量反應而產生缺口——而這個缺口又得靠昂貴的即時主電網電力來填補。
import numpy as np from scipy.optimize import minimize class MicrogridScheduler: # Rolling-horizon MPC scheduler for community microgrid. # Balances RE utilization, battery degradation, and peak pricing. def __init__(self, battery_kwh, max_charge_rate, cycle_cost): self.capacity = battery_kwh self.max_rate = max_charge_rate self.cycle_cost = cycle_cost # $/kWh/cycle def optimize(self, forecast, load, soc_0, price): # forecast: array of predicted RE generation (kW, 24h × 4) # load: array of predicted load (kW) # price: time-of-use grid price ($/kWh) T = len(forecast) def objective(x): charge, discharge, grid_import = x[:T], x[T:2*T], x[2*T:] soc = soc_0 + np.cumsum(charge * 0.92 - discharge / 0.92) / self.capacity cost = np.sum(grid_import * price) + np.sum(discharge * self.cycle_cost) cost += np.sum(np.maximum(0, soc - 1) * 100) # soc penalty return cost bounds = [(0, self.max_rate)] * T + [(0, self.max_rate)] * T + [(0, None)] * T res = minimize(objective, np.zeros(3*T), bounds=bounds, method='L-BFGS-B') return res.x[:T], res.x[T:2*T], res.x[2*T:] # charge, discharge, grid