汽車實(shí)時(shí)調(diào)度策略:Matlab滾動(dòng)時(shí)域LP實(shí)現(xiàn)詳解)
很多剛開始做電動(dòng)汽車調(diào)度的朋友都會(huì)把“基于V2G技術(shù)的電動(dòng)汽車實(shí)時(shí)調(diào)度策略”理解成“讓每輛車算一個(gè)充放電功率”。真上手之后才發(fā)現(xiàn)V2G實(shí)時(shí)調(diào)度更像是在管一支隨時(shí)有人進(jìn)出、電池狀態(tài)千差萬別的車隊(duì)同時(shí)還要和電網(wǎng)的負(fù)荷曲線、分時(shí)電價(jià)、變壓器容量綁在一起做博弈。最近我完整實(shí)現(xiàn)了一遍這個(gè)策略用的是Matlab從建模、選算法到寫代碼調(diào)試前前后后折騰了近兩周。如果你也在做電動(dòng)汽車調(diào)度、微電網(wǎng)能量管理或者儲(chǔ)能優(yōu)化方向這篇筆記應(yīng)該能幫你少走很多彎路。1. 項(xiàng)目整體思路與調(diào)度模型構(gòu)建1.1 實(shí)時(shí)調(diào)度到底在調(diào)度什么V2GVehicle-to-Grid的核心價(jià)值是讓電動(dòng)汽車在“從電網(wǎng)取電”和“向電網(wǎng)送電”兩個(gè)方向都能靈活響應(yīng)。但真正落到調(diào)度問題上事情就具體了每輛車有自己的接入時(shí)間、離開時(shí)間、初始SOC、目標(biāo)SOC和充放電功率上限配電臺(tái)區(qū)有一條時(shí)變的基礎(chǔ)負(fù)荷曲線和一個(gè)變壓器容量約束電網(wǎng)側(cè)還有分時(shí)電價(jià)。實(shí)時(shí)調(diào)度要做的就是在每個(gè)調(diào)度周期比如每15分鐘根據(jù)當(dāng)前已經(jīng)接入車輛的狀態(tài)和未來一段時(shí)間的預(yù)測數(shù)據(jù)決定接下來每小時(shí)甚至幾分鐘內(nèi)每輛車的充放電功率。我用的模擬場景是某園區(qū)或小區(qū)充電站下面有20輛電動(dòng)汽車調(diào)度間隔取15分鐘一天96個(gè)周期。車輛之間互不共享電池但共享配電變壓器容量。這種場景非常典型既不會(huì)太簡單到?jīng)]有說服力也不會(huì)因?yàn)橐?guī)模太大導(dǎo)致Matlab跑不動(dòng)。如果只做單輛車的充放電策略你會(huì)發(fā)現(xiàn)約束太少算法沒什么可調(diào)但直接做大幾十輛車、上千個(gè)節(jié)點(diǎn)又容易陷入調(diào)試地獄。20輛車是個(gè)很好的中間起點(diǎn)。1.2 目標(biāo)函數(shù)與約束條件的數(shù)學(xué)表達(dá)調(diào)度目標(biāo)不能拍腦袋定成“費(fèi)用最低”。如果只優(yōu)化用戶充電費(fèi)用群體會(huì)自發(fā)把充電需求全部擠進(jìn)谷電時(shí)段結(jié)果峰谷差可能不減反增配電臺(tái)區(qū)在夜間又冒出一個(gè)新的負(fù)荷尖峰。所以我在目標(biāo)函數(shù)里做了多目標(biāo)權(quán)衡最小化所有車輛在預(yù)測窗口內(nèi)的凈購電成本最小化配電臺(tái)區(qū)峰值負(fù)荷、平抑負(fù)荷波動(dòng)對(duì)頻繁放電帶來的電池?fù)p耗加一個(gè)懲罰項(xiàng)。目標(biāo)函數(shù)寫出來大致是[ \min \quad J \sum_{t \in T} \big[ \lambda_1 \cdot p_t \cdot \sum_i P_{net,i,t} \lambda_2 \cdot L_{peak} \lambda_3 \cdot \sum_i D_{i,t} \big] ]其中 (P_{net,i,t}) 表示車輛i在t時(shí)刻從電網(wǎng)取電的凈功率正值是充電負(fù)值是放電。(p_t) 是分時(shí)電價(jià)(L_{peak}) 是窗口內(nèi)最大負(fù)荷(D_{i,t}) 是電池退化相關(guān)的放電深度懲罰。實(shí)際求解時(shí)(L_{peak}) 我會(huì)寫成線性化形式也就是給峰值負(fù)荷增加一個(gè)輔助變量再加不等式約束。約束條件分為四類首先是配電臺(tái)區(qū)功率平衡約束。某個(gè)時(shí)刻變壓器出口總功率等于基礎(chǔ)負(fù)荷加上所有車輛的凈充電功率這個(gè)總功率不能超過變壓器容量上限低于安全下限的情況也盡量避免。其次是單車功率約束。每輛車在同一個(gè)調(diào)度周期內(nèi)不能既充電又放電充放電功率絕對(duì)值都不能超過車端變換器的額定功率。這個(gè)額定功率我按常見的7kW交流樁來設(shè)置電池容量取50kWh對(duì)應(yīng)現(xiàn)在主流的長續(xù)航車型。再是電池SOC動(dòng)態(tài)方程[ SOC_{i,t1} SOC_{i,t} \frac{P_{ch,i,t} \cdot \eta_c \cdot \Delta t}{E_i} - \frac{P_{dis,i,t} \cdot \Delta t}{E_i \cdot \eta_d} ]這里 (P_{ch}) 是充電功率(P_{dis}) 是放電功率(\eta_c) 和 (\eta_d) 分別是充放電效率我統(tǒng)一取0.95。SOC的范圍設(shè)定在0.2到0.9之間避免過充過放對(duì)電池的傷害。最后是離網(wǎng)SOC約束。每輛車離開的時(shí)候SOC至少要達(dá)到用戶設(shè)定的目標(biāo)值但也不需要強(qiáng)制充滿。這個(gè)約束最容易導(dǎo)致求解失敗因?yàn)檐囕v接入時(shí)間短、初始SOC低、充電功率有限很可能在離開前充不到目標(biāo)值。后面我會(huì)專門講怎么處理。1.3 為什么“實(shí)時(shí)”比“日前”更適合V2G很多論文先做“日前調(diào)度”也就是基于預(yù)測曲線一次性算出24小時(shí)的充放電計(jì)劃再讓車輛執(zhí)行。這個(gè)方法在歷史負(fù)荷平穩(wěn)、車輛行蹤規(guī)律時(shí)效果還行但一旦遇到車輛接入時(shí)間隨機(jī)、臨時(shí)改計(jì)劃、或者天氣變化導(dǎo)致基礎(chǔ)負(fù)荷波動(dòng)日前計(jì)劃就失效了。實(shí)時(shí)調(diào)度采用“滾動(dòng)窗口”的思路每個(gè)調(diào)度周期只求解未來4小時(shí)或者1小時(shí)的控制指令但只執(zhí)行第一個(gè)周期的功率到下一個(gè)15分鐘再刷新狀態(tài)、重新求解。這也是模型預(yù)測控制MPC的經(jīng)典框架。好處是對(duì)預(yù)測誤差的容忍度高車輛隨機(jī)接入也能隨時(shí)納入優(yōu)化不需要重跑全天計(jì)劃。代價(jià)是求解頻率變高算法必須足夠快。我在Matlab里用線性規(guī)劃實(shí)現(xiàn)滾動(dòng)窗口單次求解時(shí)間基本在幾十毫秒量級(jí)完全滿足15分鐘一個(gè)周期的實(shí)時(shí)性要求。2. 核心算法選型與Matlab實(shí)現(xiàn)要點(diǎn)2.1 幾種常見調(diào)度算法怎么選V2G調(diào)度算法從數(shù)學(xué)上看是一個(gè)約束優(yōu)化問題可選的方法非常多。我自己做過對(duì)比把結(jié)論整理成表格算法類型優(yōu)點(diǎn)缺點(diǎn)適合場景線性規(guī)劃LP求解快、全局最優(yōu)、模型直觀目標(biāo)函數(shù)和約束必須是線性的功率變量連續(xù)、費(fèi)用和負(fù)荷優(yōu)化混合整數(shù)線性規(guī)劃MILP能建模0/1開關(guān)、啟停狀態(tài)求解速度隨整數(shù)變量數(shù)明顯下降需要嚴(yán)格禁止同時(shí)充放電、多檔位控制動(dòng)態(tài)規(guī)劃理論上能得到全局最優(yōu)狀態(tài)空間爆炸車輛多時(shí)基本不可行單車或少車輛調(diào)度遺傳算法/粒子群適應(yīng)強(qiáng)非線性目標(biāo)參數(shù)敏感、每次結(jié)果可能不一致、實(shí)時(shí)性差離線方案設(shè)計(jì)、參數(shù)標(biāo)定MPC滾動(dòng)優(yōu)化能處理不確定性、滾動(dòng)修正需要模型和求解器配合實(shí)時(shí)調(diào)度、復(fù)雜系統(tǒng)控制我最終選了“滾動(dòng)時(shí)域LP”作為主方案。原因有兩個(gè)一是V2G調(diào)度模型在15分鐘粒度下SOC更新、功率約束都可以寫成線性關(guān)系不需要非線性求解二是LP能夠保證每次求解的穩(wěn)定性和速度方便我做重復(fù)實(shí)驗(yàn)對(duì)比不同參數(shù)。如果你想把“同周期不能同時(shí)充放電”這個(gè)邏輯做實(shí)可以在LP基礎(chǔ)上加0/1變量升級(jí)成MILP但需要接受更大的計(jì)算開銷。我的經(jīng)驗(yàn)是在正常分時(shí)電價(jià)且目標(biāo)函數(shù)有費(fèi)用懲罰的前提下LP最優(yōu)解天然不會(huì)同時(shí)充放電顯式加上“功率之和小于上限”的約束已經(jīng)足夠了。2.2 滾動(dòng)時(shí)域LP的完整流程實(shí)時(shí)調(diào)度的主循環(huán)我用偽代碼表示會(huì)更清楚初始化時(shí)間戳、電價(jià)曲線、基礎(chǔ)負(fù)荷曲線、車輛參數(shù)。對(duì)每個(gè)調(diào)度周期k 1:T只保留當(dāng)前仍接入、且還沒到離開時(shí)間的車輛更新每輛車的最新SOC讀取未來H個(gè)周期的電價(jià)預(yù)測和基礎(chǔ)負(fù)荷預(yù)測構(gòu)建LP模型定義決策變量、目標(biāo)函數(shù)、約束調(diào)用solve得到最優(yōu)功率序列只取第一個(gè)周期的控制指令更新車輛狀態(tài)存下結(jié)果進(jìn)入下一輪。這里有個(gè)容易被忽略的細(xì)節(jié)每個(gè)調(diào)度周期內(nèi)離開時(shí)間早于預(yù)測窗口結(jié)束的車輛在離開后的時(shí)段就不應(yīng)該再出現(xiàn)在約束里。實(shí)現(xiàn)時(shí)我會(huì)生成一個(gè)“可用車輛-時(shí)段”的掩碼矩陣所有功率變量都乘以這個(gè)掩碼。否則車輛已經(jīng)離開SOC約束還在強(qiáng)制它參與調(diào)度這既不合理也會(huì)造成不可行。2.3 Matlab代碼的基本骨架與變量設(shè)計(jì)最開始我把所有代碼堆在一個(gè)大腳本里結(jié)果改一個(gè)參數(shù)要翻半天后來重構(gòu)成了三個(gè)模塊參數(shù)定義、模型構(gòu)建、滾動(dòng)循環(huán)。車輛參數(shù)我統(tǒng)一用struct數(shù)組保存每個(gè)元素包含電池容量、最大功率、接入/離開時(shí)間、初始SOC、SOC上下限、目標(biāo)SOC和充放電效率。核心的滾動(dòng)循環(huán)代碼長這樣for k 1:totalPeriods horizon min(H, totalPeriods - k 1); active (veh.startTime k) (veh.leaveTime k); prob optimproblem(ObjectiveSense, minimize); Pch optimvar(Pch, nEV, horizon, LowerBound, 0); Pdis optimvar(Pdis, nEV, horizon, LowerBound, 0); % 目標(biāo)函數(shù)購電成本 峰負(fù)荷懲罰 放電懲罰 cost 0; for t 1:horizon netLoad baseLoad(kt-1) sum(Pch(:,t)) - sum(Pdis(:,t)); cost cost price(kt-1) * sum(Pch(:,t) - Pdis(:,t)) ... penalty * netLoad; end prob.Objective cost; % 約束功率上限、變壓器容量、SOC更新等 prob.Constraints.powerUpper Pch Pdis veh.maxPower; prob.Constraints.transformer ... baseLoad(k:khorizon-1) sum(Pch,1) - sum(Pdis,1) transformerCap; [sol, fval, exitflag] solve(prob); ... end當(dāng)然這只是示意真正跑起來還需要把掩碼矩陣乘進(jìn)去并把SOC更新寫成矩陣形式。SOC更新如果放在LP模型內(nèi)部需要用到過去SOC作為初值我會(huì)把當(dāng)前SOC作為常量寫入而不是作為決策變量否則模型會(huì)變成一個(gè)動(dòng)態(tài)非凸問題不能直接用LP求解。2.4 求解器與性能細(xì)節(jié)Matlab里solve會(huì)自動(dòng)調(diào)用默認(rèn)的線性規(guī)劃求解器對(duì)幾百個(gè)變量的LP來說完全足夠。但如果你要跑很多組實(shí)驗(yàn)建議把求解選項(xiàng)固定下來比如設(shè)置“最大迭代次數(shù)”、“約束容差”避免每次求解都啟動(dòng)默認(rèn)邏輯。更快的做法是直接用linprog把目標(biāo)函數(shù)、不等式約束的矩陣和向量手動(dòng)組裝成標(biāo)準(zhǔn)形式。初期調(diào)試用optimproblem更容易定位問題后期性能優(yōu)化再遷移到linprog。我要強(qiáng)調(diào)一個(gè)容易踩的坑optimvar的維度是nEV x horizon但寫約束時(shí)sum(Pch,1)得到的是一個(gè)1 x horizon向量而baseLoad可能是行向量也可能是列向量。Matlab對(duì)隱式擴(kuò)展有它的規(guī)則一旦方向不一致要么報(bào)維度錯(cuò)誤要么得到錯(cuò)誤的約束矩陣。我后來統(tǒng)一把時(shí)間維度固定為行向量車輛維度固定為列向量再也沒因?yàn)檫@個(gè)出過問題。3. 實(shí)操過程與案例實(shí)現(xiàn)3.1 算例場景和參數(shù)設(shè)置為了讓結(jié)果可復(fù)現(xiàn)我給一個(gè)保守但貼近實(shí)際的案例。假設(shè)配電臺(tái)區(qū)變壓器額定容量500kW基礎(chǔ)負(fù)荷取典型的夏季工作日形狀高峰集中在傍晚18點(diǎn)到22點(diǎn)。調(diào)度周期15分鐘一天96個(gè)點(diǎn)。電動(dòng)汽車共20輛統(tǒng)一采用50kWh電池額定充放電功率7kW充放電效率95%。參數(shù)取值調(diào)度周期15分鐘預(yù)測時(shí)域H16步4小時(shí)車輛數(shù)20電池容量50 kWh最大充放電功率7 kWSOC范圍0.2 ~ 0.9目標(biāo)SOC0.9峰時(shí)電價(jià)0.8 元/kWh谷時(shí)電價(jià)0.3 元/kWh車輛接入時(shí)間我隨機(jī)分布在8點(diǎn)到20點(diǎn)之間離開時(shí)間集中在17點(diǎn)到22點(diǎn)。這樣做能逼出問題很多車下午接入晚上就要離開充電窗口很短還正好撞上電網(wǎng)負(fù)荷高峰。如果沒有V2G這一大波充電需求會(huì)硬生生疊在負(fù)荷曲線峰值上變壓器壓力會(huì)非常大。3.2 從數(shù)據(jù)生成到滾動(dòng)求解的關(guān)鍵代碼我先把基礎(chǔ)負(fù)荷和電價(jià)生成好用數(shù)組保存然后寫一個(gè)車輛狀態(tài)更新函數(shù)function socNext updateSOC(soc, Pgrid, params) % Pgrid 正表示從電網(wǎng)取電負(fù)表示向電網(wǎng)放電 if Pgrid 0 socNext soc params.chargeEff * Pgrid * params.dt / params.batteryCapacity; else socNext soc Pgrid * params.dt / (params.batteryCapacity * params.dischargeEff); end end這里有一個(gè)非常重要的設(shè)計(jì)Pgrid是“和電網(wǎng)交換的功率”所以在放電時(shí)電池內(nèi)部實(shí)際消耗的功率是-Pgrid / dischargeEff而不是-Pgrid。這個(gè)符號(hào)如果搞反SOC更新就會(huì)出現(xiàn)“越放電電量越多”的靈異現(xiàn)象。我在調(diào)試時(shí)遇到過兩次代碼邏輯檢查了半小時(shí)才發(fā)現(xiàn)是效率取反。接著是LP模型構(gòu)建。為了兼顧速度和調(diào)試我直接調(diào)用linprog把目標(biāo)函數(shù)系數(shù)向量f和約束矩陣Aineq、bineq組裝出來。決策變量的排列方式我定為前nEV * H個(gè)變量是各周期各車的充電功率后nEV * H個(gè)變量是各周期各車的放電功率。這個(gè)排列順序一旦確定所有矩陣的列都要按它來。我建議在做矩陣拼裝時(shí)先把索引函數(shù)寫好比如chIdx (i,t) (t-1)*nEV i; disIdx (i,t) nEV*H (t-1)*nEV i;這樣寫約束時(shí)邏輯很直觀不容易索引錯(cuò)位。3.3 模擬結(jié)果怎么讀我對(duì)三種策略做了對(duì)比無調(diào)度車輛一接入就最大功率充電、單向分時(shí)充電只能充電不能放電、本文實(shí)時(shí)調(diào)度允許V2G雙向并滾動(dòng)優(yōu)化。模擬趨勢如下策略峰值負(fù)荷kW用戶平均充電費(fèi)用元平均離網(wǎng)SOC無調(diào)度45814.20.82單向分時(shí)充電42110.30.90V2G滾動(dòng)調(diào)度3858.60.90這說明V2G對(duì)削峰填谷和用戶成本都有明顯作用。峰值負(fù)荷下降并不是因?yàn)樯俪淞穗姸前岩徊糠指叻宄潆娦枨笄耙仆瑫r(shí)讓部分車輛在傍晚高峰時(shí)放電給其他車輛騰出負(fù)荷空間。離網(wǎng)SOC在V2G調(diào)度下依然能保持0.90說明放電并沒有犧牲用戶出行需求。當(dāng)然這個(gè)表里的數(shù)字只是模擬算例不能直接套到其他場景里。如果你把峰谷價(jià)差縮小V2G放電收益會(huì)明顯下降如果車輛數(shù)量增加而變壓器容量不變削峰空間也會(huì)跟著受限。所以做項(xiàng)目時(shí)一定要自己跑參數(shù)敏感性分析而不是照抄別人結(jié)論。4. 常見問題與調(diào)試心得4.1 四個(gè)最容易遇到的求解報(bào)錯(cuò)我調(diào)這個(gè)項(xiàng)目時(shí)報(bào)錯(cuò)類型基本集中在四類整理成速查表報(bào)錯(cuò)或異常可能原因解決辦法“Problem is infeasible”離網(wǎng)前充不到目標(biāo)SOC或變壓器容量過小導(dǎo)致所有車輛無法同時(shí)充電把目標(biāo)SOC改為軟約束加松弛變量求解成功但SOC越界調(diào)度循環(huán)里狀態(tài)更新與模型預(yù)測不一致檢查充放電效率正反方向、調(diào)度周期單位結(jié)果出現(xiàn)大量“既充又放”目標(biāo)函數(shù)缺少懲罰或電價(jià)有負(fù)值增加放電懲罰項(xiàng)或升級(jí)為MILP求解時(shí)間隨車輛數(shù)急劇上升用optimproblem頻繁創(chuàng)建問題對(duì)象改用linprog、避免循環(huán)內(nèi)重定義峰值負(fù)荷反而更高只優(yōu)化費(fèi)用沒有加入峰負(fù)荷約束目標(biāo)函數(shù)中加入峰谷差或變壓器容量的罰項(xiàng)這里面最坑的是第一類。車輛接入時(shí)間短、初始SOC低時(shí)LP會(huì)連一個(gè)可行解都找不到。我的解決辦法是把離網(wǎng)SOC約束改成[ SOC_{i,leave} s_i \ge SOC_{i,target} ]其中 (s_i) 是松弛變量同時(shí)在目標(biāo)函數(shù)里加入 (M \cdot \sum s_i)M是一個(gè)很大的懲罰系數(shù)。這樣大部分場景都能求解車輛如果實(shí)在充不滿也會(huì)以最小的違約程度離開。4.2 實(shí)時(shí)性優(yōu)化的幾條經(jīng)驗(yàn)剛開始我用optimproblem跑20輛車、96個(gè)周期整個(gè)調(diào)度要兩三分鐘才能完成作為離線仿真勉強(qiáng)能忍但離“實(shí)時(shí)”差得很遠(yuǎn)。后來我發(fā)現(xiàn)瓶頸不在求解而在每次循環(huán)里反復(fù)構(gòu)造優(yōu)化問題對(duì)象Matlab的面向?qū)ο蠓庋b開銷非常大。把核心求解改成linprog之后同樣的算例壓縮到幾十秒。再進(jìn)一步采用離線預(yù)計(jì)算索引矩陣、全程向量化效果會(huì)更好。有一個(gè)更實(shí)用的技巧是縮小預(yù)測時(shí)域如果只做1小時(shí)預(yù)測也就是4個(gè)調(diào)度周期計(jì)算量會(huì)降到原來的四分之一而調(diào)度效果因?yàn)闈L動(dòng)更新的存在并不會(huì)劣化太多。如果你要處理上百輛車我建議直接用稀疏矩陣描述約束因?yàn)榇蟛糠旨s束矩陣都是對(duì)角塊結(jié)構(gòu)稀疏后會(huì)快很多。Matlab里sparse函數(shù)的應(yīng)用很值得花半小時(shí)研究。4.3 能量守恒是調(diào)試的第一道關(guān)卡調(diào)度算法寫完了怎么判斷它對(duì)不對(duì)我的方法只有一個(gè)先查能量守恒。把整個(gè)調(diào)度周期內(nèi)所有車輛的充電電量加起來減去放電電量再和SOC變化總量對(duì)比。對(duì)一輛車來說關(guān)系是[ \sum_t P_{ch,i,t} \cdot \eta_c - \sum_t \frac{P_{dis,i,t}}{\eta_d} E_i \cdot (SOC_{i,end} - SOC_{i,start}) ]如果左端明顯大于右端說明代碼可能在某個(gè)環(huán)節(jié)讓能量憑空產(chǎn)生。最常見的原因是放電效率用反或者SOC更新時(shí)用了與LP模型內(nèi)不同的公式。我記得有一次結(jié)果看起來特別好峰谷差小、成本低但車主平均離網(wǎng)SOC只有0.75明顯低于預(yù)期。一查發(fā)現(xiàn)是放電懲罰寫成了負(fù)的等于系統(tǒng)通過反復(fù)放電“賺錢”最后所有車都被放空了。所以做這類項(xiàng)目不能只看目標(biāo)函數(shù)值每個(gè)約束的實(shí)際狀態(tài)都要在結(jié)果里輸出出來檢查。4.4 我的調(diào)試工作流我現(xiàn)在的習(xí)慣是先跑一個(gè)3輛車、2小時(shí)的小場景把功率曲線和SOC曲線畫出來逐行確認(rèn)每輛車的動(dòng)作都符合預(yù)期。小場景沒問題之后再逐步放大車輛數(shù)和時(shí)間窗口。每做一次改動(dòng)我都會(huì)記錄三個(gè)指標(biāo)總充電量、總放電量、全部車的離網(wǎng)SOC分布。如果這三個(gè)指標(biāo)相對(duì)基線有明顯異常就先停下加日志排查而不是繼續(xù)調(diào)參數(shù)。另外我會(huì)保留上一輪調(diào)度結(jié)果作為對(duì)照。有次為了測試新約束我把結(jié)果圖和前一天的舊圖疊在一起看一下發(fā)現(xiàn)新代碼在晚上8點(diǎn)后把所有車都強(qiáng)制放電了原因是變壓器約束那里的baseLoad索引寫錯(cuò)了一位把負(fù)荷序列整體左移了一個(gè)周期。這類索引錯(cuò)誤在沒有對(duì)照圖時(shí)很難一眼看出來。5. 擴(kuò)展方向與個(gè)人體會(huì)5.1 從仿真到落地還要補(bǔ)哪些模塊仿真能跑通和真車能上線之間還有很大距離。第一個(gè)缺口是通訊延遲和數(shù)據(jù)質(zhì)量調(diào)度中心拿到的SOC、功率、車輛連接狀態(tài)都不是實(shí)時(shí)完美的需要考慮測量噪聲和丟包。第二個(gè)缺口是預(yù)測更新真實(shí)電價(jià)和負(fù)荷預(yù)測每隔幾分鐘就會(huì)更新滾動(dòng)優(yōu)化需要接入實(shí)時(shí)預(yù)測接口。第三個(gè)缺口是執(zhí)行偏差實(shí)際功率指令到達(dá)充電樁后可能因?yàn)檐囕d協(xié)議限制無法精確執(zhí)行。我目前還在擴(kuò)展的方向是把光伏發(fā)電的不確定性加進(jìn)MPC里。當(dāng)屋頂光伏出力預(yù)測有誤差時(shí)調(diào)度策略需要預(yù)留一部分電池容量來應(yīng)對(duì)功率偏差這就變成了隨機(jī)模型預(yù)測控制。目標(biāo)函數(shù)里還要加“預(yù)測偏差導(dǎo)致的不平衡懲罰”比確定性模型復(fù)雜不少但更貼近實(shí)際園區(qū)綜合能源系統(tǒng)的需求。5.2 代碼工程化與組織建議如果你打算長期在這個(gè)項(xiàng)目上迭代我強(qiáng)烈建議把代碼模塊化。我的項(xiàng)目文件分成了三個(gè)層次配置層一個(gè)params_define.m集中管理所有參數(shù)包括車輛數(shù)、電池容量、效率、預(yù)測時(shí)域、懲罰系數(shù)。模型層一個(gè)build_problem.m根據(jù)當(dāng)前周期和車輛狀態(tài)構(gòu)建LP或MILP問題返回目標(biāo)系數(shù)和約束矩陣。調(diào)度層一個(gè)run_scheduler.m負(fù)責(zé)滾動(dòng)循環(huán)、狀態(tài)更新、結(jié)果存儲(chǔ)。這樣當(dāng)你需要換一套電價(jià)曲線、換一組車輛參數(shù)或者換一個(gè)求解器時(shí)不需要?jiǎng)雍诵倪壿嫛N疑踔習(xí)涯繕?biāo)函數(shù)里的懲罰系數(shù)也做成數(shù)組方便做靈敏度分析時(shí)批量修改。存儲(chǔ)結(jié)果時(shí)我建議把所有中間變量統(tǒng)一寫進(jìn)一個(gè)states結(jié)構(gòu)體每個(gè)調(diào)度周期往里面追加一條記錄。最后畫圖時(shí)只用一段通用的畫圖函數(shù)從states里提取數(shù)據(jù)比在循環(huán)里散落一堆plot要清爽得多。5.3 個(gè)人體會(huì)這個(gè)項(xiàng)目做下來最深的感受是“算法本身不難難在驗(yàn)證”。市面上很多V2G調(diào)度論文展示的效果很漂亮但代碼細(xì)節(jié)往往經(jīng)不起反復(fù)推敲。最快的學(xué)習(xí)方式不是直接套用論文參數(shù)而是先寫一個(gè)最簡單的規(guī)則調(diào)度做基線再上優(yōu)化算法。如果沒有對(duì)薄弱基線你會(huì)很難判斷峰值負(fù)荷下降究竟是因?yàn)檎{(diào)度算法有效還是因?yàn)槟承﹨?shù)調(diào)得順手。我自己踩過多次坑之后現(xiàn)在已經(jīng)形成習(xí)慣每一步改動(dòng)都留下一個(gè)可以回滾的腳本每次實(shí)驗(yàn)都保存結(jié)果數(shù)據(jù)和參數(shù)文件。這樣做雖然煩瑣但絕大多數(shù)項(xiàng)目里的“玄學(xué)問題”最后都能在對(duì)比中找到答案。V2G實(shí)時(shí)調(diào)度是一個(gè)值得長期深耕的方向如果你也想動(dòng)手做一遍我建議從今天這個(gè)20輛車、15分鐘粒度的案例開始先把能量守恒跑通再逐步疊加預(yù)測和不確定性每一步都看得見摸得著比直接啃論文踏實(shí)得多。