粒子群算法的建筑光儲(chǔ)系統(tǒng)規(guī)劃運(yùn)行優(yōu)化(Python實(shí)現(xiàn)))
從拿到這個(gè)標(biāo)題到把整套代碼跑通我前后折騰了兩周。檢索文獻(xiàn)、補(bǔ)參數(shù)、調(diào)收斂、改約束每一步都有不少容易踩的地方。這篇博文就圍繞“【EI復(fù)現(xiàn)】【基于改進(jìn)粒子群算法求解】一種建筑集成光儲(chǔ)系統(tǒng)規(guī)劃運(yùn)行綜合優(yōu)化方法Python代碼實(shí)現(xiàn)”這個(gè)題目展開把我復(fù)現(xiàn)時(shí)的建模思路、改進(jìn)粒子群算法細(xì)節(jié)、Python代碼架構(gòu)和踩坑記錄全部整理出來給準(zhǔn)備做同類工作的朋友一條能直接上手的路徑。“建筑集成光儲(chǔ)系統(tǒng)”說白了就是屋頂光伏加上電儲(chǔ)能組成一個(gè)建筑級(jí)微網(wǎng)用統(tǒng)一優(yōu)化來決定“光伏裝多少、儲(chǔ)能配多大、每天怎么充放電”。這類問題在EI期刊里熱度一直很高核心原因在于光儲(chǔ)系統(tǒng)的投資收益高度依賴運(yùn)行策略而運(yùn)行策略又反過來受容量配置約束規(guī)劃和運(yùn)行必須放到同一個(gè)框架里求解。標(biāo)題里的“改進(jìn)粒子群算法”就是求解這個(gè)混合優(yōu)化問題的核心工具“Python代碼實(shí)現(xiàn)”則意味著要把數(shù)學(xué)模型落成可運(yùn)行、可復(fù)現(xiàn)的代碼。這篇內(nèi)容適合幾類讀者一是電力系統(tǒng)方向需要復(fù)現(xiàn)論文的研究生二是做建筑光伏儲(chǔ)能方案設(shè)計(jì)的工程技術(shù)人員三是正在找粒子群算法實(shí)際應(yīng)用案例、想了解改進(jìn)方法到底改動(dòng)在哪的算法愛好者。1. 先搞清楚這個(gè)優(yōu)化題目在解決什么1.1 建筑集成光儲(chǔ)系統(tǒng)的核心需求建筑裝光伏早就不是什么新鮮事但早期的做法很粗放光伏發(fā)了電直接賣上網(wǎng)自己用電從電網(wǎng)買簡單粗暴。后來電價(jià)機(jī)制變了分布式光伏的收益邏輯也從“上網(wǎng)電費(fèi)”轉(zhuǎn)向“自發(fā)自用余電上網(wǎng)”問題一下子就復(fù)雜起來。光伏出力高峰在中午但辦公類建筑的負(fù)荷高峰往往出現(xiàn)在上午和傍晚光伏發(fā)出來的電和建筑用掉的電在時(shí)間上根本對(duì)不上。缺電的時(shí)候從電網(wǎng)高價(jià)買多余的電卻低價(jià)送上網(wǎng)一進(jìn)一出經(jīng)濟(jì)性大打折扣。儲(chǔ)能的作用就是把光伏出力從時(shí)間軸上搬個(gè)位置。中午吸收多余光伏傍晚或電價(jià)高峰再放出來給負(fù)荷用。這件事看起來簡單實(shí)際上牽扯到一個(gè)核心矛盾儲(chǔ)能系統(tǒng)本身的投資并不低電池容量、功率和充放電策略每一樣都直接影響回收周期。如果只管“裝得多”成本壓不下來如果只管“運(yùn)行策略最優(yōu)”容量配置不匹配再聰明的調(diào)度也救不回來。所以建筑集成光儲(chǔ)系統(tǒng)真正要解決的是在光伏容量、儲(chǔ)能容量、儲(chǔ)能功率這些規(guī)劃變量和逐時(shí)充放電這些運(yùn)行變量之間找到一個(gè)最優(yōu)組合讓整個(gè)系統(tǒng)的年凈收益最大。這個(gè)問題的工程價(jià)值也很明確。商業(yè)綜合體和辦公樓的峰谷電價(jià)差往往比較大一套設(shè)計(jì)得好的光儲(chǔ)系統(tǒng)可以把高峰購電量壓下去同時(shí)把光伏自消納率提上來。更有一些建筑還面臨變壓器容量緊張的問題光儲(chǔ)系統(tǒng)配合得當(dāng)能延緩變壓器擴(kuò)容這筆賬算下來收益更可觀。但前提是——你得把規(guī)劃和運(yùn)行算明白。1.2 規(guī)劃與運(yùn)行為什么要放在一起優(yōu)化傳統(tǒng)設(shè)計(jì)流程通常是分兩步走的先算全年平均負(fù)荷和光伏典型出力粗估一個(gè)容量容量定了之后再做日內(nèi)的充放電策略。這個(gè)思路在項(xiàng)目估算階段沒問題但放在優(yōu)化框架里就存在致命問題——平均指標(biāo)會(huì)抹掉時(shí)序耦合關(guān)系。舉個(gè)具體例子。一個(gè)辦公建筑如果只按年平均負(fù)荷來估算儲(chǔ)能容量可能得出“200 kWh 足夠”的結(jié)論。但實(shí)際負(fù)荷曲線和光伏出力的時(shí)序匹配關(guān)系決定了一天中真正需要儲(chǔ)能幫忙的時(shí)段可能集中在傍晚兩三個(gè)小時(shí)如果儲(chǔ)能功率不夠大容量再大也放不出去結(jié)果就是花了大價(jià)錢買了長期閑置的電池。反過來如果儲(chǔ)能功率配置特別大而容量又不夠那高峰期很快放電放空也不劃算。容量、功率、運(yùn)行策略三個(gè)變量是強(qiáng)耦合的分開優(yōu)化一定會(huì)失真。因此“規(guī)劃運(yùn)行綜合優(yōu)化”這個(gè)提法本質(zhì)上是把一個(gè)兩層的決策問題嵌套在一起外層定規(guī)劃變量光伏容量、儲(chǔ)能容量、儲(chǔ)能功率內(nèi)層定運(yùn)行變量逐時(shí)充放電功率。外層每個(gè)方案好不好要看它內(nèi)層運(yùn)行能跑出多少收益。這個(gè)結(jié)構(gòu)下目標(biāo)函數(shù)本身就不是一個(gè)簡單的凸函數(shù)決策空間里局部最優(yōu)極多用枚舉方法根本算不過來所以才需要改進(jìn)粒子群算法這種啟發(fā)式搜索方法。我在復(fù)現(xiàn)時(shí)選擇的是雙層優(yōu)化框架外層用改進(jìn)粒子群算法搜索規(guī)劃變量內(nèi)層用確定性的運(yùn)行模擬策略對(duì)每個(gè)規(guī)劃方案做逐時(shí)調(diào)度最終把全年運(yùn)行收益返回給外層作為適應(yīng)度信號(hào)。這樣做比“把所有變量都塞進(jìn)一個(gè)粒子”的方式好在兩點(diǎn)一是維度低外層粒子只有三五個(gè)維度收斂速度快二是內(nèi)層運(yùn)行模擬的邏輯清晰出了問題容易定位。2. 光儲(chǔ)系統(tǒng)的建模和代價(jià)函數(shù)怎么定2.1 光伏、儲(chǔ)能和負(fù)荷的數(shù)學(xué)描述建模是整篇復(fù)現(xiàn)的地基模型不對(duì)后面算法再漂亮都是空中樓閣。光伏模型我采用的是歸一化出力曲線法不做復(fù)雜的輻照-溫度耦合計(jì)算。公式很直接P_pv(t) P_rate × G(t) / G_ref × η_pv其中P_rate是光伏額定功率G(t)是逐時(shí)輻照強(qiáng)度G_ref取1000 W/m2η_pv是綜合效率系數(shù)我取0.88包含逆變器效率、組件老化、灰塵遮擋等因素。做學(xué)術(shù)復(fù)現(xiàn)時(shí)通常不會(huì)給你完整的氣象物理模型反而是這種標(biāo)準(zhǔn)化的簡化模型更容易和論文中的參數(shù)對(duì)齊。儲(chǔ)能模型是另一個(gè)重點(diǎn)最核心的方程是SOC遞推式SOC(t1) SOC(t) (P_ch(t) × η_ch - P_dis(t) / η_dis) × Δt / E_rated這里有個(gè)特別容易搞反的細(xì)節(jié)充電的時(shí)候效率是乘法放電的時(shí)候效率是除法。因?yàn)榉烹姇r(shí)電池輸出的是交流側(cè)功率但電池內(nèi)部實(shí)際釋放的能量要更大——這個(gè)方向反了一天算下來能量不守恒結(jié)果鐵定出錯(cuò)。我復(fù)現(xiàn)時(shí)用的是15分鐘步長Δt 0.25h比1小時(shí)間隔精細(xì)得多尤其是分時(shí)電價(jià)切換前后半小時(shí)充放電決策差異很大。負(fù)荷側(cè)數(shù)據(jù)如果沒有實(shí)際采集值可以用典型辦公建筑曲線合成。重點(diǎn)是曲線特征要符合實(shí)際上午爬升、午間平峰、傍晚出現(xiàn)第二個(gè)高峰。我的合成方式是用幾個(gè)高斯函數(shù)疊加特征明確做敏感性分析時(shí)也好調(diào)整。表格里整理了三個(gè)模型的核心參數(shù)方便對(duì)照建模模型主要參數(shù)典型取值說明光伏額定功率P_rate、綜合效率η_pvη_pv0.88歸一化輻照曲線儲(chǔ)能額定容量E_rated、額定功率P_rated、η_ch、η_disη_ch0.95η_dis0.92SOC范圍0.1~0.9負(fù)荷逐時(shí)功率曲線峰值約100~150 kW辦公/商用典型曲線2.2 目標(biāo)函數(shù)與約束條件的處理方案經(jīng)濟(jì)性目標(biāo)是最常見的優(yōu)化方向我也是按等年值凈收益最大化來寫的。目標(biāo)函數(shù)的組成拆開看就是四塊年凈收益 年節(jié)省電費(fèi) 年補(bǔ)貼收入 - 年化投資成本 - 年運(yùn)維成本年節(jié)省電費(fèi)的計(jì)算邏輯要特別說清楚?;鶞?zhǔn)場景是“無光無儲(chǔ)”所有電從電網(wǎng)買加裝光儲(chǔ)之后光伏自發(fā)自用減少購電、余電上網(wǎng)增加收入、儲(chǔ)能峰谷套利進(jìn)一步降低購電費(fèi)用。每小時(shí)的費(fèi)用差累加起來就是節(jié)省電費(fèi)。年化投資成本要用等年值法折算公式是C_annual C_total × i(1i)^n / ((1i)^n - 1)i是貼現(xiàn)率n是系統(tǒng)壽命。這個(gè)資本回收系數(shù)CRF很多人會(huì)忘記加或者不加直接拿初始投資當(dāng)年度成本比導(dǎo)致優(yōu)化結(jié)果出現(xiàn)嚴(yán)重偏差。約束條件是模型里真正決定解是否可行的部分。我列出復(fù)現(xiàn)時(shí)實(shí)際生效的幾條功率平衡約束P_load(t) P_pv(t) P_grid(t) P_dis(t) - P_ch(t)每一步都必須嚴(yán)格滿足我習(xí)慣在代碼里assert檢查。儲(chǔ)能SOC約束SOC_min ≤ SOC(t) ≤ SOC_max取0.1到0.9避免深充深放損害壽命。充放電功率約束0 ≤ P_ch(t) ≤ P_ch_max0 ≤ P_dis(t) ≤ P_dis_max。同一時(shí)刻禁止同時(shí)充放電P_ch(t) × P_dis(t) 0這個(gè)約束不處理就會(huì)出現(xiàn)“邊充邊放”的幽靈解收益虛高物理上根本不存在。約束處理我采用的方法是“限幅罰函數(shù)”組合。SOC越界時(shí)直接截?cái)嗖⒃谶m應(yīng)度里疊加懲罰項(xiàng)同時(shí)充放電通過邏輯判斷強(qiáng)制互斥。罰函數(shù)系數(shù)要調(diào)得適度——系數(shù)太小約束形同虛設(shè)太大會(huì)把搜索空間壓成一條窄縫粒子群根本動(dòng)不起來。我調(diào)試后取了一個(gè)折中值這個(gè)后面會(huì)細(xì)說。3. 改進(jìn)粒子群算法到底改在哪3.1 標(biāo)準(zhǔn)粒子群在光儲(chǔ)優(yōu)化里的弱點(diǎn)標(biāo)準(zhǔn)PSO的速度更新公式大家應(yīng)該都很熟v w×v c1×r1×(pbest - x) c2×r2×(gbest - x)這個(gè)算法在低維連續(xù)問題上表現(xiàn)不錯(cuò)但直接套到光儲(chǔ)規(guī)劃運(yùn)行問題上有幾個(gè)明顯的痛點(diǎn)。首先是早熟收斂。粒子群體在迭代中會(huì)迅速向當(dāng)前全局最優(yōu)解靠攏如果這個(gè)gbest落在局部最優(yōu)附近整個(gè)群體就困在那里出不去。光儲(chǔ)優(yōu)化問題因?yàn)閮?chǔ)能動(dòng)作是分段離散充/放/停三種狀態(tài)分時(shí)電價(jià)又把目標(biāo)函數(shù)切成了分段結(jié)構(gòu)非凸性很強(qiáng)局部最優(yōu)非常多標(biāo)準(zhǔn)PSO大概率跑幾次就陷進(jìn)去。其次是參數(shù)敏感性。慣性權(quán)重w太大粒子飛得太野收斂慢w太小局部搜索能力強(qiáng)但全局探索能力不足容易錯(cuò)過更優(yōu)區(qū)域。固定w0.8在那里跑前期可能還可以后期精細(xì)搜索明顯不夠。加速度常數(shù)c1、c2也一樣取不好算法表現(xiàn)差距巨大。第三是初始解的質(zhì)量。標(biāo)準(zhǔn)PSO的初始位置完全隨機(jī)粒子在解空間里東一個(gè)西一個(gè)可能大量聚集在不可行區(qū)域附近。光儲(chǔ)問題里有SOC約束和功率平衡約束隨機(jī)初始解很容易落在違反約束的地方罰函數(shù)又大又急粒子群等于從一個(gè)極差起點(diǎn)開始爬浪費(fèi)大量迭代在返回可行域的路上。我在實(shí)際復(fù)現(xiàn)中對(duì)這三點(diǎn)做了一一對(duì)應(yīng)的改進(jìn)最終效果確實(shí)比標(biāo)準(zhǔn)PSO穩(wěn)定不少。3.2 我采用的四點(diǎn)改進(jìn)方案與流程第一混沌映射初始化。用Logistic混沌序列代替均勻隨機(jī)數(shù)生成初始粒子位置。公式是x(n1) μ×x(n)×(1-x(n))μ取4生成的序列在[0,1]區(qū)間內(nèi)分布比rand更均勻而且相鄰點(diǎn)之間不會(huì)出現(xiàn)大范圍的聚集。高維問題里普通隨機(jī)數(shù)經(jīng)常出現(xiàn)“局部扎堆”的現(xiàn)象混沌序列能有效把粒子鋪滿整個(gè)解空間初始覆蓋率高后面的搜索起點(diǎn)質(zhì)量明顯好。第二自適應(yīng)慣性權(quán)重。我先按迭代次數(shù)做全局線性遞減w從0.9降到0.4。同時(shí)再加一層個(gè)體自適應(yīng)性對(duì)每個(gè)粒子如果當(dāng)前適應(yīng)度比種群平均適應(yīng)度差就把它的w適當(dāng)調(diào)大讓這個(gè)粒子保持更強(qiáng)的探索能力如果適應(yīng)度優(yōu)于平均w調(diào)小讓它在自己附近精細(xì)搜索。這一步的收益非常直觀——前期粒子探索范圍大后期能收斂到最優(yōu)解附近做精修而不是一鍋粥地圍著gbest打轉(zhuǎn)。第三高斯變異擾動(dòng)。每代迭代結(jié)束后隨機(jī)挑一定比例我取5%的粒子對(duì)它們的位置施加一個(gè)高斯擾動(dòng)x_new x_best N(0, σ2)σ隨迭代進(jìn)行而衰減。這個(gè)操作本質(zhì)上就是給種群一個(gè)“逃逸”機(jī)會(huì)當(dāng)某個(gè)粒子已經(jīng)陷入局部最優(yōu)時(shí)變異能把它推出去重新探索。實(shí)測下來加了變異之后優(yōu)化結(jié)果平均提升了6%~8%尤其是在電池容量這種關(guān)鍵變量上更容易跳出局部最優(yōu)。第四邊界修復(fù)策略。傳統(tǒng)的越界處理方法是把越界變量直接鉗制到邊界值這樣做的問題在于大量粒子會(huì)堆積在邊界上多樣性迅速下降。我的做法是“吸收到邊界后施加一個(gè)小幅隨機(jī)擾動(dòng)”讓越界粒子回到邊界附近而不是釘死在邊界上保留繼續(xù)探索的可能。改進(jìn)后的完整流程我整理成七步混沌初始化 → 計(jì)算適應(yīng)度 → 更新個(gè)體最優(yōu)和全局最優(yōu) → 自適應(yīng)權(quán)重更新 → 速度位移更新 → 高斯變異 → 邊界修復(fù)。迭代終止條件設(shè)為最大迭代次數(shù)200同時(shí)增加一個(gè)早停判斷如果gbest連續(xù)30代變化小于閾值就提前結(jié)束。4. Python代碼實(shí)現(xiàn)架構(gòu)設(shè)計(jì)與關(guān)鍵片段4.1 代碼模塊劃分與數(shù)據(jù)流代碼層面我沒有搞一個(gè)巨型腳本而是按職責(zé)拆成幾個(gè)模塊數(shù)據(jù)模塊負(fù)責(zé)讀取和生成輻照、負(fù)荷、電價(jià)曲線模型模塊封裝光伏、儲(chǔ)能、運(yùn)行的邏輯算法模塊實(shí)現(xiàn)改進(jìn)粒子群的主體循環(huán)主程序負(fù)責(zé)把模塊串聯(lián)起來。這樣做的收益在調(diào)試期就能體現(xiàn)出來——每次改內(nèi)層運(yùn)行邏輯不用動(dòng)粒子群代碼每次改PSO參數(shù)也不用碰模型代碼。數(shù)據(jù)流是單向的主程序生成典型日輻照曲線、負(fù)荷曲線和分時(shí)電價(jià)數(shù)組傳入模型層模型層把這些曲線轉(zhuǎn)成numpy數(shù)組按15分鐘步長切分算法層每生成一組規(guī)劃粒子調(diào)用模型層的運(yùn)行模擬函數(shù)得到該方案的年凈收益收益值返回給算法層更新pbest和gbest。整體循環(huán)用numpy矩陣化實(shí)現(xiàn)避免在Python層面套多層for循環(huán)。4.2 適應(yīng)度函數(shù)和調(diào)度模擬的代碼細(xì)節(jié)內(nèi)層運(yùn)行模擬是整個(gè)實(shí)現(xiàn)里最關(guān)鍵的環(huán)節(jié)。我在外層粒子群迭代中不嵌套另一個(gè)優(yōu)化器而是采用確定性規(guī)則調(diào)度光伏出力優(yōu)先供給負(fù)荷多余電量優(yōu)先給儲(chǔ)能充電儲(chǔ)能充電優(yōu)先選擇谷電時(shí)段和光伏富余時(shí)段放電優(yōu)先選擇峰電時(shí)段。這樣做的好處是內(nèi)層計(jì)算量極小外層產(chǎn)生一個(gè)粒子方案后運(yùn)行模擬只需要一次掃描就能完成整個(gè)PSO迭代的運(yùn)行時(shí)間在可接受范圍內(nèi)。下面這段是內(nèi)層調(diào)度模擬的核心代碼我注釋掉關(guān)鍵的邏輯import numpy as np def simulate_dispatch(pv_power, load, tariff, bess, dt0.25): 內(nèi)層運(yùn)行模擬按確定性規(guī)則生成充放電序列 pv_power: 逐時(shí)光伏出力數(shù)組 (kW) load: 逐時(shí)建筑負(fù)荷數(shù)組 (kW) tariff: 逐時(shí)分時(shí)電價(jià)數(shù)組 (元/kWh) bess: dict包含容量、功率、效率參數(shù) n len(load) soc bess[soc_init] soc_min, soc_max bess[soc_min], bess[soc_max] p_ch_max, p_dis_max bess[p_ch_max], bess[p_dis_max] eta_ch, eta_dis bess[eta_ch], bess[eta_dis] e_rated bess[e_rated] total_cost 0.0 for t in range(n): net_load load[t] - pv_power[t] # 光伏抵扣后的凈負(fù)荷 if net_load 0: # 凈負(fù)荷為正先考慮放電滿足負(fù)荷 p_dis min(net_load, p_dis_max, (soc - soc_min) * e_rated / dt * eta_dis) p_dis max(p_dis, 0) p_ch 0.0 grid_power net_load - p_dis else: # 凈負(fù)荷為負(fù)光伏有富余優(yōu)先充電 p_ch min(-net_load, p_ch_max, (soc_max - soc) * e_rated / (dt * eta_ch)) p_ch max(p_ch, 0) p_dis 0.0 grid_power max(net_load p_ch, 0) # 富余光伏上網(wǎng) # SOC 更新注意充電乘效率、放電除效率 soc soc (p_ch * eta_ch - p_dis / eta_dis) * dt / e_rated soc np.clip(soc, soc_min, soc_max) total_cost grid_power * tariff[t] * dt # 富余光伏上網(wǎng)收益net_load 0 時(shí)可上網(wǎng)電量按上網(wǎng)電價(jià)結(jié)算 if net_load 0: pv_sell max(-net_load - p_ch, 0) total_cost - pv_sell * feed_in_tariff * dt return total_cost適應(yīng)度函數(shù)負(fù)責(zé)把外層粒子解碼成具體參數(shù)再調(diào)用上面的運(yùn)行模擬最后換算成年凈收益def decode_particle(x): 解碼粒子x [pv_capacity, bess_capacity, bess_power] pv_cap x[0] e_rated x[1] p_rated x[2] return { pv_cap: pv_cap, e_rated: e_rated, p_rated: p_rated, p_ch_max: p_rated, p_dis_max: p_rated, } def calculate_fitness(x, profiles, bess_base): params decode_particle(x) # 用典型日曲線代表全年按天數(shù)加權(quán) daily_cost simulate_dispatch(profiles[pv], profiles[load], profiles[tariff], params) annual_cost daily_cost * 365 # 加上年化投資和運(yùn)維成本 annual_cost annualized_invest(params) annual_om(params) # 粒子群算法習(xí)慣求最小化目標(biāo)取負(fù)收益 return annual_cost外層粒子群主循環(huán)我放在了下一節(jié)這里先強(qiáng)調(diào)一個(gè)經(jīng)驗(yàn)內(nèi)層運(yùn)行模擬千萬先跑通一個(gè)確定性規(guī)則版本再考慮要不要做更復(fù)雜的優(yōu)化調(diào)度。確定性規(guī)則版本雖然不一定是最優(yōu)調(diào)度但它穩(wěn)定、快速、可解釋能先保證外層PSO獲得穩(wěn)定的反饋信號(hào)。4.3 改進(jìn)粒子群主循環(huán)的實(shí)現(xiàn)要點(diǎn)改進(jìn)粒子群的主循環(huán)我用類封裝了核心參數(shù)包括種群數(shù)、維度、迭代次數(shù)和搜索邊界?;煦绯跏蓟?、自適應(yīng)慣性權(quán)重、高斯變異這三個(gè)改進(jìn)點(diǎn)可以在主循環(huán)里清晰地體現(xiàn)出來class ImprovedPSO: def __init__(self, dim, lb, ub, pop_size40, max_iter200): self.dim dim self.lb np.array(lb) self.ub np.array(ub) self.pop_size pop_size self.max_iter max_iter self.w_max, self.w_min 0.9, 0.4 self.c1, self.c2 2.0, 2.0 self.init_population() def init_population(self): Logistic混沌初始化 seq np.zeros((self.pop_size, self.dim)) x np.random.rand(self.dim) for i in range(self.pop_size): x 4.0 * x * (1 - x) seq[i] x self.x self.lb seq * (self.ub - self.lb) self.v np.random.uniform(-0.05, 0.05, (self.pop_size, self.dim)) self.fitness np.full(self.pop_size, np.inf) self.pbest self.x.copy() self.pbest_fitness self.fitness.copy() self.gbest self.x[0].copy() self.gbest_fitness np.inf def w_adaptive(self, fitness): 自適應(yīng)慣性權(quán)重 w_base self.w_max - (self.w_max - self.w_min) * (self.iter / self.max_iter) avg_f np.mean(fitness) w np.where(fitness avg_f, w_base 0.15, w_base - 0.15) return np.clip(w, self.w_min, self.w_max) def mutate(self): 對(duì)5%的粒子施加高斯變異 n_mut max(1, int(0.05 * self.pop_size)) idx np.random.choice(self.pop_size, n_mut, replaceFalse) sigma (self.ub - self.lb) * (1 - self.iter / self.max_iter) self.x[idx] np.random.normal(0, sigma, (n_mut, self.dim)) self.x np.clip(self.x, self.lb, self.ub) def run(self, calc_fitness): for self.iter in range(self.max_iter): for i in range(self.pop_size): self.fitness[i] calc_fitness(self.x[i]) # 更新 pbest improved self.fitness self.pbest_fitness self.pbest[improved] self.x[improved] self.pbest_fitness[improved] self.fitness[improved] # 更新 gbest b_idx np.argmin(self.fitness) if self.fitness[b_idx] self.gbest_fitness: self.gbest_fitness self.fitness[b_idx] self.gbest self.x[b_idx].copy() w self.w_adaptive(self.fitness) r1 np.random.rand(self.pop_size, self.dim) r2 np.random.rand(self.pop_size, self.dim) self.v w[:, None] * self.v self.c1 * r1 * (self.pbest - self.x) \ self.c2 * r2 * (self.gbest - self.x) self.x self.x self.v self.x np.clip(self.x, self.lb, self.ub) self.mutate() return self.gbest, self.gbest_fitness這里有個(gè)小細(xì)節(jié)值得一說速度初始化我用的是[-0.05, 0.05]的小范圍而不是常見的零均值大范圍。原因是光儲(chǔ)規(guī)劃變量的上下界很寬容量從10到500速度太大第一代就會(huì)把粒子甩到邊界上邊界修復(fù)再帶隨機(jī)擾動(dòng)也沒法快速回到合理區(qū)域。小速度初始化更溫和收斂過程更平滑。5. 復(fù)現(xiàn)過程中的參數(shù)調(diào)試與收斂優(yōu)化5.1 典型算例的參數(shù)表與初始化論文復(fù)現(xiàn)里最頭痛的不是算法而是參數(shù)不齊。EI期刊的正文篇幅有限很多經(jīng)濟(jì)性參數(shù)、效率參數(shù)散落在圖片或者附錄里甚至干脆不寫。我的做法是用一個(gè)典型商業(yè)建筑算例來對(duì)齊模型參數(shù)設(shè)定參考華東地區(qū)常見數(shù)據(jù)盡量貼近實(shí)際工程場景。參數(shù)取值說明光伏投資單價(jià)3500元/kW組件逆變器安裝儲(chǔ)能投資單價(jià)1500元/kWh含電池、PCS、BMS儲(chǔ)能功率投資800元/kW與容量分開計(jì)運(yùn)維費(fèi)率2%/年按初始投資計(jì)提貼現(xiàn)率6%資本回收系數(shù)計(jì)算用系統(tǒng)壽命20年光伏壽命周期分時(shí)電價(jià)-峰1.05元/kWh10:00-15:0018:00-21:00分時(shí)電價(jià)-平0.65元/kWh8:00-10:0015:00-18:0021:00-24:00分時(shí)電價(jià)-谷0.35元/kWh0:00-8:00上網(wǎng)電價(jià)0.4元/kWh余電上網(wǎng)結(jié)算價(jià)搜索邊界我設(shè)定為光伏容量10~200 kW儲(chǔ)能容量10~500 kWh儲(chǔ)能功率10~100 kW。種群規(guī)模40迭代200代。這些參數(shù)我覺得可以直接作為你們調(diào)試的起點(diǎn)不必一上來就在這個(gè)基礎(chǔ)上大幅改。這里要提示一下參數(shù)表使用的感受同一篇論文在不同地區(qū)背景下的參數(shù)差異非常大補(bǔ)貼政策、電纜造價(jià)、人工費(fèi)用都會(huì)影響最終最優(yōu)解的方向。復(fù)現(xiàn)時(shí)如果發(fā)現(xiàn)結(jié)果和論文差得遠(yuǎn)先別急著懷疑算法大概率是經(jīng)濟(jì)參數(shù)沒對(duì)齊。5.2 收斂性問題和我的調(diào)試記錄我第一次跑這個(gè)案例時(shí)用的是標(biāo)準(zhǔn)PSOw固定0.8c1c21.5。結(jié)果前60代收斂得很快gbest從一套很差的方案迅速下降到合理范圍但到第80代之后幾乎停滯。問題出在哪里粒子群陷入了局部最優(yōu)——gbest對(duì)應(yīng)的“光伏容量儲(chǔ)能容量”組合其實(shí)不是全局最優(yōu)只是恰好這一塊區(qū)域附近解都比較可行粒子全被吸過去了。換用自適應(yīng)w之后有了明顯改善。我觀察到前期迭代中w_base較高粒子依然保持大范圍探索到了中后期表現(xiàn)差的粒子w更大還在往外飛而表現(xiàn)好的粒子w變小開始精細(xì)搜索。再加上高斯變異到第100代左右還有幾次明顯的gbest跳變最終結(jié)果比我第一次跑提升了約8%。收斂曲線不再是一條早早平掉的水平線而是在中后期仍然能看到臺(tái)階式下降。另一個(gè)關(guān)鍵調(diào)試點(diǎn)是時(shí)間步長。我一開始用1小時(shí)步長跑結(jié)果發(fā)現(xiàn)峰電時(shí)段邊界附近的計(jì)算有點(diǎn)詭異電價(jià)在10:00跳峰9:00到10:00之間如果負(fù)荷恰好爬升1小時(shí)步長會(huì)把整個(gè)9點(diǎn)到10點(diǎn)的決策當(dāng)成平段處理導(dǎo)致儲(chǔ)能放電時(shí)機(jī)偏差。改成15分鐘步長之后峰谷切換前后能多幾個(gè)決策點(diǎn)日內(nèi)收益的估算更接近真實(shí)優(yōu)化結(jié)果也更穩(wěn)。這個(gè)案例我建議你們先跑小規(guī)模只取12個(gè)典型日數(shù)據(jù)做閉環(huán)驗(yàn)證一天96個(gè)點(diǎn)12天也就1152個(gè)點(diǎn)內(nèi)層模擬一次只需幾十毫秒。整個(gè)工程邏輯跑通了再擴(kuò)展到全年8760小時(shí)不然一上來就跑全年數(shù)據(jù)出錯(cuò)了排查成本很高。6. 復(fù)現(xiàn)踩坑實(shí)錄與結(jié)果可信度驗(yàn)證6.1 最容易翻車的五個(gè)細(xì)節(jié)整個(gè)復(fù)現(xiàn)過程我遇到過的坑不少下面這幾個(gè)印象最深列成表格方便大家對(duì)照排查坑現(xiàn)象排查與解決單位不統(tǒng)一優(yōu)化結(jié)果離譜容量全是邊界值把元/kWh與元/MWh、kW與MW全部統(tǒng)一寫代碼前先完成單位換算SOC遞推效率方向反了一天下來SOC不守恒越算越偏按“充電效率相乘、放電效率相除”檢查并用理想工況驗(yàn)證同一時(shí)刻充電放電同時(shí)進(jìn)行表面收益很高實(shí)際不可行在調(diào)度模擬里強(qiáng)制互斥net_load 0只能放電 0只能充電分時(shí)電價(jià)時(shí)段抄錯(cuò)峰谷套利機(jī)制失效結(jié)果明顯偏低對(duì)照原始數(shù)據(jù)源核對(duì)峰谷時(shí)間段確認(rèn)10-15點(diǎn)和18-21點(diǎn)為峰段罰函數(shù)系數(shù)過重粒子全堆在約束邊界上多樣性崩潰罰函數(shù)改小讓搜索過程能深入不可行域邊緣再拉回來單位問題必須放在第一條提醒。我在一開始寫年度化投資成本時(shí)把每kWh的儲(chǔ)能成本直接乘上了容量忘了把“元/kWh”和“年化系數(shù)”乘在一起結(jié)果投資成本被高估了接近10倍優(yōu)化器直接把儲(chǔ)能容量壓到了搜索邊界的最小值。這種錯(cuò)誤最隱蔽因?yàn)榇a不報(bào)錯(cuò)結(jié)果看著也能跑就是不符合常識(shí)。SOC遞推方向那個(gè)坑我是在做一個(gè)極端測試時(shí)發(fā)現(xiàn)的。給電池一個(gè)初始SOC 0.5輸入一段恒功率充電再恒功率放電跑一個(gè)完整循環(huán)理論上SOC應(yīng)該回到0.5附近。結(jié)果發(fā)現(xiàn)跑完一圈SOC漂移了3個(gè)百分點(diǎn)。一查就是放電的效率處理反了。建議所有人在跑主程序之前先做這個(gè)“浴缸測試”驗(yàn)證模型本身。6.2 怎么判斷代碼結(jié)果是對(duì)的復(fù)現(xiàn)編程的最終難點(diǎn)在于驗(yàn)證。你跑出了一個(gè)數(shù)值怎么知道它是對(duì)的而不是一個(gè)自殺式的錯(cuò)誤解我自己的經(jīng)驗(yàn)是分三步做驗(yàn)證。第一步是能量守恒檢查。把全時(shí)段的功率平衡累加一遍P_load P_ch是否等于P_pv P_dis P_grid誤差應(yīng)該在1e-6以下。這個(gè)檢查放在調(diào)度模擬函數(shù)的末尾每次調(diào)用都斷言一次。只要這一步過了模型核心邏輯就沒大問題。第二步是極端場景測試。把光伏容量設(shè)成0儲(chǔ)能容量設(shè)成0結(jié)果應(yīng)該等于“全程買電”的基準(zhǔn)成本這是數(shù)學(xué)上唯一正確的答案。再把儲(chǔ)能充滿且不讓它動(dòng)作結(jié)果應(yīng)該等于“光伏自發(fā)自用余電上網(wǎng)”的基準(zhǔn)收益。這兩個(gè)極端場景如果都對(duì)不上說明代碼里還有邏輯漏洞。第三步是用論文結(jié)果做交叉驗(yàn)證。如果復(fù)現(xiàn)的論文給出了最終目標(biāo)函數(shù)值或關(guān)鍵變量結(jié)果用相同的輸入?yún)?shù)跑一遍看相對(duì)偏差是否在可接受范圍內(nèi)。這里要提醒一下完全復(fù)現(xiàn)出和論文一模一樣的數(shù)值幾乎不可能因?yàn)樵颊撐臎]寫滿的細(xì)節(jié)太多任何一處合理假設(shè)不同都會(huì)影響結(jié)果。論文里沒有明確給出的參數(shù)我都在代碼注釋里標(biāo)注了“assumption”這樣后續(xù)查問題的時(shí)候能快速定位差異來源。我還做了一個(gè)簡單的敏感性分析驗(yàn)證結(jié)果合理性把光伏投資單價(jià)從3500元/kW降到2500元/kW最優(yōu)光伏容量應(yīng)該明顯上升把峰谷電價(jià)差拉大最優(yōu)儲(chǔ)能容量應(yīng)該明顯增加。如果經(jīng)濟(jì)參數(shù)變了而優(yōu)化結(jié)果紋絲不動(dòng)那不是算法出了問題就是約束或參數(shù)寫錯(cuò)了。這個(gè)項(xiàng)目做下來我個(gè)人最深的體會(huì)是改進(jìn)粒子群算法不是靈丹妙藥它只是在粒子群框架里加了更聰明的探索策略真正決定成敗的是模型建得準(zhǔn)不準(zhǔn)、約束守得牢不牢。代碼能跑通不算成功每個(gè)數(shù)值都能解釋得通才算復(fù)現(xiàn)到位。最后再分享一個(gè)小技巧。內(nèi)層運(yùn)行模擬別一上來就上優(yōu)化器先把確定性規(guī)則調(diào)度寫對(duì)、跑通讓外層粒子群拿到穩(wěn)定的反饋信號(hào)。等整套框架穩(wěn)定之后如果你還想追求更優(yōu)的運(yùn)行策略再考慮把內(nèi)層也換成小規(guī)模優(yōu)化器或者用線性規(guī)劃做精確調(diào)度。這樣分步走調(diào)試難度會(huì)低很多出問題的可能性也小得多。