化的電動汽車調(diào)度MATLAB實現(xiàn)與案例解析)
1. 雙層優(yōu)化到底在解決什么問題1.1 為什么單層優(yōu)化搞不定電動汽車調(diào)度先說結(jié)論電動汽車調(diào)度本質(zhì)上是一筆有兩方參與的賬單層優(yōu)化只能算清楚一方的利益算不清楚另一方的。很多人第一次接觸這個課題會拿一個經(jīng)典的單層優(yōu)化模型去套比如以系統(tǒng)總成本最小為目標把電動汽車的充放電功率當決策變量約束加上電池容量、充放電功率上下限、電網(wǎng)負荷平衡跑一個線性規(guī)劃或者混合整數(shù)規(guī)劃就收工了。這種做法在課堂作業(yè)里能交差但放到真實場景里會有一個繞不過去的矛盾你優(yōu)化的對象其實分屬不同的利益主體而它們的目標根本不是一回事。電網(wǎng)側(cè)想要的是削峰填谷、平抑波動最好電動汽車在低谷期充電、高峰期放電但車主側(cè)想要的是充電費用最低、電池壽命損耗最小最好在電價最低的時候充滿在電價最高的時候多放電賺錢。這兩個目標有時候是一致的但更多時候是沖突的。你用單層模型一桿子優(yōu)化到底本質(zhì)上是假設電網(wǎng)說了算車主完全聽話這在現(xiàn)實中不成立。所以近幾年學術(shù)圈和工程圈都在推雙層優(yōu)化Bi-level Optimization核心思想就是把決策拆成兩層上層是領(lǐng)導者下層是跟隨者各自有自己的目標函數(shù)和約束下層對上層給出的策略做出最優(yōu)響應上層在預測到這種響應之后再來優(yōu)化自己的決策。這個結(jié)構(gòu)用生活化的話說就像物業(yè)公司在制定小區(qū)停車收費標準它得先想到車主會怎么反應——收費高了大家不停收費低了車位不夠。物業(yè)是領(lǐng)導者車主是跟隨者物業(yè)管理費的標準是在預測車主行為的基礎(chǔ)上定的。電動汽車調(diào)度里的電網(wǎng)公司和聚合商、聚合商和車主、充電站和用戶全都是這種遞階決策關(guān)系。1.2 雙層優(yōu)化的基本數(shù)學結(jié)構(gòu)標準形式的雙層優(yōu)化可以寫成上層Leader min F(x, y) s.t. G(x, y) ≤ 0其中 y 是下層問題的最優(yōu)解下層Follower min f(x, y) s.t. g(x, y) ≤ 0這里的核心難點在于上層優(yōu)化的約束條件里含有一個“下層問題的最優(yōu)解 y”它不是普通決策變量而是下層優(yōu)化問題的輸出。只要下層問題有唯一解上層還能處理如果下層問題有多解問題就變成病態(tài)的了。我在實際項目里處理電動汽車調(diào)度時通常會讓下層問題是一個嚴格凸的二次規(guī)劃這樣能保證唯一解避免一堆理論麻煩。從算法角度求解雙層優(yōu)化主要有三類路線。第一類是極值點搜索法利用線性雙層規(guī)劃的最優(yōu)解一定在約束多面體的某個極點這一性質(zhì)去查點適合小規(guī)模問題。第二類是罰函數(shù)法把下層問題的KKT條件作為約束加入上層把雙層問題轉(zhuǎn)成單層帶互補約束的數(shù)學規(guī)劃也就是MPECMathematical Program with Equilibrium Constraints這個在MATLAB里用fmincon配合一些處理是可以做的但互補約束會帶來數(shù)值困難。第三類是智能算法嵌套外層用遺傳算法或粒子群搜索領(lǐng)導者的決策內(nèi)層用成熟的QP求解器解跟隨者的優(yōu)化問題這種方案在工程上最省事我對初學者也最推薦。1.3 本博文涉及的MATLAB代碼研究內(nèi)容我今天想分享的這套MATLAB代碼研究就是圍繞“基于雙層優(yōu)化的電動汽車優(yōu)化調(diào)度”這個題目展開的。它把上層設定為電網(wǎng)或充電站運營商目標是最小化配電網(wǎng)的負荷峰谷差或者系統(tǒng)運行成本下層設定為電動汽車聚合商目標是在滿足用戶充電需求的前提下最小化充電費用同時考慮電池退化成本。兩層之間通過充電電價信號來互動——上層制定分時電價下層根據(jù)電價優(yōu)化充電計劃上層再根據(jù)下層的充電計劃評估負荷曲線形成完整的閉環(huán)。這套研究代碼的核心輸出包括雙層迭代收斂曲線、優(yōu)化前后的負荷曲線對比、各輛電動汽車的充放電計劃、分時電價策略、以及不同場景下的敏感性分析。我會在下文逐步拆解它的模型搭建、MATLAB實現(xiàn)方法、實操過程和避坑經(jīng)驗盡量做到拿來就能跑、跑完能看懂、看懂能改寫成自己的版本。2. 模型設計與參數(shù)設置的關(guān)鍵決策2.1 上層配電網(wǎng)運營商的優(yōu)化目標與約束我習慣把上層模型設置成配電網(wǎng)運營商。為什么不讓它直接是電網(wǎng)公司因為配電網(wǎng)運營商更貼近電動汽車接入的10kV饋線層面能體現(xiàn)局部負荷的峰谷問題數(shù)據(jù)也好構(gòu)造。上層目標函數(shù)我建議用兩個指標做成加權(quán)和一個是負荷峰谷差最小化一個是系統(tǒng)總運行成本最小化。這樣既能體現(xiàn)電網(wǎng)側(cè)的調(diào)峰訴求又能照顧經(jīng)濟性。具體地設調(diào)度時段為24小時步長1小時一共T24個時段。第t個時段的常規(guī)負荷為P_base(t)電動汽車充電總功率為P_ev(t)則配電網(wǎng)凈負荷為P_net(t) P_base(t) P_ev(t)。上層目標函數(shù)可以寫成min F w1 * max(P_net) - min(P_net) w2 * sum(c_buy(t) * P_net(t))其中c_buy(t)是電網(wǎng)向配電網(wǎng)售電的分時電價。w1和w2是權(quán)重系數(shù)我常用的組合是0.7和0.3把峰谷差放在首位運行成本放在次位。如果你側(cè)重經(jīng)濟性可以把權(quán)重反過來。上層的約束包括充電站總功率上限各時段配電網(wǎng)功率不能超過變壓器容量每個時段電價的變化范圍一般控制在基準電價的0.7到1.3倍還有電價平滑約束防止電價在相鄰時段劇烈跳變導致用戶反感一般限制相鄰時段電價差不超過0.2元/千瓦時。這些約束在MATLAB里都很好處理一會兒會講具體寫法。2.2 下層電動汽車聚合商的充電優(yōu)化模型下層模型是電動汽車聚合商它管理著一批電動汽車目標是在滿足用戶充電需求的前提下最小化總充電費用加上電池退化成本。這里有一個常見的選擇讓聚合商同時優(yōu)化每輛車的充放電功率還是只優(yōu)化充電不放電我建議分為兩個版本。基礎(chǔ)版只充電不放電代碼簡單、收斂快適合教學進階版允許車輛在高峰時段放電給電網(wǎng)也就是V2G雖然模型復雜一些但更能體現(xiàn)雙層優(yōu)化的價值也更能發(fā)論文。每輛電動汽車的核心參數(shù)包括電池容量E_cap千瓦時、初始電量SOC_init、目標電量SOC_target、最大充電功率P_ch_max、最大放電功率P_dis_max、充電效率eta_ch、放電效率eta_dis、接入時間和離開時間。聚合商的決策變量是每輛車在每個時段的充電功率和放電功率目標函數(shù)是min f sum_t sum_i [ price(t) * P_ch_i(t) / eta_ch - price(t) * P_dis_i(t) * eta_dis beta * (SOC_i(t) - SOC_i(t-1))^2 ]其中最后一項是電池退化懲罰項用SOC變化量的平方來近似電池循環(huán)壽命損耗。beta需要標定我通常取0.01到0.05之間太大的話車輛會懶于響應電價變化太小的話退化成本可以忽略起不到限制作用。約束條件包括每個時段的功率上下限SOC的動態(tài)方程SOC_i(t1) SOC_i(t) (P_ch_i(t) * eta_ch - P_dis_i(t) / eta_dis) * dt / E_capSOC的上下限比如0.15到0.95離開時必須達到目標SOC以及同一時段不能同時充放電的約束——這個約束在MATLAB里可以用二進制變量來處理但如果你用的是純連續(xù)變量求解器就需要加一個小的懲罰項或者直接把充放電合并成一個決策變量符號為正代表充電為負代表放電這樣省去二進制變量求解速度會快很多。2.3 雙層之間的利益交互與電價傳遞機制兩層模型的銜接靠的就是電價。上層運營商制定24小時的分時電價下層聚合商拿到電價后求解充電計劃然后把每時段的充電功率返回給上層上層再評估負荷曲線并調(diào)整電價。這個交互過程非常像市場里的“報價-響應-再報價”。但這里有個細節(jié)下層聚合商求解出來的充電計劃本質(zhì)上是對上層電價的“最優(yōu)反應函數(shù)”。上層不需要知道每輛車的具體參數(shù)它只需要知道“當電價是這樣一個向量時總充電功率會變成那樣一個向量”。所以我在代碼實現(xiàn)時會在上層迭代里反復調(diào)用下層求解器這種嵌套結(jié)構(gòu)也叫迭代雙層優(yōu)化。它的優(yōu)勢是不需要顯式推導反應函數(shù)的解析表達式反正下層是凸優(yōu)化MATLAB里用quadprog或者linprog秒解。具體在搭建MATLAB程序時我會把電價向量作為全局變量寫一個函數(shù)solve_follower_price(price)這個函數(shù)內(nèi)部建立下層模型、調(diào)用求解器、返回每時段的總充電功率。上層每輪更新電價后就調(diào)用這個函數(shù)獲取響應然后計算上層目標。這樣代碼結(jié)構(gòu)非常清晰也方便后續(xù)改成不同規(guī)模的車輛數(shù)量。3. MATLAB實現(xiàn)流程與關(guān)鍵代碼架構(gòu)3.1 整體框架主程序、上層求解器與下層求解器的分工我建議把整套代碼分成三個文件加一個數(shù)據(jù)文件這樣邏輯最清楚。第一個是main.m負責初始化參數(shù)、設置全局變量、調(diào)用雙層求解循環(huán)、輸出結(jié)果和繪圖。第二個是upper_model.m里面定義上層目標函數(shù)和約束。第三個是lower_model.m負責建立下層優(yōu)化模型、調(diào)用quadprog或linprog求解并把結(jié)果返回給上層。實際寫的時候下層模型往往會被封裝成一個函數(shù)函數(shù)簽名類似function P_ev solve_lower(price, EV_data)輸入是電價向量和電動汽車參數(shù)結(jié)構(gòu)體輸出是24時段的聚合充電功率。上層模型作為目標函數(shù)傳給優(yōu)化求解器簽名是function F upper_obj(price)它內(nèi)部先調(diào)用P_ev solve_lower(price, EV_data)再計算凈負荷和峰谷差最后返回加權(quán)目標值。我一直強調(diào)的迭代式雙層優(yōu)化實際上就是在外層跑一個優(yōu)化算法比如遺傳算法、粒子群或者fmincon。每次迭代優(yōu)化算法都會生成一個新的電價向量然后下層根據(jù)這個電價向量重新優(yōu)化。所以整個雙層模型被“壓扁”成一個關(guān)于電價的單層優(yōu)化上層目標函數(shù)里已經(jīng)嵌入了下層的最優(yōu)響應。這種處理方式對工程實現(xiàn)特別友好因為你不用處理復雜的KKT條件和互補約束只需要保證下層的求解器足夠穩(wěn)定在每次調(diào)用時都能給出合理的最優(yōu)解。不過這里有一個需要注意的點如果用fmincon這樣的梯度優(yōu)化算法求解上層你會發(fā)現(xiàn)upper_obj對price的梯度其實是不連續(xù)的因為下層最優(yōu)解對電價的變化不是光滑的。fmincon用有限差分試探梯度時很容易碰到數(shù)值噪聲導致迭代不穩(wěn)定。所以我更推薦用無導數(shù)優(yōu)化算法比如MATLAB遺傳算法ga、粒子群particleswarm或者直接自己寫一個簡單的坐標輪換搜索。下面我會給出實際的代碼示例。3.2 下層模型用quadprog求解的詳細實現(xiàn)下層模型的目標函數(shù)是二次規(guī)劃??椿毓降谝豁検请妰r的線性項第二項是SOC變化平方的二次項。如果我們把每輛車的每個時段的充電功率當成決策變量x那么目標函數(shù)可以寫成標準二次型0.5 * x * H * x f * x。H矩陣來自SOC變化懲罰項注意它是對角帶狀結(jié)構(gòu)。f向量來自電價相關(guān)項。下面是我低頻使用的quadprog調(diào)用模板你可以在MATLAB里直接套用H zeros(N * T, N * T); % 填充SOC懲罰項 for i 1:N for t 1:T-1 % SOC差異項對兩個相鄰決策變量的貢獻 idx1 (i-1)*T t; idx2 (i-1)*T t 1; H(idx1, idx1) H(idx1, idx1) 2 * beta_i; H(idx1, idx2) H(idx1, idx2) - 2 * beta_i; H(idx2, idx1) H(idx2, idx1) - 2 * beta_i; H(idx2, idx2) H(idx2, idx2) 2 * beta_i; end endf zeros(N*T, 1); for i 1:N for t 1:T idx (i-1)*T t; f(idx) price(t) / eta_ch_i; % 需要除以充電效率 end end線性不等式約束Ax b用來限制最大功率和SOC上下限。SOC上下限本質(zhì)上是累積功率的線性不等式可以展開寫成累加形式。等式約束Aeqx beq表示初始SOC。然后調(diào)用options optimoptions(quadprog, Algorithm, interior-point-convex, Display, off); x quadprog(H, f, A, b, Aeq, beq, lb, ub, [], options);這里lb和ub就是每輛車每個時段的充放電功率限值。如果你允許V2G那么x的下界是負的表示放電上界是正數(shù)表示充電。如果只允許充電下界就是0。3.3 上層用遺傳算法迭代求解的實現(xiàn)方案上層求解我推薦用MATLAB自帶的ga函數(shù)。為什么選遺傳算法而不選粒子群因為ga處理邊界約束比較簡單而且MATLAB的ga支持自定義種群初始范圍便于把初始電價設置成接近真實分時電價加速收斂。還有一個原因是ga在每次評估目標函數(shù)時會調(diào)用下層quadprog如果下層求解失敗ga不會直接崩潰而是返回巨大懲罰值。粒子群則容易因為NaN傳播導致整個種群崩潰。ga的主要調(diào)用方式是nvars T; % 電價變量個數(shù)24 lb 0.7 * base_price; % 電價下限 ub 1.3 * base_price; % 電價上限 IntCon []; % 電價是連續(xù)變量options optimoptions(ga, PopulationSize, 60, MaxGenerations, 50, ... Display, iter, PlotFcn, gaplotbestf); [best_price, best_F] ga((p) upper_obj(p, data), nvars, [], [], [], [], lb, ub, [], options);注意ga內(nèi)部會隨機產(chǎn)生初始種群所以每次運行結(jié)果可能略有差異。為了保證可復現(xiàn)可以在main.m最開頭加一行rng(2024)把隨機種子固定下來。我強烈建議你養(yǎng)成這個習慣尤其是在做科研需要對比實驗時否則跑三次出三個結(jié)果審稿人看了頭大。upper_obj函數(shù)內(nèi)部需要考慮一個實際問題如果下層求解出來的某些時段功率異常大導致凈負荷超過變壓器容量上層目標應該被懲罰。我在代碼里是這么寫的function F upper_obj(price, data) P_ev solve_lower(price, data.EV_data); % 調(diào)用下層 P_net data.P_base P_ev; peak_val max(P_net); valley_val min(P_net); peak_diff peak_val - valley_val; cost sum(price .* P_net); % 懲罰項若凈負荷超過限值則加大懲罰 overload sum(max(P_net - data.P_max_limit, 0)); F data.w1 * peak_diff data.w2 * cost 1000 * overload; end這里1000這個懲罰系數(shù)不是隨便拍的它必須遠大于正常目標函數(shù)的量級才能保證算法優(yōu)先避開越限解。你可以先跑一次不加懲罰的版本看目標函數(shù)大致是多少再把懲罰系數(shù)設成目標函數(shù)的10到100倍。3.4 編寫代碼前必須準備好的數(shù)據(jù)文件數(shù)據(jù)準備往往比代碼本身更費時間。我給你一個標準的數(shù)據(jù)結(jié)構(gòu)建一個data.m腳本或者.mat文件存起來。常負荷曲線P_base我用一個典型夏季日負荷曲線峰值出現(xiàn)在19點到21點大約3000 kW谷值在凌晨3點到5點大約1200 kW。你可以直接用正弦函數(shù)疊加噪聲生成也可以從電力系統(tǒng)公開數(shù)據(jù)集中拿。電動汽車參數(shù)建議生成50輛車車型分為三類。小型車電池40 kWh最大充電功率7 kW中型車電池60 kWh最大充電功率11 kW大型車電池80 kWh最大充電功率22 kW。每輛車的接入時間服從泊松分布集中在18點到21點離開時間集中在早上7點到9點。初始SOC在0.3到0.6之間隨機目標SOC設為0.9。分時電價的基準我這里用峰谷平三段電價峰段10點到15點、18點到21點電價為1.2元/度平段7點到10點、15點到18點、21點到23點電價為0.8元/度谷段23點到次日7點電價為0.4元/度。上層優(yōu)化會讓電價在這三檔附近微調(diào)。把這些數(shù)據(jù)都定義好之后代碼的可讀性和復現(xiàn)性會大大提升。我見過很多人把數(shù)據(jù)硬編碼在目標函數(shù)里換個場景就得改函數(shù)非常痛苦。你寧可多花半小時把數(shù)據(jù)結(jié)構(gòu)化也別在后面改代碼改到懷疑人生。4. 實操過程與結(jié)果分析4.1 從零運行一遍的完整流程第一步先把上一節(jié)的三個函數(shù)文件建好確保路徑里沒有奇怪的文件夾名稱MATLAB對帶空格和中文的路徑兼容性不穩(wěn)定建議全部用英文路徑。第二步在main.m里調(diào)用初始化數(shù)據(jù)。我習慣寫成data init_data(); global EV_data; % 方便子函數(shù)讀取 EV_data data.EV_data;其實我不太推薦用全局變量但雙層嵌套調(diào)用如果每層都傳個大結(jié)構(gòu)體代碼會顯得很啰嗦。折中方案是把EV_data封裝成handle類或者直接用persistent變量但全局變量在快速原型里確實最省事。等你的代碼開發(fā)成熟之后再改成函數(shù)參數(shù)傳遞也不遲。第三步調(diào)用ga求解。第一次運行建議把種群規(guī)模設小一點比如30代數(shù)設20先驗證流程有沒有bug。確認能跑通之后再加大規(guī)模到60和50獲得更穩(wěn)定的結(jié)果。第四步用返回值畫圖。我習慣畫三張圖第一張是上層目標函數(shù)的收斂曲線第二張是優(yōu)化前后的負荷曲線對比包括原始負荷、僅充電的凈負荷、V2G后的凈負荷第三張是優(yōu)化得到的24小時電價曲線。如果還想看單車級的結(jié)果可以選一輛代表性的EV畫它的SOC和充放電功率時序圖。4.2 典型的收斂過程與優(yōu)化效果解讀我用50輛電動汽車、30個種群規(guī)模、20代遺傳算法做了一次快速驗證。上層目標函數(shù)從最初的950左右經(jīng)過大約12代下降到780之后基本平穩(wěn)。ga的輸出顯示Best fitness曲線在前10代下降明顯后面變化很小說明算法已經(jīng)收斂得比較好了。對比優(yōu)化前后的負荷曲線原始負荷的峰谷差是1800 kW優(yōu)化后僅充電模式峰谷差降到1400 kW削峰率大約22%。如果啟用V2G模式峰谷差能進一步降到1100 kW削峰率達到38%。不過V2G模式下下層聚合商的充電費用不是最小化而是略有上升因為電價高峰時段車輛被調(diào)度去放電放棄了本來可以充電的低電價。這個結(jié)果其實揭示了雙層優(yōu)化的本質(zhì)——上層收益的改善是以犧牲下層部分利益為代價的如果下層完全不妥協(xié)整體就無法達到最優(yōu)。這時候你一定會問電網(wǎng)側(cè)省下來的錢能不能補貼車主這就涉及到利益分配機制設計超出了雙層優(yōu)化本身。在科研中你可以把上層目標改成整體社會福利最大把下層車主的充電費用作為一項負收益納入然后在下層約束中保留車主利益的最低閾值。這種做法既能有雙層結(jié)構(gòu)又能體現(xiàn)公平性。4.3 參數(shù)敏感性分析怎么做雙層優(yōu)化代碼跑通之后我建議你做一個敏感性分析來支撐結(jié)論。常見的分析維度有三個第一權(quán)重w1和w2的取值對結(jié)果的影響。我分別取(0.9, 0.1)、(0.7, 0.3)、(0.5, 0.5)觀察峰谷差和總成本的變化。結(jié)果是權(quán)重越偏向峰谷差電價波動的幅度就越大因為運營商會用更高的峰時電價逼迫車輛錯峰。第二電動汽車數(shù)量從20輛增加到100輛觀察雙層最優(yōu)值的邊際效應。數(shù)量少的時候每增加一輛車峰谷差改善明顯數(shù)量多了之后改善逐漸飽和因為電網(wǎng)容量約束成了瓶頸。第三電池退化懲罰系數(shù)beta的影響。beta從0.001增到0.1下層車輛的充放電次數(shù)顯著減少尤其是V2G的放電次數(shù)被抑制峰谷差隨之變大。這個分析能幫你向讀者解釋為什么V2G不能濫用電池壽命是硬約束。5. 常見問題與MATLAB實踐排坑5.1 下層quadprog求解失敗或解不穩(wěn)定的原因我在跑這套代碼時踩過最大的坑是當電價在某些時段非常接近時quadprog報錯“The problem is infeasible”。排查下來問題出在SOC目標約束上如果車輛接入時段過短比如晚上22點接入、早上6點離開只有8個小時電池初始SOC只有0.3目標SOC要求0.9每小時的充電能力上限是7 kW40 kWh的電池要充24 kWh需要大約3.4小時滿功率充電理論上能完成。但如果充電效率是0.9實際需要的充電量為26.7 kWh接近4小時如果車輛在4小時內(nèi)還受到SOC上限95%的約束可能就會無解。解決辦法有兩個一是放寬離開時SOC要求把目標SOC從0.9改成0.85二是提高最大充電功率。但這些都是物理極限有時就是無法同時滿足。我在代碼里加了開放處理——下層在無解時自動返回一個巨大的懲罰值上層看到這個懲罰就會避開這種不合理的電價設置。另外quadprog對H矩陣的特點也有要求它必須是半正定的。因為電池退化懲罰項里我用了SOC差值的平方如果beta為負H就變成負定矩陣quadprog會直接報錯。所以請確保beta始終為正數(shù)。5.2 遺傳算法收斂慢或陷入局部最優(yōu)的調(diào)參心得遺傳算法本身是隨機算法你很難保證每次都找到全局最優(yōu)。我的經(jīng)驗是光靠增加種群規(guī)模和代數(shù)來提升解質(zhì)量性價比很低。更有效的方法有兩種一是用上一個場景的最優(yōu)解作為初始種群的種子也就是把best_price放在初始種群的一個個體里二是把ga的CrossoverFraction設大一些比如0.85讓交叉產(chǎn)生更多新個體同時MutationFcn用自適應變異。MATLAB里可以通過InitialPopulationMatrix設置初始種群。比如options.InitialPopulationMatrix [best_price_prev; rand(pop_size-1, T) .* (ub - lb) lb];這樣能在保持多樣性的同時讓算法從一個已知的優(yōu)質(zhì)解附近開始探索。我實際測試中這種做法能把收斂代數(shù)從15代壓到6代左右。如果你覺得遺傳算法總在局部最優(yōu)附近打轉(zhuǎn)還有一個方案先用粗粒度網(wǎng)格搜索生成幾個候選電價再把這些候選電價作為初始種群個體。比如把24時段電價簡化成峰平谷三個值枚舉三檔電價的組合選出前幾個目標函數(shù)較低的作為初始種群。這個技巧在寫論文時很好用既體現(xiàn)了初始化策略的合理性又讓結(jié)果更穩(wěn)定。5.3 繪圖輸出與結(jié)果保存的細節(jié)MATLAB繪圖中我建議把圖像字體統(tǒng)一設置成Times New Roman或Helvetica尺寸通過set(gca, FontSize, 12)調(diào)整。存圖時不要用png因為論文或博文插圖可能需要矢量圖用exportgraphics(gcf, result.pdf, ContentType, vector)會更清晰。如果你用的是MATLAB 2020以上版本exportgraphics是標配2020之前的版本可以用print -dpdf。另外每次運行之后把關(guān)鍵變量保存到mat文件中方便后續(xù)分析save(results.mat, best_price, P_ev, P_net, F_history);F_history就是每一代的最佳目標函數(shù)值可以在ga的OutputFcn里收集。如果沒有收集也可以用gaplotbestf那把圖里的數(shù)據(jù)讀出來但比較麻煩。我一般直接在main.m里加一個OutputFcn來記錄代碼如function [state, options, optchanged] record_fitness(options, state, flag) global F_history; % 或者用持久變量 if strcmp(flag, iter) F_history(end1) min(state.Score); end end這個OutputFcn在ga里每代結(jié)束時被調(diào)用把當前代的最優(yōu)值存下來。5.4 代碼擴展從MATLAB到嵌入式部署前的注意事項很多人做完雙層優(yōu)化的MATLAB代碼之后下一步想把它部署到實際充電樁調(diào)度系統(tǒng)里。這里我要提醒一句ga這種智能算法在實時調(diào)度里基本不可用因為一次雙層求解可能跑幾十秒而實際調(diào)度時間尺度是15分鐘或1小時。真要工程化通常的做法是把在線雙層優(yōu)化簡化成離線訓練先在離線場景中用雙層優(yōu)化算出不同典型日的最優(yōu)電價策略存成一張策略表在線運行時根據(jù)當天的負荷預測和車輛接入情況查表或做插值得到電價再調(diào)用下層quadprog求解充電計劃。因為quadprog本身求解速度很快毫秒級就能完成所以在線實時調(diào)度完全可行。MATLAB Coder可以把quadprog這類內(nèi)置求解器轉(zhuǎn)換成C代碼但對于遺傳算法這種全局優(yōu)化器轉(zhuǎn)換比較困難。所以如果你有落地需求建議把上層離線策略訓練留在MATLAB里下層在線求解用MATLAB Coder、Python的cvxpy或者其他嵌入式QP庫實現(xiàn)。這個思路在實際工程中非常成熟也值得寫進技術(shù)報告的展望部分。6. 雙層優(yōu)化代碼研究之后還能怎么玩6.1 從靜態(tài)調(diào)度擴展到實時滾動優(yōu)化我現(xiàn)在跑的這套雙層優(yōu)化是假設全天電價事前已知、所有車輛接入信息也完全已知屬于開環(huán)調(diào)度。實際上真實場景中車輛是隨時接入、隨時離開的而且SOC上報值有誤差。一個直接的擴展方向是模型預測控制也就是滾動時域優(yōu)化每個小時重新求解一次接下來24小時的雙層優(yōu)化但只執(zhí)行下一個小時的動作然后隨著新信息到來更新滾動窗口。這樣既能保留雙層結(jié)構(gòu)又能應對不確定性。在MATLAB里實現(xiàn)滾動優(yōu)化其實不難只要把main函數(shù)包進一個for循環(huán)在每次循環(huán)中更新EV接入狀態(tài)、負荷預測值和初始SOC然后調(diào)用外層算法重新求解。需要注意的是每次滾動都要把上層ga的初始種群設置成上次最優(yōu)解附近否則實時性跟不上。6.2 從純電網(wǎng)視角擴展到多利益主體博弈如果不想局限于上下兩層的金字塔結(jié)構(gòu)可以試試雙層到多層的拓展比如電網(wǎng)—充電站聚合商—車主三方。中間層的聚合商既是上層的跟隨者又是下層的領(lǐng)導者它的目標函數(shù)是在電網(wǎng)給的批發(fā)電價下通過制定零售電價來引導車主同時最大化自己的利潤。這個結(jié)構(gòu)在MATLAB里依然可以用嵌套迭代實現(xiàn)但要小心每一層都有自己獨立的優(yōu)化變量和算法運算時間會指數(shù)級上升。我建議先做兩層穩(wěn)定、收斂性好再往三層擴展。另外一類常見擴展是加入可再生能源出力不確定性。你可以在上層模型中把光伏和風電出力描述成區(qū)間變量然后采用魯棒優(yōu)化的思維方式優(yōu)化最惡劣場景。MATLAB的魯棒優(yōu)化工具箱或者YALMIP配合適當?shù)那蠼馄骺梢蕴幚磉@類問題但代碼量會明顯增加需要有一定優(yōu)化基礎(chǔ)才能駕馭。6.3 我的實操經(jīng)驗總結(jié)這套代碼前前后后我迭代了三個版本最深的體會是雙層優(yōu)化最難的既不是數(shù)學也不是編程而是“讓上下兩層都能被解釋清楚”。很多人把模型堆得很復雜但跑出來的結(jié)果說不出為什么或者給出的電價策略明顯違背常識這時候你就要回頭檢查目標和約束是不是設置反了。比如我最初把下層電池退化懲罰項加得太大導致下層無論如何都不放電V2G功能直接失效上層再怎么優(yōu)化都削不了峰。后來把beta從0.5降到0.02效果立刻出來了。還有一點MATLAB的版本差異有時候很讓人頭疼。quadprog在舊版本和新版本之間的接口參數(shù)不完全一致比如舊版本用LargeScale新版本用Algorithm如果你從網(wǎng)上下載的代碼直接跑很可能會因為算法選項不兼容而報錯。我建議盡量用MATLAB 2020b或更新版本并且把optimoptions的寫法統(tǒng)一。如果用的是別人的老代碼看到optimset就要留個心眼最好用optimoptions重寫一遍求解器選項。最后再說一個個人習慣我做的每一次雙層優(yōu)化實驗都會把隨機種子、參數(shù)表和結(jié)果圖打包存成一個文件夾命名為場景的描述比如“V2G_beta002_50EV”。這樣三個月后再回來看依然能清楚知道當時做了什么??蒲幸埠霉こ桃擦T可復現(xiàn)性是最大的生產(chǎn)力。希望這篇文章能讓你在電動汽車雙層優(yōu)化調(diào)度的MATLAB路上少踩幾個坑先跑出第一版能收斂的代碼再慢慢打磨出自己的研究特色。