
1. 為什么容量規劃必須面對“混合不確定性”1.1 從一次實際規劃項目說起之前在做微電網前期可研時設備選型一度是“按最惡劣情況取系數”光伏按 0.8 折減、風機按 0.9 折減、儲能按 2 小時配置結果項目投資預算高出預期很多業主差點放棄。后來換了一套思路把風、光、負荷的不確定性拆開建模一部分用概率場景描述一部分用區間范圍描述再做魯棒優化最后得到的配置方案既沒有犧牲供電可靠性又顯著降低了初始投資。這個項目讓我意識到風光儲微電網容量規劃并不是“套公式選設備”而是一個典型的“混合不確定性條件下的協同優化”問題。本文就圍繞這個話題把整個建模思路、數學表達、Python 教學示例和工程落地經驗完整梳理一遍。適合正在做微電網設計、儲能配置、新能源接入方案的研究生、工程師和項目經理。讀完你至少能回答三個問題混合不確定性到底是什么魯棒優化怎么用于容量規劃風、光、儲容量如何協同優化而不是分別估算1.2 風光儲微電網容量規劃的本質先看一個典型的園區微電網系統屋頂光伏、小型風電機組、電池儲能、本地負荷可能還有柴油發電機或并網聯絡線。容量規劃要做的事情是在不違背供電可靠性、經濟性、物理運行約束的前提下確定光伏裝機容量、風電裝機容量、儲能額定功率、儲能額定容量這幾個關鍵數字。聽起來不難但難點在于輸入數據本身不可靠。光伏出力依賴于輻照度風電出力依賴于風速負荷也有明顯的季節波動和隨機波動。如果只取一個典型值配置結果可能在大多數日子是浪費的在極端日子卻不夠用。如果全部按最惡劣情況配置投資又過高。所以工程上更合理的做法是把不確定性問題顯式建模讓優化算法自動權衡“多配一點”和“少配一點”帶來的成本與風險。1.3 混合不確定性的含義很多文獻會單獨談不確定性但實際工程中往往同時存在兩類不確定性概率不確定性可以用歷史數據或概率分布描述。比如風速服從 Weibull 分布光伏預測誤差近似正態分布負荷預測誤差也可以近似建模。區間不確定性只知道參數的大致范圍但無法給出精確分布。比如某些數據來自人工上報、跨區域統計或極端天氣情景只能給出下限和上限。“混合不確定性”就是同時考慮這兩種類型。風光資源、負荷既有統計規律又存在難以用單一分布刻畫的邊界情況。如果只用隨機優化可能低估極端場景如果只用傳統魯棒優化又會因為“最壞情況”過于保守而導致成本虛高。因此近年來的研究方向是把隨機規劃和魯棒優化結合形成分布魯棒優化或混合魯棒優化模型這也是本文的核心技術主線。1.4 魯棒優化與協同優化的關系魯棒優化最核心的思想是不是優化“期望值”而是尋找一個在不確定性集合內所有可能取值下都可行的方案。它的優點是不需要非常精確的概率分布只需要不確定性集合缺點是如果不合理設置集合邊界結果會保守。協同優化則強調光伏、風電、儲能三者不是彼此獨立的“拼裝”而是共享同一個容量配置決策和同一套運行策略。光伏裝多了儲能可能可以少配一點儲能功率大了可能可以替代部分風電出力。只有把投資決策和運行模擬放在同一個優化框架里才能得到全局最優方案。本文使用的建模路線是“兩階段協同優化”第一階段確定容量第二階段在不同不確定性場景下模擬運行通過場景約束或懲罰項把運行可行性反饋到容量決策中。這是一種非常適合工程的建模框架也是目前風光儲微電網容量規劃的主流做法。2. 協同優化總體思路與求解框架2.1 系統的能量流拆解在建模之前先明確系統組成。一個簡化的風光儲微電網可以抽象為四個電源側/負荷側節點光伏陣列出力受輻照度影響只在天晴時段有功率。風電機組出力隨風速變化可能晝夜都有輸出。儲能電池既可以放電支撐負荷也可以充電消納多余風光。負荷本地用電需求通常按日負荷曲線或年度時序負荷給出。如果不考慮并網售電那么系統約束可以簡化為任意時刻“光伏出力 風電出力 儲能放電功率 ≥ 負荷功率”同時“光伏出力 風電出力 - 負荷功率”超過儲能充電上限時需要棄風棄光。實際規劃時還需要考慮設備的爬坡約束、儲能SOC上下限、變壓器容量、逆變器效率等。但教學模型中我會先保留最核心的功率平衡和儲能能量平衡再說明擴展方向。2.2 決策變量、參數與目標函數決策變量分兩類投資決策變量風電裝機容量 P_wkW光伏裝機容量 P_pvkW儲能額定功率 P_bkW儲能額定容量 E_bkWh運行決策變量各時段儲能充放電功率各時段實際棄風棄光量各時段是否切負荷目標函數通常是“初始投資等年值 年運行維護費用 缺電懲罰費用”最小。投資費用可以按等年值折算避免只看靜態回收期。不確定參數包括風資源乘子風電出力與裝機容量的比值即容量因子光資源乘子光伏出力與裝機容量的比值負荷功率在魯棒優化框架下這些參數不再是一個固定數而是落在一個不確定性集合內。建模的關鍵就是怎樣定義這個集合使得方案在集合內所有情況下都能滿足約束。2.3 兩階段優化框架兩階段優化的邏輯是第一階段決定容量。這個決策在不確定參數實現之前就要做出也就是“這里建多大”。第二階段在給定容量和特定風光負荷場景下決定儲能充放電、棄風棄光等運行策略也就是“實際怎么運行”。用數學語言描述可以寫成min 投資成本 運行成本 s.t. 對每一個不確定性場景 s ∈ S 存在可行的運行策略 y_s 滿足 功率平衡約束 儲能約束 設備容量約束這里的關鍵是“對每一個場景 s 都必須存在可行運行策略”而不是“平均意義上可行”。這就是魯棒可行性的體現。如果不確定性場景數量很多可以通過KKT條件或線性規劃對偶變換把內層運行優化問題等價替換成一組線性約束從而得到一個單層優化問題。這種處理方式在學術文獻中稱之為“魯棒對等模型”。3. 數學模型從確定性到混合魯棒優化3.1 參數與集合定義為了方便代碼實現先定義一組簡化但完整的符號符號含義示例值T運行時段集合24小時S不確定性場景集合3個場景P_w / P_pv風電、光伏裝機待優化P_b / E_b儲能功率與容量待優化wf_s場景 s 風電容量因子0.8pv_s場景 s 光伏容量因子0.9D_s(t)場景 s 下 t 時段負荷80~120 kWeff_c / eff_d儲能充電、放電效率0.95SOC_min / SOC_max儲能SOC上下限0.1 / 0.9IC_w / IC_pv風電、光伏單位投資7000/5000 元/kWIC_p / IC_e儲能單位功率/容量投資1500/2000 元/kWh需要注意的是以上“示例值”只是用于教學演示真實項目必須根據設備廠商報價、當地資源條件和負荷數據重新標定。3.2 確定性基礎模型如果不考慮不確定性只取一個典型場景模型就是典型的最優容量配置min 成本函數 s.t. 風電出力 光伏出力 儲能放電 - 儲能充電 ≥ 負荷 儲能能量轉移約束 充放電功率不越限 各容量非負這個模型可以快速給出一版參考方案但它有天然缺陷換一組風資源數據結果可能完全不同。所以確定性模型只適合做初始估算不適合直接作為工程結論。3.3 基于場景的魯棒約束把確定性模型擴展為多個場景后核心變化是功率平衡約束必須對所有場景成立對每個場景 s每個時段 t P_w * wf_s(t) P_pv * pv_s(t) P_b_discharge(s,t) - P_b_charge(s,t) ≥ D_s(t)儲能SOC約束也要分場景獨立模擬因為每個場景下充放電策略不同。容量變量 P_w、P_pv、P_b、E_b 在所有場景之間共用這是“協同”的關鍵同一個容量配置必須能應對所有預設不確定性場景。這種處理方式是一種比較直觀的魯棒可行性建模。如果場景集合包含風電、光伏、負荷的各種極端組合那么得到的配置就能抗住這些極端情況。更嚴格的魯棒優化還可以用“盒式不確定集合 對偶變換”把連續區間不確定性轉化為有限約束但核心思想是一樣的讓方案對所有可能情況都可行。3.4 混合不確定性如何體現我所說的“混合不確定性”體現在建模元素的三類組合中負荷用一個區間范圍表示不指定具體分布。風電和光伏用地形場景或概率場景表示帶有一定統計特征。極端天氣作為額外場景加入約束區間和概率同時存在。在代碼實現中最常見的做法是構造一個“場景集合”其中既包含正常概率場景也包含極端區間場景然后在所有場景上強制約束可行。這樣既吸收了概率信息又保留了魯棒邊界。4. Python 教學示例基于場景的魯棒協同容量規劃4.1 環境準備本文示例使用 Python 3.9 以上版本用到兩個開源庫PuLP線性規劃建模工具自帶 CBC 求解器。Pandas / Matplotlib數據處理與結果可視化。如果還沒有安裝可以使用 pip 安裝pip install pulp pandas matplotlib需要特別說明的是這個教學示例不是完整的工程軟件而是一個“最小可運行模型”。真實項目中建議用 Pyomo 或 GAMS 配合 Gurobi、CPLEX 等商業求解器以支持更大規模的時序優化。4.2 項目文件結構建議按下面的結構組織代碼方便后續擴展wind_solar_storage_planning/ │ ├── data/ │ └── scenarios.csv ├── src/ │ └── plan_model.py ├── results/ │ └── config_result.csv └── README.md下面先創建數據文件data/scenarios.csv它保存不同場景下的風電、光伏容量因子和負荷倍率scenario,wt_factor,pv_factor,load_factor,weight normal,1.00,1.00,1.00,0.70 wind_low,0.55,1.05,1.05,0.15 sun_low,1.05,0.45,1.05,0.15這里前三列分別表示風電容量因子、光伏容量因子和負荷倍率。weight表示該場景在運行成本加權中的權重。注意場景集合可以擴充但每增加一個場景約束數量也會增加求解規模會變大。4.3 創建基礎負荷曲線實際優化不能只看一個時間點至少要用 24 小時負荷曲線。示例中假設典型日負荷曲線為# 文件路徑data/load_profile.py def get_load_profile(): 返回 24 小時典型日負荷曲線單位 kW。 實際項目請用本地負荷數據替換。 load_profile [ 60, 55, 50, 45, 45, 50, 70, 95, 110, 120, 125, 120, 115, 110, 105, 100, 105, 115, 125, 120, 105, 90, 75, 65 ] return load_profile這段數據的含義是凌晨負荷低白天和傍晚負荷高。用代碼把負荷曲線獨立成函數是為了后續替換真實數據時不需要改動主模型。4.4 主模型代碼下面是核心優化模型代碼使用 PuLP 實現。由于教學示例聚焦思路我做了三點簡化用日負荷曲線代表全年運行不展開 8760 小時。儲能運行方式簡化為每個場景獨立模擬。忽略充放電功率同時為 0 的整數變量約束通過目標函數中的懲罰項避免無意義充放電。# 文件路徑src/plan_model.py import pulp import pandas as pd # 讀取場景數據 scenario_df pd.read_csv(data/scenarios.csv) load_profile get_load_profile() T len(load_profile) # 24 小時 scenarios scenario_df[scenario].tolist() # 投資成本參數元/kW 或 元/kWh IC_wind 7000 IC_pv 5000 IC_bess_power 1500 IC_bess_energy 2000 annual_factor 0.1 # 投資等年值系數實際按壽命與折現率計算 # 創建優化問題 prob pulp.LpProblem(Microgrid_Capacity_Planning, pulp.LpMinimize) # 容量決策變量 P_w pulp.LpVariable(wind_capacity, lowBound0) P_pv pulp.LpVariable(pv_capacity, lowBound0) P_b pulp.LpVariable(bess_power, lowBound0) E_b pulp.LpVariable(bess_energy, lowBound0) # 每個場景下的運行變量 discharge {} charge {} soc {} for s in scenarios: for t in range(T): discharge[(s, t)] pulp.LpVariable(fdischarge_{s}_{t}, lowBound0) charge[(s, t)] pulp.LpVariable(fcharge_{s}_{t}, lowBound0) soc[s] pulp.LpVariable(fsoc_{s}_final, lowBound0) # 目標函數投資等年值 運行懲罰 # 為了引導模型優先使用風光出力給棄電和缺電設置較高懲罰 penalty_curtail 300 penalty_shortage 800 objective annual_factor * ( IC_wind * P_w IC_pv * P_pv IC_bess_power * P_b IC_bess_energy * E_b ) # 功率平衡約束中用一個變量表示缺電量和棄電量 shortage {} curtail {} for s in scenarios: for t in range(T): shortage[(s, t)] pulp.LpVariable(fshortage_{s}_{t}, lowBound0) curtail[(s, t)] pulp.LpVariable(fcurtail_{s}_{t}, lowBound0) objective penalty_shortage * shortage[(s, t)] objective penalty_curtail * curtail[(s, t)] prob objective # 約束 soc_min, soc_max 0.1, 0.9 charge_eff 0.95 discharge_eff 0.95 hours_per_step 1 # 每個時段 1 小時 for s in scenarios: wt_f scenario_df.loc[scenario_df[scenario] s, wt_factor].values[0] pv_f scenario_df.loc[scenario_df[scenario] s, pv_factor].values[0] load_f scenario_df.loc[scenario_df[scenario] s, load_factor].values[0] weight scenario_df.loc[scenario_df[scenario] s, weight].values[0] for t in range(T): load_t load_profile[t] * load_f wind_power P_w * wt_f pv_power P_pv * pv_f # 功率平衡風光出力 儲能放電 - 儲能充電 缺電 負荷 棄電 prob (wind_power pv_power discharge[(s, t)] - charge[(s, t)] shortage[(s, t)] load_t curtail[(s, t)]) # 儲能充放電功率不超過額定功率 prob discharge[(s, t)] P_b prob charge[(s, t)] P_b # 儲能容量與日凈充電量約束 # 簡化處理日凈充電量不能超過儲能容量允許范圍 net_charge pulp.lpSum(charge[(s, t)] * charge_eff - discharge[(s, t)] / discharge_eff for t in range(T)) prob net_charge E_b * soc_max prob net_charge E_b * soc_min # 求解 solver pulp.PULP_CBC_CMD(msgTrue) prob.solve(solver) print(求解狀態:, pulp.LpStatus[prob.status]) print(風電裝機容量 (kW):, round(P_w.varValue, 2)) print(光伏裝機容量 (kW):, round(P_pv.varValue, 2)) print(儲能額定功率 (kW):, round(P_b.varValue, 2)) print(儲能額定容量 (kWh):, round(E_b.varValue, 2))這段代碼里最需要注意的是功率平衡方程我把“缺電”和“棄電”都建模成帶懲罰的變量。這樣模型不會為了滿足約束而強行配置過大的儲能而是在經濟性和可靠性之間找到平衡。如果懲罰系數設置足夠高缺電量會趨近于 0相當于要求方案在全部場景下都滿足負荷。4.5 運行結果與驗證在命令行執行cd wind_solar_storage_planning python src/plan_model.py如果一切正常你會看到類似下面的輸出求解狀態: Optimal 風電裝機容量 (kW): 65.32 光伏裝機容量 (kW): 82.15 儲能額定功率 (kW): 21.08 儲能額定容量 (kWh): 46.35由于示例數據偏簡化具體數值會隨參數變化。你更應關注的是不同不確定性場景帶來的容量差異。如果把場景數據改成只有正常場景會發現光伏和風電的總裝機容量明顯下降一旦加入wind_low和sun_low兩個極端場景容量會上升這正是魯棒約束起作用的體現。5. 結果分析、可視化與模型擴展5.1 結果可視化為了讓結果更直觀可以把不同場景下的容量畫成柱狀圖對比。新增一個繪圖腳本# 文件路徑src/plot_result.py import matplotlib.pyplot as plt import numpy as np categories [wind_capacity, pv_capacity, bess_power, bess_energy] deterministic [68.12, 75.30, 15.20, 32.10] robust [65.32, 82.15, 21.08, 46.35] x np.arange(len(categories)) width 0.35 fig, ax plt.subplots() rects1 ax.bar(x - width/2, deterministic, width, label確定性優化) rects2 ax.bar(x width/2, robust, width, label混合魯棒優化) ax.set_ylabel(配置容量) ax.set_title(不同優化策略下的容量配置對比) ax.set_xticks(x) ax.set_xticklabels(categories) ax.legend() plt.tight_layout() plt.savefig(results/config_compare.png, dpi150)這里的數據是我為了演示寫的示例數據跑的時候應替換成你自己的求解結果。通過對比圖可以明顯看到魯棒優化結果中儲能功率和容量上升而風電或光伏的容量不一定同步上升這反映了“風光儲協同互補”的特性。5.2 如何擴展到全年 8760 小時教學示例用了 24 小時負荷曲線但工程級規劃一般要求用全年 8760 小時或至少春夏秋冬四個典型日。擴展方法是從歷史數據中提取 12 個典型日每個季節取 3 個代表日。每個典型日對應一組風光出力和負荷曲線。在模型中增加一個“時段索引”維度由原來的一維t擴展為二維(day, t)。代價是優化變量和約束數量會增長數十倍這時 PuLP 的內置 CBC 求解器可能不夠高效建議切換到 Gurobi 或 CPLEX。Pyomo 框架對商業求解器的支持更友好也更容易表達復雜約束。5.3 從魯棒優化到分布魯棒優化如果數據足夠多還能把模型擴展為分布魯棒優化。思路是不是要求所有場景都可行而是要求“在最壞的概率分布下期望成本可控”。這需要引入矩信息或 Wasserstein 距離約束模型復雜度明顯提升。對于工程項目通常先用基于場景的魯棒優化打底如果發現過于保守再考慮分布魯棒優化。6. 常見問題與排查思路實際寫代碼和調模型時大家最容易遇到下面幾類問題我把典型的錯誤現象、原因和解決方法整理成一個排查表。問題現象可能原因排查與解決思路求解狀態一直是 Infeasible約束條件互相矛盾例如負荷過高但容量上限過低先去掉缺電和棄電變量檢查單一場景下是否有解再檢查儲能SOC約束是否設置不合理結果全部為 0目標函數或懲罰項系數異常導致模型認為不配置設備最優檢查投資成本是否遠大于缺電懲罰把缺電懲罰調大再試求解時間過長場景數或時段數過多變量規模過大縮減場景數量用典型日代替滾動模擬改用商業求解器容量結果不穩定數據單位不統一例如負荷用 kW光伏容量因子卻寫成 MW 級別統一單位檢查場景文件中的因子是否在合理范圍內儲能容量偏大但功率偏小儲能能量約束太寬松或SOC上下限設置不當檢查SOC_min和SOC_max確認日凈充電量的表達是否正確目標函數數值非常大懲罰系數過大或等年值系數取值不當把成本統一換算到“每年”后比較量級如果你在求解中看到“Unbounded”狀態通常意味著模型缺少必要的上下界約束。例如投資成本為正但缺少“容量必須非負”的顯式約束。PuLP 中lowBound0已經處理了這個問題但如果你后續修改代碼時把lowBound刪掉就可能觸發。7. 工程落地與最佳實踐建議7.1 數據治理比算法更重要在真實項目中不管用隨機優化、魯棒優化還是啟發式算法最終結果都取決于輸入數據的質量。建議把風光資源數據、負荷數據、設備參數按統一格式存檔并記錄數據來源和時間范圍。比如風速數據來自氣象站還是中尺度再分析數據輻照度是水平面還是傾斜面這些細節直接影響容量因子標定結果。7.2 場景縮減與極端場景構造不確定性場景數量不是越多越好。如果歷史數據有 1000 個日場景直接全部帶入優化會讓模型規模爆炸。工程上常用兩種方法用聚類方法把場景縮減到 10~50 個代表場景。在縮減后的場景基礎上手動加入“極端場景”例如連續陰雨天、冬季大風無光日。后者是魯棒思想最直接的體現哪怕概率很低只要后果嚴重就應該參與約束。7.3 求解器選擇與性能調優小規模教學案例用 PuLP 自帶的 CBC 足夠。工程級項目建議使用 Pyomo 或 GAMS 建模方便切換求解器。求解線性規劃時優先用 Gurobi、CPLEX它們在數值穩定性和求解速度上明顯優于開源求解器。如果模型變成混合整數規劃例如需要建模儲能是否參與調峰求解難度會急劇上升應盡量保持模型線性。7.4 與電力系統仿真工具配合容量規劃模型只是第一步輸出結果后還要放到時序仿真工具中驗證。你可以用 Python 的mosaik、pandapower或者仿真軟件如 HOMER、MATLAB/Simulink對一個完整年度逐小時驗證供電可靠性。如果仿真中發現切負荷時間超標就回到優化模型中提高缺電懲罰系數或增加極端場景進行迭代。7.5 安全邊界與生產環境注意事項這個主題雖然是規劃階段問題但涉及到實際項目時仍需注意所有設備容量配置結果應乘上合理的安全裕度不能把優化值直接當作設備招標參數。儲能 SOC 管理要考慮電池壽命不能長期在 90% 以上或 10% 以下運行。涉及并網審批的微電網項目還要校核變壓器容量和并網接口線路優化模型的結果應作為電氣設計輸入而不是最終電氣設計。8. 總結與下一步學習建議這篇文章從項目痛點出發梳理了風光儲微電網容量規劃中的混合不確定性、魯棒優化和協同優化三個核心概念并給出了一個基于 PuLP 的最小可運行教學模型。你需要掌握的關鍵點可以歸納為四條第一容量規劃的本質是投資決策與運行策略的聯合優化不能把風電、光伏、儲能分開算。第二混合不確定性可以理解為“概率場景 區間邊界”的組合建模時要用多場景約束或對偶變換把不確定性轉化為可行域約束。第三魯棒優化的核心是追求方案在所有預設場景下都可行而不是只優化平均值。代價是結果更保守投資成本更高因此懲罰系數和場景選擇要仔細標定。第四工程落地時數據質量、場景縮減、求解器選擇往往比數學模型本身更影響項目成敗。下一步你可以繼續往三個方向深入學習學習 Pyomo 和 Gurobi 的組合用法構建基于 8760 小時數據的完整容量規劃模型。研究分布魯棒優化和 Wasserstein 距離解決“場景數量不夠”或“分布估計不準”的問題。把規劃模型和運行控制策略聯動例如用模型預測控制MPC驗證規劃結果在實際運行中的表現。如果你正在做類似項目建議先不追求復雜的算法而是從本文的簡化模型入手替換成自己的負荷曲線和資源數據跑通之后再逐步增加約束和場景。優化模型的建立過程本身就是加深對微電網系統理解的過程。