Smart-Microgrid-Energy-Forecasting

當太陽能和風力發電成為社區的日常電源,預測就不再只是數字遊戲——它是決定何時儲電、何時釋放、何時向主電網購電的關鍵演算。

Solar PV
3.8 MW
Wind
2.1 MW
Storage
6.4 MWh
Grid Import
0.9 MW
Forecast MAE 4.7% 日前發電預測平均絕對誤差
Self-Sufficiency 83% 社區微電網自主供電比率
Response Time 0.8 s 需量反應訊號到儲能動作延遲
Peak Shaving 31% 尖峰負載削減幅度

預測不是天氣報告

在傳統集中式電網中,發電端是可控的——燃煤或燃氣機組可以根據調度指令增減出力。但在分散式微電網中,發電端本身成了最大的變數:一片雲遮住太陽能板,發電量可能在三十秒內驟降 60%;一陣突如其來的強風,又可能讓風機超出安全轉速而自動切離。預測系統的任務不只是「明天大概發多少電」,而是必須以 15 分鐘為粒度,持續更新未來 48 小時的機率分佈。

現代微電網預測架構通常採用混合模型——物理模型根據衛星雲圖、風場模擬和日照角度計算基礎發電量;機器學習模型則從歷史資料中學習局部微氣候的規律,例如某個山谷的晨霧消散時間、某排建築物對午後太陽能板的遮擋效應。兩者融合後,再透過集成學習(ensemble)產出帶有不確定性區間的預測結果。實務經驗顯示,這種混合架構比起純物理模型或純資料驅動模型,能將極端天氣日的預測誤差降低約 38%

儲能的節奏感

如果預測是眼睛,儲能就是肌肉。但儲能系統的充放電策略遠比「電多就存、電少就放」複雜得多。鋰離子電池的循環壽命直接受到充放電深度和速率的影響——頻繁地在 20% 到 100% 之間來回,會讓可用容量在三年內衰減到原來的 70%。因此,智慧調度演算法必須同時權衡三個目標:最大化可再生能源使用率、最小化電池衰退成本、以及確保緊急備轉容量。

實務上常採用模型預測控制(MPC)框架:在每個時間步,演算法會向前模擬未來 24 小時內數百種可能的發電與負載情境,選擇一條在期望成本和風險之間取得最佳平衡的充放電軌跡。然後只執行第一步的控制指令,下一個時間步再根據新的預測和實際狀態重新優化。這種「滾動優化」策略能有效應對預測誤差,避免一次錯誤的提前放電導致傍晚尖峰時段無電可用。

Conservative 4.2 MW P90 可靠區間
Optimistic 6.3 MW P10 上緣估計
Solar panels and wind turbine microgrid
Fig 1. 社區微電網整合屋頂太陽能、小型風機與集中式儲能櫃 Source: Unsplash

需量反應的社會層

微電網不只是技術系統,也是社會系統。當預測顯示傍晚尖峰將出現供電缺口時,系統可以向參與需量反應計畫的住戶發送訊號——「如果接下來兩小時將空調設定調高兩度,你可以獲得今日電價 20% 的折扣」。但住戶的行為反應並不像發電機那樣可預測:有些家庭願意配合、有些永遠不回應、有些只在特定條件下才會參與(例如家中沒有嬰幼兒或長者時)。

因此,預測模型還需要納入行為預測層:根據歷史參與模式、天氣條件、時間(平日/假日/節慶),估算每次需量反應事件的預期負載削減量與其不確定性。這種「社會物理學」維度是微電網預測中最容易被低估、卻對實際運轉影響最大的環節。一個技術上完美的日前排程,可能因為 30% 的住戶臨時退出需量反應而產生缺口——而這個缺口又得靠昂貴的即時主電網電力來填補。

Community energy management dashboard
Fig 2. 社區能源管理儀表板:即時顯示發電、儲能、負載與需量反應參與率 Source: Unsplash
microgrid_scheduler.py PYTHON
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