
說實話看到“基于粒子群算法的計及需求響應的風光儲燃柴微電網日前經濟調度研究”這種題目我腦子里立刻跳出兩個字折騰。但我說的折騰不是貶義而是這個課題本身確實能把人折騰明白。做電力系統的人都知道微電網調度看著就是個優(yōu)化問題但真要落到Matlab代碼里約束條件一多粒子群算法收斂不穩(wěn)需求響應參數調不好結果不是發(fā)散就是經濟性差得離譜。這篇博文就拿這個課題當主線把建模、代碼、調參、踩坑整個過程捋一遍希望能給正在做類似仿真或畢設的朋友一點參考。這個方向適合誰如果你是電氣工程、新能源、控制工程方向的研究生或者剛接觸微電網優(yōu)化調度的工程師那這篇文章就是給你寫的。你不一定要有很強的數學背景但最好會一點Matlab基礎知道什么是適應度函數能看懂約束條件。放心我會把粒子群算法知識、需求響應建模、日前經濟調度模型怎么在Matlab里落地一點點拆開講清楚。1. 為什么微電網日前經濟調度要“計及需求響應”1.1 微電網的電源結構與調度難點風光儲燃柴微電網這個名字拆開看就很有意思。風、光是不確定性的主力儲能在中間起緩沖燃柴機組我習慣理解為燃氣輪機和柴油發(fā)電機的組合是可調度的骨干電源。整個微電網既可以孤島運行也可以并網運行所以調度里還涉及從主網購電、向主網售電的功率交互。這種電源結構帶來的第一個問題就是“多能源耦合”。風電、光伏出力白天和晚上差異大負荷高峰又常常和光伏出力高峰錯位。儲能電池能轉移電量但是有SOC上下限、充放電功率限制和壽命成本。燃柴機組能穩(wěn)住功率平衡但啟停成本和燃料成本都不低。更麻煩的是如果不考慮需求響應那負荷曲線就是剛性的調度只能被動去追負荷。可一旦把可削減負荷、可轉移負荷放進來調度就多了一個自由度整個優(yōu)化空間瞬間變大。從數學上看這是一個典型的多變量、多約束、非線性優(yōu)化問題。常用做法是構建一個24小時的日前調度模型以系統總運行成本最小為目標函數把每個時段各機組的出力、儲能充放電功率、購售電功率、需求響應負荷調整量都當作決策變量然后求解。這個模型如果規(guī)模小用窮舉或線性規(guī)劃還能對付但一旦機組數增多、時段細化到15分鐘變量數就會爆炸這時候啟發(fā)式算法就派上用場了。1.2 需求響應改變的不只是負荷曲線需求響應是我在這個項目里覺得最有意思的部分。傳統調度里負荷是固定輸入你只能去調節(jié)機組出力來匹配它。但需求響應把一部分負荷變成了“可調資源”。比如工業(yè)用戶的可中斷負荷、商業(yè)樓的空調負荷、居民側的智能家電都可以在電價激勵或者合同約束下削減或轉移到其他時段。在日前調度模型里需求響應通常被處理成兩類可削減負荷某個時段可以直接削減一部分用電量但要支付補償成本??赊D移負荷負荷總量不變但可以把高峰時段的用電量平移到低谷時段存在轉移上限和時間窗口約束。計及需求響應之后微電網的凈負荷曲線會變得更平緩。高峰時段負荷降低燃柴機組就可以少發(fā)一些電儲能的放電壓力也小低谷時段負荷提升風電或光伏的棄電率可能下降。最后體現在成本上往往是燃料成本減少、棄風棄光減少但需求響應補償成本增加。到底值不值就是要靠優(yōu)化模型來回答的問題。我自己在仿真里看過一個很有意思的現象當需求響應補償價格設置得比較高時算法會傾向于保留更多可調負荷不削減因為省下的燃料成本可能cover不住補償成本當補償價格低時算法又會“瘋狂”削減負荷甚至把削減量逼近上限。這個閾值不是拍腦袋定的它跟系統邊際成本強相關。所以后文我會專門說需求響應成本系數怎么標定這也是新手最容易忽略的點。2. 粒子群算法為什么適合這個調度問題2.1 從混合整數規(guī)劃到粒子群的取舍剛接觸微電網調度的人可能會問這不是優(yōu)化問題嗎為什么不直接用線性規(guī)劃、混合整數線性規(guī)劃或者商業(yè)求解器確實如果你能把模型完全線性化用Yalmip加Cplex或者Gurobi速度和精度都比粒子群好很多。但問題是實際做研究的時候模型往往沒那么“乖”。比如燃柴油機的油耗特性曲線通常是非線性的可能帶有二次項甚至分段非線性儲能充放電效率也可能是非線性的需求響應模型里還經常出現邏輯變量比如一個時段能不能削減、削減到什么比例涉及0-1變量。這時候如果用混合整數非線性規(guī)劃去求解收斂性、初始解、求解時間都會成為問題。粒子群算法的優(yōu)勢其實很樸素原理簡單不用求梯度對目標函數可導性沒要求。編碼靈活連續(xù)變量和離散變量可以混在一起處理。全局搜索能力不錯不容易被局部極值困死。Matlab寫起來非??鞄资泻诵拇a就夠跑一個原型。當然它的缺點也很明顯不保證全局最優(yōu)、結果具有隨機性、約束條件處理需要技巧。所以在這個項目里我并不是完全拋棄數學規(guī)劃而是把粒子群當作一個“能快速給出可行解并方便擴展場景”的工具。尤其是要跟需求響應參數、風光出力曲線做敏感性分析時粒子群的重跑成本非常低特別適合教學和預研。2.2 粒子群算法核心流程與關鍵參數粒子群的核心思想用大白話說就是“跟著群體里最好的個體走同時保留自己的歷史經驗”。每個粒子代表一組決策變量在迭代過程中粒子會根據兩個“引力”更新自己的速度和位置個體最優(yōu)pBest這個粒子自己歷史跑到過的最優(yōu)位置。全局最優(yōu)gBest整個種群到目前為止找到的最優(yōu)位置。速度更新公式是v(i1) w * v(i) c1 * r1 * (pBest - x(i)) c2 * r2 * (gBest - x(i))位置更新公式是x(i1) x(i) v(i1)其中w是慣性權重控制粒子對之前速度的保持程度c1和c2是學習因子分別代表“向自己學習”和“向群體學習”的強度r1和r2是[0,1]之間的隨機數。在微電網調度這個場景里粒子維度一般就是所有決策變量的總和。比如24個時段每個時段有風電出力、光伏出力如果是可調度式的但通常風光是給定值、柴油機出力、燃氣輪機出力、儲能充放電功率、購售電功率、可削減負荷量、可轉移負荷量等變量數量輕松上三位數。粒子群維度一高初始化和參數設計就顯得格外重要。我常用的PSO參數范圍如下表參數常見取值范圍我的推薦值種群規(guī)模50~300120~200最大迭代次數100~1000300~500慣性權重w0.3~1.20.9線性遞減到0.4學習因子c11~31.5~2.0學習因子c21~31.5~2.0速度上限vMax決策變量范圍的10%~20%視具體變量而定為什么w要從0.9線性遞減到0.4因為迭代前期希望粒子大步探索擴大搜索范圍后期希望粒子在最優(yōu)解附近精細搜索提高收斂精度。如果你用固定w要么前期收斂太快陷入局部最優(yōu)要么后期在最優(yōu)解附近反復震蕩。3. 調度模型的目標函數與約束條件怎么搭3.1 目標函數燃料成本、運維成本、購售電成本和需求響應成本日前經濟調度說白了就是讓明天的系統運行總成本最低。一個完整的目標函數至少應該包含這幾塊燃柴機組燃料成本。柴油機和燃氣輪機的燃料成本通常用二次函數近似C_fuel sum( a_i * P_i(t)^2 b_i * P_i(t) c_i )其中a_i、b_i、c_i是機組油耗特性系數P_i(t)是機組在t時段的出力。機組運維成本。風電、光伏雖然燃料成本為零但運行維護成本不能忽略。儲能也有充放電損耗對應的維護成本。C_om sum( k_wt * P_wt(t) k_pv * P_pv(t) k_bat * (P_ch(t) P_dis(t)) )與主網交互成本。并網運行時從主網購電要花錢向主網售電可以賺錢。這里需要注意購電價和售電價往往不一樣時段不同價格也不同。C_grid sum( price_buy(t) * P_buy(t) - price_sell(t) * P_sell(t) )需求響應補償成本??上鳒p負荷和可轉移負荷都需要給用戶補償這部分成本必須納入目標函數否則算法會為了省燃料成本而無限削減負荷。C_DR sum( cost_cut(t) * P_cut(t) cost_shift(t) * P_shift(t) )目標函數綜合起來就是min C_total C_fuel C_om C_grid C_DR我在代碼里通常把目標函數寫成一個獨立的腳本方便隨時替換成本系數。比如下面這段簡化的Matlab思路function cost objFun(x, data) % x 是粒子位置包含各時段決策變量 % 先解碼得到 P_diesel, P_gas, P_ch, P_dis, P_buy, P_sell, P_cut... P_diesel x(1:24); P_gas x(25:48); % ... cost_fuel sum(data.a1 * P_diesel.^2 data.b1 * P_diesel data.c1 ... data.a2 * P_gas.^2 data.b2 * P_gas data.c2); cost_om sum(data.k_wt * data.P_wt data.k_pv * data.P_pv ... data.k_bat * (P_ch P_dis)); cost_grid sum(data.price_buy .* P_buy - data.price_sell .* P_sell); cost_dr sum(data.cost_cut .* P_cut data.cost_shift .* P_shift); cost cost_fuel cost_om cost_grid cost_dr; end3.2 約束條件功率平衡、機組出力、儲能SOC與可調負荷有了目標函數粒子群還要在約束條件圍成的可行域里搜索。約束條件寫不好粒子群就會到處飛結果完全不能用。我一般會把約束分成四類來處理。第一類是功率平衡約束。這是硬約束中的硬約束每個時段都必須滿足P_wt(t) P_pv(t) P_diesel(t) P_gas(t) P_dis(t) P_buy(t) P_load(t) - P_cut(t) - P_shift_in(t) P_shift_out(t) P_ch(t) P_sell(t)注意可轉移負荷的處理方式。如果凈負荷轉移導致本時段負荷增加那就用正項如果負荷減少就用負項。這地方不統一后面約束檢查就會亂。第二類是機組出力上下限約束。柴油機和燃氣輪機都有最小穩(wěn)定出力和最大出力限制P_diesel_min P_diesel(t) P_diesel_max P_gas_min P_gas(t) P_gas_max風電和光伏在日前調度中通常按預測值處理不作為決策變量因為它們的可調度性很差。如果你要做“風光棄電”可以額外引入棄風、棄光變量但默認情況下它們按預測出力全額消納。第三類是儲能約束。儲能不是想充就充、想放就放的它的SOC有連續(xù)性SOC(t1) SOC(t) eta_ch * P_ch(t) * dt / Cap - P_dis(t) * dt / (eta_dis * Cap)同時SOC要保持在允許范圍內比如0.2到0.9。充放電功率也有上限而且一般來說不能同時充放電這可以通過約束處理技術或者編碼方式來規(guī)避。第四類是需求響應約束??上鳒p負荷量不能超過用戶允許的削減上限而且一天內總削減量可能也有配額限制??赊D移負荷要滿足“轉移前后總用電量不變”的約束不然就是憑空創(chuàng)造電能了。在粒子群實現里我建議不要指望算法自動滿足所有約束而是要在適應度函數里加懲罰項。對于功率平衡、SOC連續(xù)性這類等式約束懲罰系數要取大一點對于不等式約束用越界量構造懲罰項就行。3.3 需求響應建模的兩種實現方式我之前看過很多論文需求響應建?;ɑ詈芏嗟畛S玫钠鋵嵕褪莾煞N。一種是基于價格彈性矩陣。把負荷分解為不同時段對電價的響應某個時段的負荷變化量是所有時段電價變化的線性組合。這種方法適合做電價型需求響應分析但需要彈性系數矩陣數據不好弄。另一種是基于激勵的可調度負荷模型。設定微電網運營商可以和用戶簽訂可中斷負荷合同每個時段最多能削減一定比例削減之后按固定單價補償。這個模型更貼近實際調度操作參數少容易編進粒子群代碼。所以我在這個項目里主要用第二種。為了防止算法“把負荷全削沒了”我建議對可削減負荷總量再加一個約束比如一天內累計削減量不超過總負荷的一定比例比如5%~15%。這個比例可以參考實際需求響應項目的參與率。如果你完全不限制優(yōu)化結果很可能出現負荷曲線被削到很低看起來成本很漂亮但實際不可行。4. Matlab代碼實現與調參實戰(zhàn)4.1 代碼總框架與數據準備我習慣把Matalb工程拆成下面幾個文件main.m主程序負責初始化參數、調用粒子群、輸出結果。objFun.m目標函數與約束懲罰函數。pso.m粒子群主循環(huán)。data_load.m加載風電、光伏、負荷、電價等曲線數據。plot_result.m繪制各機組出力曲線、SOC曲線、負荷曲線對比圖。這樣分文件的好處是后面換數據、換機組參數、改目標函數的時候不用大動干戈。數據準備這一步最容易被忽視。有些朋友拿到的風電、光伏、負荷數據都是標幺值或者歸一化后的沒有注意基準功率結果調度結果一看就離譜。我一般用實際單位功率用kW時間按1小時儲能容量用kWh電價用元/kWh。數據長度必須是24維向量對應24小時。典型數據示例% 風電預測出力 (kW) P_wt [120, 110, 100, 90, 80, 70, 100, 140, 180, 220, 250, 260, ... 240, 210, 190, 170, 150, 140, 130, 120, 110, 100, 90, 80]; % 光伏預測出力 (kW)夜間為0 P_pv [0, 0, 0, 0, 0, 0, 20, 80, 150, 220, 280, 310, ... 300, 260, 200, 130, 60, 10, 0, 0, 0, 0, 0, 0]; % 基礎負荷 (kW) P_load [200, 190, 180, 170, 160, 170, 200, 260, 310, 340, 360, 370, ... 360, 350, 340, 330, 340, 350, 360, 380, 350, 320, 280, 230];這樣的曲線雖然不一定是真實數據但足夠用來調試算法。如果你想用真實數據可以考慮把公開的微電網數據集裁剪成24維。需要注意數據量越大粒子群維度越高收斂越慢。4.2 粒子編碼、解碼和適應度函數編寫粒子編碼是整個程序的關鍵。我試過兩種方式。第一種是直接編碼所有決策變量。比如粒子維度為24×NN代表每時段的決策變量個數。這種方式直觀但維度高粒子群搜索困難。在一臺普通電腦上跑300代、120個粒子可能要等好幾分鐘。第二種是只編碼“可決策”的那部分也就是燃柴機組出力、儲能充放電功率、購售電功率、需求響應調整量。風電和光伏按預測值直接代入不參與編碼。這樣維度會低一些收斂也更快。我最終采用的是第二種核心解碼邏輯是先固定SOC初值根據功率平衡等式反推出一個“余量”然后用這個余量去調整某個變量。但這容易導致約束不滿足所以我會在解碼后做一個修正步驟確保每個時段功率平衡。適應度函數里除了目標函數值我還要加上約束越界懲罰。比如SOC越界就加一個很大的懲罰項。懲罰系數太小算法會無視約束太大又會讓目標函數變得不平滑搜索困難。我的經驗是先跑一次不帶懲罰的目標函數看看成本量級然后取懲罰系數為目標函數量級的100~1000倍。一個簡化的適應度函數框架function fitness objFun(x, data) % 解碼 ... % 計算成本 ... % 約束越界 penalty 0; if any(SOC SOC_min) || any(SOC SOC_max) penalty penalty 1e6 * sum(max(0, SOC_min - SOC) max(0, SOC - SOC_max)); end if abs(P_balance) 1e-3 penalty penalty 1e6 * sum(abs(P_balance)); end fitness totalCost penalty; end這里有個坑懲罰項的數值可能遠大于正常目標函數導致粒子群在早期只關注懲罰而忽略了成本優(yōu)化。我的做法是隨著迭代次數增加逐漸提高懲罰系數讓算法先找可行域再在經濟性上優(yōu)化。4.3 運行結果判讀收斂曲線、各機組出力計劃算法跑完不能只看一個最終成本數字還要看收斂曲線。我判斷收斂的標準是目標函數值在最后50代內基本不再下降最多在小數點后兩位內波動。如果曲線還在明顯下降說明迭代次數不夠要加大maxIter如果曲線早早平了說明種群多樣性不足可以調大w或增加種群規(guī)模。各機組的出力計劃是判斷結果是否合理的重要依據。正常情況下柴油機和燃氣輪機應該盡量保持較平穩(wěn)的出力和較低的啟停次數儲能SOC曲線應該是一條平滑的充放電曲線不會出現頻繁劇烈波動。如果SOC曲線像鋸齒一樣跳那大概率是SOC連續(xù)性約束沒處理好或者充放電功率變化太快粒子群產生了“抖動解”。需求響應結果也要重點看??上鳒p負荷一般應該集中出現在電價高峰時段可轉移負荷應該從高峰負荷段轉移到低谷段。如果算法把可削減負荷安排在凌晨低谷時段那說明需求響應成本參數或者時段約束設置有問題調度結果違背了基本邏輯。我習慣畫這樣的圖一張是基礎負荷和考慮需求響應后的凈負荷對比一張是各機組出力堆疊面積圖一張是儲能SOC曲線和充放電功率曲線。這三張圖基本能說明調度策略的全部信息。5. 常見問題與避坑經驗5.1 粒子初始化與邊界處理不當導致結果發(fā)散這是我踩過最深的一個坑。如果粒子初始化時完全隨機生成很多粒子一開始就嚴重違背功率平衡或SOC約束適應度函數里全是懲罰項粒子的“好壞”完全由懲罰項決定算法很難找到有意義的搜索方向。我的解決辦法是初始化時先生成一個“基準可行解”。怎么生成簡單粗暴的做法是讓柴油機和燃氣輪機按負荷平均值出力儲能不充不放購電功率按功率平衡反算。然后在這個基準解上加一個較小的高斯擾動生成初始種群。這樣每個粒子都離可行域不遠算法一開始就能沿著降低成本的方向搜索。邊界處理也很重要。Matlab里直接用max/min截斷位置雖然簡單但會讓很多粒子卡在邊界上。更好的做法是引入“邊界反彈”或“邊界吸收”% 越界后把速度反向避免粒子反復越界 x(i, x(i,:) xMax) 2 * xMax - x(i, x(i,:) xMax); x(i, x(i,:) xMin) 2 * xMin - x(i, x(i,:) xMin); v(i, x(i,:) xMax) -v(i, x(i,:) xMax);效果不保證所有問題都有效但至少比直接截斷要好能保留一些粒子多樣性。5.2 慣性權重與學習因子調參的實操建議粒子群的參數整定很多教程都在講“經驗公式”但你去試的時候會發(fā)現不同問題的敏感度差別很大。在這個微電網調度問題里我發(fā)現慣性權重w對儲能SOC曲線的影響最明顯。w過大粒子容易飛得過遠SOC曲線波動劇烈w過小收斂快但很容易陷入局部最優(yōu)最后柴油機出力不穩(wěn)定。我最終采用w從0.95到0.4線性遞減迭代到中間階段時一次典型運行結果已經很穩(wěn)定。c1和c2也不宜等都取2。我的實測是c1取1.8、c2取1.5時收斂速度和最終成本比較均衡。c1太大粒子會過度依賴自身歷史經驗群體信息利用不足c2太大粒子群容易過早聚集到某個局部解。你可以設定一個“弱調參流程”先固定c1c21.5調節(jié)w再固定w微調c1和c2。一次只動一個參數不然出了問題根本不知道是誰引起的。另外速度上限vMax也值得注意。如果vMax太大粒子在迭代后期還在大范圍震蕩無法精細收斂如果vMax太小粒子搜索范圍受限。我一般把每個變量的vMax設定為該變量取值范圍的15%左右比如柴油機出力范圍是100~1000kW那vMax就設135kW。5.3 需求響應參數設置不當的連鎖問題需求響應參數的“雷區(qū)”主要有三個。第一是補償成本設置過低。比如你讓可削減負荷的補償單價只有0.1元/kWh而系統邊際發(fā)電成本可能要到0.8元/kWh那算法會認為削減負荷“白賺”于是把可削減負荷全部用完凈負荷曲線被削得很夸張。這樣雖然總成本低了但需求響應行為已經完全失真。我的建議是先算一下系統平均度電成本然后讓需求響應補償單價略低于或等于邊際成本避免過度削減。第二是可削減上限設置太高。如果每個時段都允許削減50%負荷優(yōu)化結果可能讓負荷曲線變成近乎平線這其實是“用需求響應把系統重新塑形”但實際工程里用戶根本不可能削減這么多。我一般限制在10%~20%。第三是可轉移負荷的“電量守恒”約束沒做好。我見過不少代碼把可轉移負荷當成獨立變量結果總負荷憑空增加或減少了整個功率平衡模型都失去意義。這個約束必須嚴格檢查我建議在適應度函數里加一個專門懲罰項比如轉移前后總負荷變化量懲罰系數設成最大。6. 從日前調度出發(fā)還能做哪些擴展6.1 從單目標到多目標粒子群很多人在做完單目標調度之后又會想加一個“碳排放最低”或“可再生能源消納率最高”的目標。這就是多目標優(yōu)化問題了。多目標粒子群的大致思路是引入Pareto支配關系維護一個外部檔案Archive存放一組互不支配的非劣解。每次迭代更新個體最優(yōu)和全局最優(yōu)時不再用一個標量值判斷好壞而是看支配關系或者使用擁擠距離選擇。Matlab里可以自己寫也可以用一些開源工具箱。需要注意多目標問題的決策變量和約束基本不變變的只是適應度函數變成了向量以及選擇機制變了。我個人的建議是如果只是做論文驗證先跑單目標畫出各項成本的占比再在約束里把碳排放設成一個上限做單目標約束優(yōu)化比直接上多目標要穩(wěn)定得多。6.2 從離線仿真到滾動優(yōu)化日前調度本質是“開環(huán)”調度預測明天24小時的情況一次求出所有時段的計劃。但實際運行中風速、光照、負荷都會有預測誤差所以更進一步的思路是采用滾動優(yōu)化或者模型預測控制。滾動優(yōu)化的思路很簡單只執(zhí)行下一時段的調度指令等新數據更新后再重新優(yōu)化未來24小時或者未來若干小時。這樣每次都修正預測誤差。你可以把粒子群寫成一個函數在滾動窗口里反復調用。這部分改起來并不復雜但要注意每次優(yōu)化的初始SOC必須沿用上一時段的狀態(tài)否則調度結果無法銜接。我在擴展實驗里試過把日前調度改成每4小時滾動一次最終總成本會略微上升但系統運行的安全性和負荷跟蹤能力明顯提升。如果你后續(xù)要寫小論文這個方向可以作為一個對比場景。Matlab里還有一個思路是利用并行計算工具箱加速粒子群因為每個粒子的適應度計算是獨立的完全可以用parfor替代for。在種群規(guī)模200、迭代300次的情況下四核并行大約能提速2到3倍。不過要注意parfor里的隨機數序列設置如果沒設好每次并行跑的結果可能不一樣復現性很差。最后說一個我自己的體會。做這種調度仿真最怕的不是算法復雜而是“模型自嗨”。你在Matlab里跑得再漂亮也要回到現實里問一句這個削減負荷量用戶真的愿意接受嗎儲能SOC這樣變化電池壽命撐得住嗎這兩個問題想清楚了你的模型才有說服力代碼也才真正算得上完成。先別急著加更多算法改進把基礎模型的約束和參數校準到位再談擴展。我一直覺得粒子群在這個問題里只是一個工具真正值錢的是你對微電網物理過程的理解以及把這種理解轉化成代碼約束的能力。