MATLAB下考虑V2G的光储充一体化微网多目标优化调度策略,这个课题我断断续续跑了大半个学期,前前后后改了四版模型才把结果做顺。说实话,V2G单独拿出来很好理解,无非就是让电动汽车在闲置时反向送电,可一旦把它放进光储充一体化微网里,还要和储能、光伏、电网购售电一起做多目标优化调度,复杂度立刻会上一个台阶。这篇内容不打算复述论文里那些公式推导,而是分享我实际建模、写MATLAB代码、调约束、看结果时踩过的坑和最终能稳定复现的一套做法,给正在做微网能量管理、电动汽车聚合调度或者准备把V2G写进毕业论文的读者一个相对完整的参考。
1. 先把问题问清楚:V2G加入后微网调度到底多出哪些坑
1.1 从“光储充”到“光储充+V2G”,问题已经换了赛道
先说个很直观的对比。传统的光储充微网里,光伏、储能、普通充电桩、本地负荷,这四样东西是“静止”的:光伏出力靠天气预测,储能容量是个固定值,充电桩只是单向负荷。调度逻辑核心就是削峰填谷——光伏多的时候给储能充电,电价高的时候储能放电,负荷太大时从主网购电,目标函数往往只有一个最小化运行成本。
V2G加入之后,本质变化在于:原来充电桩上那一排电动汽车,从刚性负荷变成了可双向调度的移动储能。这个概念听起来很美,但到了建模层面,麻烦事一个接一个冒出来:
- 储能容量是动态的。早上8点停车场里可能连着50辆满电的车,下午2点可能多数车已经开走,剩下10辆还都是低电量,微网侧可调用的V2G总容量完全不是定值。
- 不能把车当纯电池用。储能的SOC约束是单向的,只要不低于下限即可;但V2G有一个用户期望问题,车主晚上下班必须带走足够电量,你如果只按照微网成本最优去调度,很可能在傍晚高价时段把车放成空电,第二天车主直接找你麻烦。
- 时间耦合更强了。V2G傍晚的放电能力,很大程度上取决于白天车辆到场时的初始电量和充电安排,这就使得优化问题从单个时段决策变成了跨时段动态决策。
所以你会发现,V2G场景下做优化调度,不是在原有模型上加一个放电变量那么简单,而是要把“移动储能”的用户行为边界、能量动态、双向功率交互全部重写一遍。我建议所有准备在这个方向做复现的人,第一步不要急着写代码,先把自己要处理的V2G模型边界想清楚:你考虑的是私人车队的聚合模型,还是单桩级模型?车辆到达离开时刻是确定性已知,还是概率分布?这两个选择会直接决定后面问题规模和解法路线。
1.2 为什么它天然是一个多目标优化问题
很多初学者拿到类似题目直接上来就写“min 总运行成本”,跑完发现储能和V2G在仿真里被用到飞起,成本降了一大截,但在实际工程里根本没法执行。原因就在于:单目标经济最优的调度,一定会把电池资产往死里用。
举个例子。如果不给储能循环次数、寿命损耗做任何惩罚,优化器会在电价低谷充满、高峰放空,完美执行每日一次完整循环,这在数学上成本确实最低。可一旦把电池更换成本折算进运行成本,这个策略很可能就是亏的。V2G也是同样道理:电价尖峰时段把所有电动汽车的电全部抽出来卖给本地负荷或返送电网,微网账面成本降下来了,车主群体的充电费、离网SOC满意度却会大幅恶化。
所以在一个可落地的“光储充+V2G”调度系统里,至少需要两个相互冲突的目标:
- 运行经济性目标:与主网的购售电费用、储能循环损耗、V2G放电补偿等多项成本之和最小;
- 用户侧或资产侧目标:尽量满足车主离网时的期望SOC,或让充电费用更公平,或减少蓄电池等效循环损耗。
这两个目标通常没法同时达到最优,只能得到一组帕累托(Pareto)非劣解,让决策者根据当天实际场景选择一个折中方案。看到这里你大概也明白了,论文里常说“多目标优化”,不是故弄玄虚,而是这种物理系统本身确实存在多个利益主体,优化目标天然是多维的。
1.3 问题规模与时间粒度:别一开始就上重型算法
我见过不少人在刚把模型搭出来时就急着调NSGA-II或者MOPSO,跑了几个小时不出结果,还以为是算法参数问题,实际上问题出在决策变量太多、约束非线性太强或者时间粒度选择不合理上。
建模前先把调度周期和时间粒度定下来。常见做法有三种:
- 24小时调度,1小时一个时段:T=24,适合日前调度,变量规模小,适合先验证模型逻辑;
- 24小时调度,15分钟一个时段:T=96,能捕捉更细的负荷波动和电价变化,但对预测数据要求高;
- 滚动优化:每次优化未来4-6小时,15分钟粒度,更接近工程实际,但代码复杂度明显增加。
我的习惯是先用T=24的日前调度把模型逻辑完全跑通,确认没有不可行约束和病态数值之后,再在需要的时候把采样时间缩短到15分钟。V2G场景还需要格外注意一个问题:车辆到达和离开通常发生在整点附近,用1小时间隔可以和通勤行为对齐,反而比15分钟更容易处理用户行为边界。
2. 从物理系统到数学表达式:光储充微网怎么建模才不容易翻车
2.1 设备模型:光伏、储能、V2G的数学抽象要“够用但不过度”
做优化调度不需要把电力电子暂态模型搬进来,那是Simulink里做电磁暂态仿真的事情。调度层面的模型只需要把能量时间和功率边界描述清楚。
光伏部分用一个日前预测出力序列 Ppv(t) 就够了,再加一个弃光变量 Pcurt(t),表示光伏实际出力可以在预测值以下调整,用来保证任意时刻系统都能满足功率平衡。很多人在模型里不给弃光变量,一旦负荷很低、储能满充、V2G不参与时功率就会冗余,数学上直接导致无解,这就是典型建模不完整。我在代码里通常会写到约束: P_pv_use(t) = Ppv_forecast(t) - Pcurt(t) 且 0 ≤ Pcurt(t) ≤ Ppv_forecast(t)。
储能部分要用SOC递推约束而不是简单的能量守恒。设储能额定容量为 Cap,充电功率 Pch(t)、放电功率 Pdis(t),充电效率 ηch、放电效率 ηdis,那么SOC序列 Sess(t) 满足: Sess(t+1) = Sess(t) + (ηch * Pch(t) - Pdis(t) / ηdis) * Δt / Cap
这里有两个容易出错的细节。
第一,效率放在充电侧还是放电侧?不同文献习惯不同,但你要保持整个算例里能量平衡自洽,否则最后统计储能损耗时会莫名少了能量。我习惯充电乘效率、放电除效率,也就是电从电网进入电池要打折,电池放出到母线也要打折,两端都有能量损耗,看起来更贴近物理实际。
第二,Δt 的单位要统一。如果功率单位是kW,容量单位是kWh,Δt 是小时。把Δt写成1就完事的前提是你已经把功率折算成了每时段的电量。否则SOC递推会整体偏大或偏小,这个错误往往要在结果阶段才发现。
V2G部分,我不建议对每一辆电动汽车都单独建一个SOC状态变量。几十辆车乘以24个时段,变量数量一下子膨胀,求解效率恶化,而且单车的出行行为数据根本拿不干净。更实用的做法是采用“聚合V2G模型”:把愿意参与调度的电动汽车看成一个等效储能池,每个时段可用的总充电功率上限、总放电功率上限、总能量上限,分别由当前在网车辆数和平均剩余电量折算得到。
用公式描述就是: Pv2g_dch_min(t) ≤ Pv2g_dch(t) ≤ Pv2g_dch_max(t) SOCv2g(t+1) = SOCv2g(t) + (ηv2g_ch * Pv2g_ch(t) - Pv2g_dch(t) / ηv2g_dis) * Δt / Capv2g_total
同时在最后一个时段施加“车辆离网总SOC期望”约束: SOCv2g(T+1) ≥ SOCv2g_expect
这个约束是整篇文章里V2G和普通储能模型的本质区别。如果没有它,你又回到了把车当固定电池用的套路,调度结果的参考价值会大大下降。
2.2 目标函数构造:成本和“不那么容易量化的目标”怎么放进去
目标一,运行成本最小化。我在模型里通常包含四项:
- 主网购电费用:sum(Price_buy(t) * Pbuy(t) * Δt),这里 Pbuy 是从上级电网买电的功率,价格用分时电价。
- 主网售电收益:减去 sum(Price_sell(t) * Psell(t) * Δt),目前分布式光伏上网电价一般低于购电价格,模型里要保证 Price_sell < Price_buy,否则求解器可能通过买卖套利制造不现实的收益。
- 储能循环损耗折算成本:对每一次充放电循环按等效成本惩罚,或者简化为对放电电量乘以一个单位损耗成本。很多文献写“电池寿命损耗成本”其实只是用了线性近似,但对于容量型调度应用,线性近似已经足够,做研究时可以在灵敏度分析里讨论这个参数的取值。
- V2G放电补偿成本:用户不会无缘无故让车反向放电,运营商或微网必须给予补偿。补偿单价一般设置在峰时电价以下,否则V2G的介入反而可能导致成本失控。
目标二,用户侧满意度最大化。最直观的量化方式是让车主群体的离网SOC尽量高,可以写成最小化离网SOC与期望SOC的偏差: min sum( max(0, SOC_v2g_expect - SOC_v2g(T+1))^2 ) 或者把时段充电需求纳入目标,避免低谷不充电、高峰才充电的“假省钱”行为。
我实际跑下来的体会是:两个目标已经能让问题的博弈关系非常清晰,不建议新手一开始就做三目标,比如经济性、寿命、网损同时优化。三目标会让帕累托前沿的可视化和决策变得很复杂,代码调试难度会明显上升,很多时候得到的一大片解在工程上并没有本质区别,反而不利于论文或报告的结论提炼。
2.3 约束条件:漏掉哪个都会让结果“看着正常,实际不可行”
约束是优化问题的灵魂,我在这块吃过不少亏。一个可行、可信的光储充+V2G调度模型,至少需要以下约束:
功率平衡约束。这几乎是所有微网模型的定海神针: Pbuy(t) + Ppv_use(t) + Pdis(t) + Pv2g_dch(t) = Pload(t) + Pch(t) + Pv2g_ch(t) + Psell(t)
注意功率平衡里每一项都要写全。漏了Psell,光伏大发时段就没有多余功率的去路;漏了Pv2g_ch,车辆充电负荷凭空消失,整个系统就少了一大块负荷,平衡关系完全失真。
储能和V2G的SOC范围约束。除了上下限,还要注意终端状态约束。储能通常要求调度周期结束SOC回到初始值或者不低于某个值,否则前一天把电池放空,第二天早上连基础调节能力都没有。V2G则要设置离网SOC下限,这部分在第2.1节已经提过。
充放电功率上下限约束。每个时段储能充电功率和放电功率都不能超过额定限值,V2G的聚合充放电功率也要按照当前在网车辆数折算,而不是始终用峰值车辆数。
充放电互斥约束。理论上储能不会同时充电和放电,但因为充电功率和放电功率是两个独立变量,求解器有可能为了满足某个目标让它们同时大于零,这在物理上是荒谬的。处理办法通常是用一个二进制变量: Pch(t) ≤ Pch_max * b(t) Pdis(t) ≤ Pdis_max * (1 - b(t)) 当b(t)=1时只允许充电,b(t)=0时只允许放电。购电和售电之间同样需要一组互斥约束。
联络线功率约束,也叫变压器容量约束: Pbuy(t) + Psell(t) ≤ Pline_max 这决定微网和主网之间的最大交换功率,在实际工程里非常关键。漏了它,求解器可能给出一个需要超大容量变压器的方案,完全没有经济可行性。
我给一个最常被新手忽略的提示:不要等到所有目标函数都写完了再检查约束的物理一致性。先把约束集合单独拉出来跑一个“只找可行解”的测试,也就是把目标函数设成常数0,看求解器能不能找到一组满足所有约束的可行解。如果这一步都过不去,后面所有优化结果都没有讨论意义。
3. 多目标优化到底用哪种解法:数学模型法和群智能算法的分界线
3.1 先把单目标试算跑通,它比任何调参都有用
我在做多目标之前一定会先把单目标跑一遍,这不仅是模型验证的手段,还能给后续多目标分析提供重要的边界参考。比如先跑一遍“仅最小化运行成本”的单目标优化,得到最低成本 f1_min;再把目标换成“仅最大化用户SOC满意度”,得到 f1_max。这两个值能告诉你每个目标的理论可达范围,后续做归一化和Pareto前沿分析都离不开它。
另外,单目标模型如果用的是线性或混合整数线性规划求解器,结果具有全局最优性保证。如果多目标的结果落在单目标全局最优解之外,那就要怀疑是不是算法早熟或者约束写错了。
3.2 加权求和法什么时候够用
加权求和是处理多目标最朴素的方法:min w1f1 + w2f2,通过调整w1和w2把双目标压成单目标。这个方法在工程上一直没有被淘汰,原因是它足够快,而且能够给调度员一个明确的执行方案。
但它有两个非常现实的问题:
- 权重没有物理含义。w1=0.7、w2=0.3 并不表示“经济性占70%”,因为成本可能是几万元量级,用户SOC偏差可能是0~0.5,量纲都不一样,直接加权会把小量纲目标完全淹没。
- 无法表示帕累托前沿的非凸部分。对于非凸目标空间,有些帕累托最优解永远无法通过加权求和得到。
解决第一个问题的办法是归一化,把两个目标都除以各自单独优化时得到的最优值,或者除以目标取值范围,让两个目标都落在0到1附近,再去调权重。解决第二个问题的答案是使用真正的多目标进化算法或者epsilon约束法。
3.3 帕累托方法的适用场景
当模型本身存在非线性目标或约束,或者决策者明确需要多个可选方案来做权衡分析,这时候用NSGA-II这类多目标进化算法就是合理的。
我自己的选择原则可以总结成一张表:
| 情况 | 推荐解法 |
|---|---|
| 约束全部线性,含二进制变量 | YALMIP + Gurobi/CPLEX 做 MILP 精确求解 |
| 目标或约束含非线性项 | MINLP求解器,或线性化后再用 MILP |
| 需要给出一组Pareto替代方案 | NSGA-II、MOPSO 等进化算法 |
| 问题规模极大,车辆数上千 | 聚合模型 + 数学规划;微观模型用启发式近似 |
请特别注意,我不认为“多目标优化”就应该等于“用NSGA-II”。如果你的模型线性化后规模适中,Gurobi几分钟能拿到很多Pareto点(通过epsilon约束法逐次求解),这个结果比进化算法更有说服力,因为它有全局最优性差距信息。NSGA-II的价值更多体现在非线性、非凸、变量耦合复杂这些数学规划很难处理的场景里。这个判断也会影响审稿人对你工作的评价。
4. MATLAB落地实现:从YALMIP建模到NSGA-II代码骨架
4.1 为什么用YALMIP而不是手写优化器
MATLAB里手写内点法或者单纯形法去求解调度问题,作为学习线性规划原理可以,但用来做研究效率太低了。YALMIP这个工具箱最大的优势是建模语言贴近数学公式,你几乎可以把上式中的约束一行行翻译进代码,不容易出现代数错误。
配合的求解器我优先推荐Gurobi,学术许可免费,MILP求解速度极快。没有Gurobi时,MATLAB自带的 intlinprog 也能应付中等规模的混合整数线性规划,只是求解时间会慢一些。
在MATLAB中定义变量的方式大致如下: % 决策变量 Pbuy = sdpvar(1,T); % 向主网购电功率 Psell = sdpvar(1,T); % 向主网售电功率 Pch = sdpvar(1,T); % 储能充电功率 Pdis = sdpvar(1,T); % 储能放电功率 Pcurt = sdpvar(1,T); % 弃光功率 b_ch = binvar(1,T); % 储能充放电互斥标志 SOC_ess = sdpvar(1,T+1); % 储能SOC序列
注意这里SOC序列长度是T+1,因为要描述从0时刻到T时刻的递推关系。把SOC(1)当成初始SOC,SOC(T+1)当成调度结束SOC,这个长度错位问题在写约束循环时非常容易搞混,我建议先在草稿纸上把时间轴画出来,再对应写循环。
4.2 YALMIP约束和目标的常规写法
给出一段骨架,展示功率平衡和SOC递推的写法:
% 约束集合 Cons = []; for t = 1:T % 功率平衡 Cons = [Cons, Pbuy(t) + Ppv_use(t) + Pdis(t) + Pv2g_dch(t) == ... Pload(t) + Pch(t) + Pv2g_ch(t) + Psell(t)]; % 光伏实际出力等于预测减去弃光 Cons = [Cons, Ppv_use(t) == Ppv_forecast(t) - Pcurt(t)]; Cons = [Cons, 0 <= Pcurt(t) <= Ppv_forecast(t)];
% 储能充放电功率限值以及互斥约束 Cons = [Cons, 0 <= Pch(t) <= Pch_max * b_ch(t)]; Cons = [Cons, 0 <= Pdis(t) <= Pdis_max * (1 - b_ch(t))]; % 储能SOC递推 Cons = [Cons, SOC_ess(t+1) == SOC_ess(t) + ... (eta_ch * Pch(t) - Pdis(t) / eta_dis) * dt / Cap_ess]; Cons = [Cons, SOC_min <= SOC_ess(t+1) <= SOC_max];end
% 初始和终止SOC耦合约束 Cons = [Cons, SOC_ess(1) == 0.2]; Cons = [Cons, SOC_ess(T+1) >= 0.2];
目标函数用一个示例示意: Cost = sum(Price_buy .* Pbuy * dt) - sum(Price_sell .* Psell * dt) ... + sum(deg_cost * Pdis * dt) ... + sum(v2g_compensate * Pv2g_dch * dt) ... + sum(penalty_curtail * Pcurt * dt);
ops = sdpsettings('solver', 'gurobi', 'verbose', 2); optimize(Cons, Cost, ops);
这里把储能损耗和弃光惩罚纳入目标函数,并不只是数学上的“软约束”,它还起到了正则化的作用:优化器在解不唯一时,会倾向于不弃光、不过度损耗电池,而不是随意选择某个麻烦解。
4.3 如果用NSGA-II,应该怎样组织代码结构
并不是所有情况都适合用YALMIP+Gurobi。如果目标函数里有比较强的非线性,或者你要展示Pareto前沿那套分析,可以切换到NSGA-II。MATLAB里如果没有全局优化工具箱,手写一个NSGA-II其实不复杂,核心只需要四步:
- 种群初始化与编码。把每个时段的储能充放电功率以及V2G放电功率直接编码为决策变量。对于24小时系统,变量数量大约在48~96之间。变量越多搜索空间越大,建议先用单目标结果的范围缩窄变量的上下界,例如储能的充放电功率不必让它在[-∞,∞]里搜索,而是直接给定[-Pch_max, Pch_max]。
- 约束处理。进化算法不能直接处理等式约束,所以我习惯把功率平衡约束转化成罚函数加入适应度。SOC递推约束则可以代入递推公式直接计算,不作为额外罚项,这样可以减少罚函数数量。
- 非支配排序与拥挤度选择。这是NSGA-II的核心机制。对所有个体按帕累托支配关系分层,同一层内按拥挤距离排序,优先保留分布更稀疏的解。
- 交叉变异。对实数编码变量用模拟二进制交叉(SBX)和多项式变异,对二进制互斥变量用均匀变异。进化代数我习惯控制在200左右,种群大小视变量数量而定,24时段问题用100~150就够。
做完一轮优化后,你会得到一组Pareto解集,每个个体对应一套完整的日前调度方案。这个时候不要急着选最优解,而是把前沿画出来,结合系统约束看哪些解在实际中是可执行的,哪些解只是数学上最优但会让储能SOC波动过于剧烈。
5. 调度结果怎么看:用算例检验V2G是否真的在“锦上添花”
5.1 一个用于演示的算例配置
我构想的演示算例可以参考以下参数,适合做研究验证但具体工程场景需实际标定:
- 光伏装机:200 kWp,典型日预测发电量约1500 kWh;
- 储能:100 kW / 200 kWh,初始SOC 20%,充放电效率95%;
- 常规负荷:日用电量约2500 kWh,峰值负荷出现在傍晚;
- V2G聚合体:参与车辆60辆,在网时间主要是白天通勤时段,聚合可调容量约400 kWh,单时段最大充放功率120 kW;
- 分时电价:峰时1.2元/kWh,平时0.7元/kWh,谷时0.4元/kWh。
在这个条件下,我对比三种情景:
- A:无V2G参与,仅光储联合优化经济性;
- B:有V2G参与,但是只优化微网购电成本,不约束车主离网SOC;
- C:有V2G参与且约束车主离网SOC不低于0.55,按双目标得到一组折中解。
为了让结果不要看起来像随手编的,下面的表格只表示数字之间的关系,不对应某个特定日期,主要说明V2G参与前后调度方案的变化逻辑。
| 情景 | 日运行成本 | 主网峰值购电功率 | V2G总放电量 | 离网平均SOC | 储能等效循环次数 |
|---|---|---|---|---|---|
| A | 690 元 | 154 kW | 0 kWh | — | 0.8 |
| B | 622 元 | 138 kW | 430 kWh | 0.35 | 0.6 |
| C(折中解) | 652 元 | 142 kW | 210 kWh | 0.63 | 0.7 |
从这张表能读出几个关键结论:
第一,V2G参与确实降低了购电成本,因为部分峰时负荷由电动汽车反向放电供给,主网峰值购电功率也下降了。这算是V2G在技术经济性上最直白的价值。
第二,如果完全不做用户约束,情景B虽然成本最低,但车主离网平均SOC只有35%,可以说调度把电动汽车榨得太干了。这种情况放到真实停车场,车主大概当晚就会取消V2G签约。
第三,情景C把离网SOC约束抬高到55%以上后,V2G放电量明显收敛,成本相比情景B有所回升,但依然低于无V2G的基准情景A。这说明“合理参与”的V2G仍然能带来经济收益,只是收益没有“压榨式调度”那么好看,而这才是可落地的方案。
5.2 Pareto前沿长什么样,怎么解读
用NSGA-II或epsilon约束法画出来的Pareto前沿,通常是以运行成本为横轴、以用户SOC偏差为纵轴的一条递减曲线。曲线的两端分别代表两个极端:
- 靠近左下角的点:成本低,但SOC满意度差;
- 靠近右上角的点:SOC保证充分,但成本高;
- 弯曲部分曲率大的区域:通常就是“性价比”较高的折中区域,也是调度员需要重点关注的解段。
在实际项目中,我从不会直接把Pareto前沿中某一个点拿出来说“这就是最优解”。多目标优化的产出本来就是一个解集,而不是唯一解。正确的用法是:让决策者根据当天的天气、电价、车辆参与率等因素,在Pareto前沿上人工选择一个点,这个点再落到具体调度时序上去执行。如果用户实在没有偏好,也可以用熵权法、TOPSIS等多准则决策方法自动挑一个折中解,但那已经是后处理问题,不属于优化模型本身的职责。
5.3 SOC曲线和功率时序图的检查方法
调度结果出来后,我把绘图看作最后一道验算。至少要看三类图:
储能SOC曲线是否光滑且在合理区间。如果SOC曲线出现剧烈锯齿,比如从80%瞬间跳到20%,大概率是SOC递推等式里效率或容量单位写错了,也可能是Δt没换算对。检查时沿时间轴手算几个点验证。
V2G聚合SOC曲线是否呈现“白天充、傍晚放”的合理规律。如果优化结果总在后半夜对车进行大功率充电,然后清晨又不放电,这个策略在用户行为上就不合理,可能你需要给V2G充电时段增加时间窗约束,比如只允许在车辆在网且满足通勤需求的前提下参与调度。
储能功率和V2G功率的同时性。借由互斥约束已经规避了单个设备同时充放,但还要看整体系统里储能放电和V2G放电是否会在同一时段大量叠加。如果叠加后导致主网购电功率接近零甚至出现大量返送电,而分时电价又在峰时,那说明能量管理的时段协同已经达到了预期效果。
MATLAB里的plot与stairs足够完成这些检查,关键不是画图技巧,而是你要自己在图上追问“这个调度结果在物理上会不会被执行”。我每跑完一组数据,都会挑两三个典型时段,手动核算功率平衡和时间递推,这个习惯帮我抓出过至少三处代码错误。
6. 这个模型里最难绕开的三个坑,以及我的处理思路
6.1 SOC初值:一个小小的初值扰动,可能毁掉一整天调度
很多人的模型只在约束里写了SOC的上下限,却没认真约束SOC初值。实际上,调度优化是个跨时段决策问题,第一时段的SOC初值会通过递推等式影响全天所有时段的可行域。
比如储能初始SOC设成80%,夜间电价低谷时段充电空间就非常小,储能可能在负荷高峰来临前已经逼近SOC下限,导致傍晚没有电可放。V2G也有同样的道理:如果把每辆车进场时的初始SOC都设成100%,那白天的调度几乎不需要给车充电,傍晚却有大量能量可以放,整个调度策略会过于乐观。
我的做法是,把初始SOC当作一个明确的输入参数而不是随便设个0.5了事。储能初始SOC按照日前调度结束时的实际值回代,V2G聚合初始SOC按照通勤车辆的统计平均剩余电量来设置。这样得到的调度结果才具备跨日连续运行的前提。
6.2 目标量纲不一致:成本和SOC偏差直接加权,权重调了等于白调
这是我在加权法里栽得最深的一个跟头。早期版本里目标函数是“0.5 * 运行成本 + 0.5 * SOC偏差”,结果无论怎么调权重,优化器都只盯着成本项,因为成本是几百元量级,SOC偏差是0.0几量级,后者的梯度几乎可以被忽略。
后来我改成先分别求解两个单目标问题,得到成本目标的最小值和最大值、SOC偏差目标的最小值和最大值,然后把目标函数改写成归一化形式: f1_norm = (f1 - f1_min) / (f1_max - f1_min) f2_norm = (f2 - f2_min) / (f2_max - f2_min) min w1 * f1_norm + w2 * f2_norm
这样w1和w2才真正具备“偏好权重”的含义。对于Pareto前沿分析,我通常直接用epsilon约束法,把SOC满意度作为约束逐步收紧或放松,来枚举前沿点,这个方法比一遍遍试权重稳定得多。
6.3 二进制互斥变量与进化算法结合时的变通处理
用YALMIP+Gurobi时,储能充放电互斥可以干净地写成两个线性不等式加一个二进制变量。但切到NSGA-II时,二进制变量会让编码和变异操作变复杂,而且罚函数处理等式约束的难度也会上升。
我的变通方案是:把每个时段的储能功率定义为一个有符号变量 P_ess(t),正值表示充电、负值表示放电。有了这个变量,充电功率 Pch(t) = max(0, P_ess(t)),放电功率 Pdis(t) = max(0, -P_ess(t)),在目标函数和SOC递推式中分别代入。这样互斥问题自动满足,不需要增加二进制变量。
代价是引入了max函数,目标函数变成非线性。但这种非线性在进化算法里根本不是问题,因为进化算法本来就不要求凸性和线性。储能循环损耗和V2G放电补偿项也都能写成基于有符号变量的函数形式。走这条路之后,NSGA-II编码只需要一个长度为T的储能功率向量加上一个长度为T的V2G功率向量,代码清爽很多,调试也方便。
有一点要特别提醒:用max函数表达充电/放电功率后,储能某个时段不可能同时充电和放电,这个条件在单个时段内成立;但如果时间粒度很粗,一个小时内先充后放这种物理上可能发生的状态会被模型忽略,这是时间聚合误差,需要自己评估是否能接受。
7. 关于这段代码还有哪些能继续扩展的方向
这套模型本质上是确定性日前调度,所有光伏预测、负荷预测、车辆到达时间都是“已知的”。如果要做更接近工程落地的版本,可以沿两个方向扩展:一是把不确定性考虑进来,光伏预测误差用鲁棒优化或者随机场景法建模,车辆到达离开时间用蒙特卡洛抽样描述;二是从日前调度走向日内滚动优化,每15分钟重新求解一次未来4小时的调度问题,用更新后的预测修正储能和V2G的执行策略。说白了,目前这套代码解决的是“知道明天会发生什么,今天怎么排计划”的问题;现实世界更难的往往是“明天预测不准,该怎么边做边改”。如果是做毕业论文,把日前调度做完通常已经能满足阶段性目标,再往上加滚动优化需要额外的时间成本和代码工作量,建议先把基础版打磨扎实再做扩展。我个人实际做完这套模型的体会是,最大的收获不是某个漂亮的最优成本数字,而是逐渐建立起了对“约束比目标更重要”的直觉——只要把物理边界和用户边界描述清楚,优化算法本身反而只是工具箱里的一个选项而已。