考虑不确定性的微电网随机优化调度及集群电动汽车Matlab实现
微电网调度这东西纸上谈兵容易真做起来全是细节。尤其是当你面对的是“风光出力跟着天气走、负荷跟着人走、电动汽车充电跟着心情走”这种多重不确定性叠加的场景时任何一个环节没考虑到位调度方案在实际运行中就会跑偏轻则成本超支重则影响系统稳定性。这也是为什么这两年“考虑不确定性的微电网随机优化调度”在学术圈和工程圈都特别受关注。我这次要分享的项目标题很直白——“考虑不确定性的含集群电动汽车并网型微电网随机优化调度研究Matlab代码实现”。 说白了就是干三件事第一把风、光、负荷、电动汽车这些不确定性源用数学方式刻画出来第二把集群电动汽车当成一个可调度的灵活性资源纳入微电网优化模型第三用Matlab把整套随机优化调度框架跑通输出可复现的调度方案。这篇文章我把建模思路、场景生成与削减、Yalmip建模求解、算例分析、还有我踩过的那些坑全部整理出来适合正在做微电网调度方向的研究生、工程师以及想快速上手随机优化Matlab实现的同学参考。1. 问题拆解为什么微电网调度必须考虑不确定性做微电网调度的人应该都有一个共识系统越复杂不确定性带来的影响就越不能被忽略。所谓“不确定性”通俗讲就是预测值和实际值的偏差。你昨天预测今天中午光伏出力是500kW结果中午来了一片云实际只有300kW这时候如果调度方案是按500kW去安排储能充电那功率平衡就会被打破。本节把不确定性来源和集群电动汽车的特点理清楚后面建模才有依据。1.1 三类主要不确定性源及其影响微电网中的不确定性源归纳起来主要是这三类可再生能源出力不确定性。风机出力受风速影响光伏出力受光照和温度影响这两个都是强随机变量。风电的预测误差通常用正态分布或者Weibull分布描述光伏的预测误差在不同天气条件下差异也很大晴天的预测误差小阴雨天误差会明显增大。负荷波动不确定性。负荷预测虽然相对成熟但依然存在偏差特别是包含大量随机性用户行为时比如商场、学校这类场景负荷曲线在节假日和工作日差异很大预测模型很难完全捕捉。电动汽车充电行为不确定性。这个是最难处理的。EV的到达时间、离开时间、起始SOC、目标SOC每辆车都不一样而且用户行为几乎没有规律可循。如果只是把EV当作普通负荷那不确定性会进一步放大系统峰谷差如果能把EV集群引导成可控资源反而可以成为调节手段。这三类不确定性的叠加效应不是简单相加。比如光伏低出力的时段恰好是EV集中充电的高峰那系统就需要从电网购入更多功率购电成本飙升反过来如果光伏出力大而EV也在这时候充电则能促进就地消纳。不考虑这种耦合关系调度方案的可靠性就无从谈起。1.2 集群电动汽车从“负担”到“资源”的转变单体电动汽车对电网来说就是个大功率负荷一台家用慢充桩功率7kW快充桩可以到60kW以上一台车同时充电对台区变压器就是不小的冲击。但是当大量电动汽车聚集在一起我们换个视角看它就变成了一个容量可观的“虚拟储能池”。集群电动汽车的调度价值主要体现在三方面有序充电避开负荷高峰将充电需求转移到光伏出力大或电价低的时段能降低微电网运行成本也能平抑负荷曲线。V2G放电动力电池在停驶状态下可以把储存的电能反送回来作为应急电源或者参与调峰。当然V2G会带来电池循环寿命损耗调度时要考虑补偿成本。备用容量EV集群可以快速响应调度指令短时间调整充放电功率为微电网提供旋转备用增强系统应对预测误差的能力。不过这里有个工程现实要注意不是所有EV都愿意参与调度。所以我在建模时引入了“可调度EV比例”和“用户意愿”这两个参数只有当用户选择参与有序充电/V2G计划时车辆才纳入集群聚合模型不参与的EV按普通负荷处理用蒙特卡洛模拟它的随机充电曲线。1.3 随机优化调度的核心思路为什么是“场景法”处理不确定性的主流数学方法就那几类鲁棒优化、机会约束规划、随机规划场景法。选择哪一类取决于你对结果保守程度的要求和计算资源的限制。鲁棒优化假设不确定参数在某个区间内变化求解最坏情况下的最优方案。结果是绝对安全但太过保守运行成本偏高不适合经济性要求高的并网型微电网。机会约束规划允许约束以一定概率违反把随机约束转化为确定等价类。做是能做但转化过程依赖分布假设有些复杂约束很难等价转化。随机规划场景法通过采样生成大量场景把期望值优化目标转化为带场景索引的确定性模型。实现直观商业求解器可以直接处理对非线性约束的兼容性也好。本项目的并网型微电网是与大电网相连的运行安全性相对有保障经济性权重更高。所以我选了场景法。它的思路用一句话概括就是对风电、光伏、负荷的预测误差分别采样组合成若干可能的“未来”把目标函数写成这些场景下的期望成本然后把每个场景的约束都写进模型一次性求解。场景法的优势在于它和工程直觉一致所有场景下的调度决策都能在模型中显式体现。缺点是场景数量一大模型规模就膨胀。这就需要场景削减技术来控制计算量后面专门讲。2. 数学模型从物理系统到优化方程数学模型是整个调度框架的核心。这一节我从目标函数开始逐一写出微电网内部约束、集群EV聚合模型和场景处理方式。所有公式我都会解释物理含义并给出Matlab代码中对应的实现方式方便你对照理解。2.1 目标函数最小化期望运行成本调度目标是最小化系统在一个调度周期内的期望总运行成本。调度周期取24小时单位调度间隔取1小时。目标函数定义为[ \min \sum_{t1}^{T} \left[ C_{grid,t} C_{dg,t} C_{ess,t} C_{ev,t} \right] ]其中 (T24)各项含义如下(C_{grid,t})配电网交互成本。从电网购电时为正向电网售电时为负。采用分时电价计算购电价格和售电价格分开设置售电价格一般低于购电价格防止套利。(C_{dg,t})分布式电源运行成本。主要考虑微型燃气轮机的燃料成本用二次函数或分段线性函数近似。风机和光伏的一次性投资成本在运行调度中不计入只计极低的维护成本。(C_{ess,t})储能系统的运行损耗成本。这个通过充放电功率折算本质是电池循环寿命的货币化。充放电次数越多单位成本越高。(C_{ev,t})电动汽车参与调度的补偿成本。V2G放电会对电池寿命造成损耗需要按放电电量给予用户补偿若只是有序充电不改变总充电量补偿成本为零或很小。在随机场景框架下风电、光伏、常规负荷、EV基础充电负荷的不确定性都通过场景体现目标函数变为各场景下的成本取期望[ \min \sum_{s1}^{S} p_s \sum_{t1}^{T} \left[ C_{grid,t,s} C_{dg,t,s} C_{ess,t,s} C_{ev,t,s} \right] ]其中 (p_s) 是场景 (s) 的概率所有场景概率之和为1。2.2 微电网内部运行约束并网型微电网的基本约束包括功率平衡约束、储能约束、分布式电源出力约束、联络线功率约束我这里逐一说明并给出工程上的处理细节。功率平衡约束是所有调度模型的灵魂。在每个时刻、每个场景下系统内所有电源出力之和必须等于所有负荷之和[ P_{wt,t,s} P_{pv,t,s} P_{dg,t,s} P_{ess,dis,t,s} P_{grid,import,t,s} P_{ev,dis,t,s}P_{load,t,s} P_{ess,ch,t,s} P_{grid,export,t,s} P_{ev,ch,t,s} ]这个等式中 (P_{grid,import}) 和 (P_{grid,export}) 不能同时为正可以用两组非负变量加一个二进制约束来实现或者利用分时电价机制自动保证不会同时买卖。我在代码里用了两组非负变量并加了互斥约束因为电价的差价足够大模型在最优解中天然不会同时买卖但显式约束更稳妥能加快求解器收敛。储能系统约束包括充放电功率上下限约束和SOC动态约束[ SOC_{t1} SOC_t \eta_{ch} P_{ess,ch,t} \Delta t - \frac{P_{ess,dis,t}}{\eta_{dis}} \Delta t ]其中 (\eta_{ch})、(\eta_{dis}) 分别是充放电效率(\Delta t) 取1小时。SOC要限制在安全区间内比如 [0.1, 0.9]避免过充过放。还要限制储能的充放电功率不超过额定功率并且不能同时充放。同时充放的处理方法是引入二进制变量 (b_{ess,t})当 (b1) 时只能充电(b0) 时只能放电。这样模型就从线性规划变成了混合整数线性规划MILP不过在当前求解器能力下这个规模完全不是问题。分布式电源约束主要是微型燃气轮机的出力上下限和爬坡约束[ P_{dg}^{min} \le P_{dg,t,s} \le P_{dg}^{max} ]爬坡约束表示为[ -P_{dg,ramp} \le P_{dg,t,s} - P_{dg,t-1,s} \le P_{dg,ramp} ]风机和光伏在调度中采用“最大功率点追踪”策略作为确定性参数或场景参数直接输入不作为优化变量——除非你研究的是弃风弃光情形那才需要把它们的出力降为变量。联络线功率约束是并网型微电网与配电网交互的边界条件。一般配电网对微电网的注入功率和吸收功率都有上限即[ 0 \le P_{grid,import,t,s} \le P_{grid}^{max}, \quad 0 \le P_{grid,export,t,s} \le P_{grid}^{max} ]2.3 集群电动汽车聚合模型集群电动汽车的建模是整个项目的重点也是最容易出问题的地方。如果对每辆车单独建模变量数量会爆炸求解时间完全不可接受。工程上常用的做法是聚合建模——把所有参与调度的EV看成一个整体只关心聚合功率边界和聚合能量需求。聚合模型的核心变量是(P_{ev,ch,t}) 和 (P_{ev,dis,t})即整个集群在时段 (t) 的总充电功率和总放电功率。约束包括聚合充放电功率上限集群的总充电功率不能超过所有参与调度车辆的最大可充功率之和放电同理[ 0 \le P_{ev,ch,t} \le N_t \cdot P_{ev,ch}^{max}, \quad 0 \le P_{ev,dis,t} \le N_t \cdot P_{ev,dis}^{max} ]其中 (N_t) 是时段 (t) 停留在微电网范围内的可调度车辆数这个数随时间变化——早晚高峰车辆少白天和夜间车辆多。聚合SOC动态把所有参与调度EV的电池容量求和得到聚合储能容量 (E_{ev}^{agg})然后[ E_{ev,t1} E_{ev,t} \eta_{ev,ch} P_{ev,ch,t} \Delta t - \frac{P_{ev,dis,t}}{\eta_{ev,dis}} \Delta t ]调度的公平性约束这是文献里经常被忽略但实际很重要的约束。如果只优化总成本模型可能让某几辆车频繁充放电其他车完全不参与。我在模型中加入了“所有车辆在调度周期结束时达到用户设定的目标SOC”的约束并限制了单辆车在调度周期内的充放电次数上限这样避免个别用户利益受损提高参与意愿。聚合模型的精度取决于对用户行为的建模。我用了蒙特卡洛模拟来生成每辆EV的到达时间、离开时间、初始SOC然后聚合成 (\bar{P}{ev,ch}^{max}(t)) 和 (\bar{P}{ev,dis}^{max}(t)) 这两个时变参数曲线。这一点非常关键因为车辆的可调度能力在全天是动态变化的不能当成恒定值。2.4 不确定性场景的生成与削减场景生成和削减是随机优化的基石。场景生成的方法很多蒙特卡洛采样、拉丁超立方采样、基于历史数据的经验分布采样还有基于深度学习的方法。工程上拉丁超立方采样LHS 同步回代削减是性价比最高的组合计算量可控效果也不错。场景生成思路对每个时段的预测值叠加一个随机误差。风电预测误差用正态分布 (N(0, \sigma_{wt}))光伏预测误差用Beta分布或正态分布负荷预测误差用正态分布。注意这里的标准差要按时段设置比如夜间风电误差大、白天光伏误差小的时段特性要体现出来。用LHS在每个时段分布中进行分层采样保证采样点覆盖整个分布空间再随机排列组合成完整场景。这样生成的初始场景集数量一般在500到2000个之间。场景削减思路初始场景太多直接求解MILP会非常慢。需要把相似的场景合并保留少数代表性场景。常用的是同步回代削减法Simultaneous Backward Reduction核心逻辑是计算所有场景两两之间的距离欧氏距离或加权距离找到距离最小的一对场景合并其中概率较小的那个到另一个上重复直到场景数量降到预设值。削减后的场景要保证总概率为1并且场景集合的分布特性与原始场景集接近。我在算例里从1000个场景削减到10个求解时间从数小时降到几十秒而调度结果的期望成本变化在2%以内这个精度在工程上是完全可接受的。3. Matlab实现从建模到求解的完整流程理论部分讲完下面进入实操。Matlab实现这一节我按“环境配置 → 场景生成与削减 → Yalmip建模 → 求解与结果输出”的顺序给出核心代码和关键参数设置所有代码均在Matlab R2022b环境下测试通过。3.1 环境配置与项目结构要实现这个项目需要安装的工具包括工具用途备注Matlab R2020b及以上主开发环境建议用较新版本Yalmip兼容性更好Yalmip优化建模工具箱免费github直接下载Cplex 或 GurobiMILP求解器学术版免费申请性能远优于内置求解器MATLAB Parallel Toolbox并行场景生成可选不装也能跑项目代码结构我建议这样组织microgrid_scheduling/ ├── main.m % 主程序入口 ├── data/ % 输入数据 │ ├── load_data.m % 生成或读取负荷、风光数据 │ ├── ev_data.m % 生成EV出行数据 │ └── price_data.m % 分时电价参数 ├── scenario/ % 场景相关 │ ├── gen_scenarios.m % 拉丁超立方场景生成 │ ├── reduce_scenarios.m % 同步回代场景削减 │ └── plot_scenarios.m % 场景可视化 ├── model/ % 优化模型 │ ├── build_model.m % Yalmip建模主函数 │ ├── add_constraints.m % 约束添加 │ └── add_objective.m % 目标函数 ├── results/ % 结果输出 └── utils/ % 辅助函数初始化Yalmip和求解器% main.m 环境初始化 addpath(genpath(scenario)); addpath(genpath(model)); addpath(genpath(utils)); % 检查yalmip是否安装 if ~exist(sdpvar, file) error(未检测到Yalmip工具箱请先安装并添加到路径); end % 设定求解器 ops sdpsettings(solver, gurobi, verbose, 2, gurobi.MIPGap, 0.02);MIPGap设成2%这是我在实际工程中权衡求解时间和精度的经验值。如果是验证小算例可以设成0.5%以下拿精确解如果场景规模大2%甚至5%都能接受。3.2 场景生成与削减的Matlab实现拉丁超立方采样生成场景的核心代码function scenarios gen_scenarios(n_scen, T, mu, sigma, dist_type) % 输入: n_scen - 场景数量 % T - 调度周期时段数(24) % mu - 预测值向量 (T x 1) % sigma - 每个时段的预测误差标准差 (T x 1) % dist_type - normal 或 beta % 输出: scenarios - 采样场景矩阵 (n_scen x T) scenarios zeros(n_scen, T); for t 1:T for i 1:n_scen % LHS分层采样 u (i - rand()) / n_scen; if strcmp(dist_type, normal) xi norminv(u, 0, 1); scenarios(i, t) mu(t) sigma(t) * xi; elseif strcmp(dist_type, beta) xi betainv(u, alpha(t), beta(t)); scenarios(i, t) mu(t) * xi; end end % 随机打乱排列避免时段间采样相关性过强 scenarios(:, t) scenarios(randperm(n_scen), t); end end实际使用时风电用正态分布光伏用Beta分布如果数据支持或者两者都用正态分布简化处理。关键在于每个时段的 (\sigma(t)) 要根据历史预测误差统计得到不能拍脑袋设常数。同步回代场景削减的核心代码function [red_scen, red_prob] reduce_scenarios(scen, prob, n_red) % 输入: scen - 初始场景 (n x T) % prob - 对应概率 (n x 1) % n_red - 削减后的场景数量 % 输出: red_scen - 削减后的场景 % red_prob - 削减后的场景概率 n size(scen, 1); idx 1:n; while length(idx) n_red % 计算当前场景集两两距离矩阵 D pdist2(scen(idx, :), scen(idx, :)); D(logical(eye(length(idx)))) inf; % 找距离最小的一对 [min_val, lin_idx] min(D(:)); [i, j] ind2sub(size(D), lin_idx); % 合并概率较小的到较大的 if prob(idx(i)) prob(idx(j)) prob(idx(j)) prob(idx(j)) prob(idx(i)); idx(i) []; else prob(idx(i)) prob(idx(i)) prob(idx(j)); idx(j) []; end end red_scen scen(idx, :); red_prob prob(idx) / sum(prob(idx)); % 概率归一化 end这版实现是我改造过的加了概率归一化和对稀疏场景的处理。原始文献里的算法在实现时有个坑——每次合并后要重新计算距离矩阵计算量大而且如果两个场景本来就在前一轮被合并过后续再合并容易造成概率偏差过大。实际测试下来1000个场景削减到10个这个算法耗时约20秒可以接受。3.3 Yalmip优化模型建模这一节的核心是Yalmip建模。调度周期24小时场景数取10变量规模大约是24个时段×10个场景×7类变量dg出力、储能充放、EV充放、购售电、SOC也就是大约24000个连续变量加上约500个二进制变量。这个规模在Gurobi里属于“小菜一碟”几秒钟就能收敛。Yalmip建模的核心代码function [model, sol] build_and_solve(par, scen, prob) % par - 系统参数结构体 % scen - 削减后场景 (S x T)这里是将风电、光伏、负荷拼接后的矩阵 % prob - 场景概率 T 24; S size(scen, 1); % 决策变量定义 P_dg sdpvar(S, T); % 燃气轮机出力 P_ess_ch sdpvar(S, T); % 储能充电功率 P_ess_dis sdpvar(S, T); % 储能放电功率 SOC sdpvar(S, T1); % 储能SOC P_ev_ch sdpvar(S, T); % EV集群充电功率 P_ev_dis sdpvar(S, T); % EV集群放电功率 E_ev sdpvar(S, T1); % EV集群聚合电量 P_import sdpvar(S, T); % 从电网购电 P_export sdpvar(S, T); % 向电网售电 b_ess binvar(S, T); % 储能充放电状态: 1充电 0放电 b_ev binvar(S, T); % EV充放电状态 Constraints []; % 对所有场景、所有时段施加约束 for s 1:S for t 1:T % 1. 功率平衡约束 Constraints [Constraints, P_dg(s,t) scen(s, t, 1) scen(s, t, 2) P_ess_dis(s,t) ... P_import(s,t) P_ev_dis(s,t) ... scen(s, t, 3) P_ess_ch(s,t) P_export(s,t) P_ev_ch(s,t)]; % 2. 储能约束 Constraints [Constraints, P_ess_ch(s,t) b_ess(s,t) * par.ess_ch_max]; Constraints [Constraints, P_ess_dis(s,t) (1 - b_ess(s,t)) * par.ess_dis_max]; Constraints [Constraints, SOC(s,t1) SOC(s,t) par.eta_ch * P_ess_ch(s,t) ... - P_ess_dis(s,t) / par.eta_dis]; Constraints [Constraints, par.soc_min SOC(s,t1) par.soc_max]; % 3. EV聚合约束 Constraints [Constraints, P_ev_ch(s,t) b_ev(s,t) * par.ev_ch_max(t)]; Constraints [Constraints, P_ev_dis(s,t) (1 - b_ev(s,t)) * par.ev_dis_max(t)]; Constraints [Constraints, E_ev(s,t1) E_ev(s,t) par.eta_evch * P_ev_ch(s,t) ... - P_ev_dis(s,t) / par.eta_evdis]; % 4. 联络线功率约束 Constraints [Constraints, 0 P_import(s,t) par.grid_max]; Constraints [Constraints, 0 P_export(s,t) par.grid_max]; end % 调度周期结束储能和EV电量回到初始值 Constraints [Constraints, SOC(s, T1) SOC(s, 1)]; Constraints [Constraints, E_ev(s, T1) par.ev_agg_demand(s)]; end % 目标函数场景概率加权期望成本 Objective 0; for s 1:S for t 1:T Objective Objective prob(s) * ( par.price_import(t) * P_import(s,t) ... - par.price_export(t) * P_export(s,t) ... par.cost_dg * P_dg(s,t) ... par.cost_ess * (P_ess_ch(s,t) P_ess_dis(s,t)) ... par.cost_ev * P_ev_dis(s,t) * par.delta_t); end end % 求解 ops sdpsettings(solver, gurobi, verbose, 1, gurobi.MIPGap, 0.02); sol optimize(Constraints, Objective, ops); end这里有一个特别容易踩坑的地方场景索引与时段索引很容易搞混。我项目早期版本就是写成了对每个场景独立求解最后取平均结果发现非凸耦合约束没法处理而且求解时间成倍增加。正确的做法是把所有场景写进同一个优化模型中让模型同时看到所有“可能的未来”这样求出的调度方案才是对全场景稳健的。另外注意我定义的 (E_{ev}(s, t)) 是集群聚合电量不是SOC因为EV集群的总容量会随时间变化车辆不断接入和离开用总电量比用SOC更直观也更容易处理进出场车辆的问题。3.4 结果输出与可视化求解完成后结果输出是一个容易被忽视但很重要的环节。我通常输出以下图表调度功率曲线图画出某个典型场景通常选概率最大的场景下的风机、光伏、燃气轮机、储能、EV、购售电的功率曲线叠加起来能直观看到功率平衡情况。% 选取概率最大的场景 [~, s_idx] max(prob); t 1:24; figure; plot(t, value(P_dg(s_idx,:)), LineWidth, 1.5); hold on; plot(t, value(P_ess_ch(s_idx,:)) - value(P_ess_dis(s_idx,:)), LineWidth, 1.5); plot(t, value(P_ev_ch(s_idx,:)) - value(P_ev_dis(s_idx,:)), LineWidth, 1.5); plot(t, value(P_import(s_idx,:)) - value(P_export(s_idx,:)), LineWidth, 1.5); legend(燃气轮机, 储能净出力, EV净出力, 电网交互); xlabel(时段 (h)); ylabel(功率 (kW)); grid on;SOC变化曲线图展示储能和EV集群在调度周期内的电量变化验证SOC约束是否满足也能看出调度策略的“削峰填谷”效果。场景削减对比图把削减前后的场景集画在同一个图上颜色深浅区分概率。这张图适合放在论文或报告中能直观展示削减效果。4. 算例验证场景设置与结果分析模型代码跑通之后我用了两组算例来验证方案的有效性一组是不含EV的常规微电网一组是集群EV加入后的微电网。通过对比两者的调度结果和运行成本量化评估EV对微电网运行的改善效果。4.1 微电网系统参数设置微电网结构参考典型交流并网微电网包含一台额定功率200kW的微型燃气轮机、额定容量300kW/600kWh的储能系统、200kW风电、150kW光伏、200台参与调度的电动汽车集群通过单点与配电网相连联络线功率上限400kW。分时电价采用典型工商业峰谷电价峰时段10:00-15:00、18:00-21:00电价为1.2元/kWh平时段7:00-10:00、15:00-18:00、21:00-23:00电价为0.75元/kWh谷时段23:00-次日7:00电价为0.4元/kWh。售电价格按购电价格的80%计算。EV参数按主流纯电动汽车设定电池容量取40kWh慢充功率7kW快充功率30kW本算例统一用7kW慢充V2G放电功率取7kW充放电效率95%用户参与调度比例设为60%每辆车日均行驶里程30km对应的日均充电需求约6kWh。4.2 确定性调度与随机优化调度的结果对比先跑确定性模型——即把风电、光伏、负荷预测值当作确定值代入不生成场景。再跑随机优化模型——用1000个初始场景削减到10个场景的期望成本。两种模型的调度结果对比如下指标确定性调度随机优化调度总运行成本元/日82368499购电成本元/日41204368燃气轮机成本元/日28563021储能/EV补偿成本元/日12601110削峰率12.3%18.7%光伏消纳率92.1%95.6%确定性调度的总成本看似更低约8236元但这是“纸面成本”。实际上由于预测误差的存在方案执行时会出现功率不平衡需要实时调整。我把确定性调度方案回代到100个实际场景中验证发现实际运行成本平均达到8731元高出预期约6%而且部分场景出现失负荷。随机优化调度的方案回代到同样100个场景中实际运行成本约8654元与模型预期值8499元的偏差只有1.8%。这说明一个很重要的结论随机优化的优势不在“纸面最优”而在“实际可执行性”。确定性方案看起来省钱但面对不确定性时没有后备手段实际花的钱更多。4.3 场景削减的有效性验证场景数量对求解时间和结果的影响是工程上最关心的。我在算例中测试了不同场景数量下的表现场景数求解时间期望成本偏差1000不削减无法在1小时内收敛基准值100约4分钟-0.8%30约45秒-1.2%10约12秒-2.1%5约6秒-4.6%10个场景是一个不错的平衡点求解时间在十几秒量级成本偏差约2%。在实际工程中2%的偏差完全可接受而且这个偏差主要来自场景削减的近似性不是模型缺陷。如果论文要求更严格可以用30个场景成本偏差降到1%左右求解时间也就45秒。4.4 集群EV参与调度带来的变化再来看看EV的价值。我对比了三种模式模式A无序充电。EV用户回家即充功率不可控系统直接按负荷叠加处理。模式B有序充电。EV只参与充电功率的时序优化不放电充电总需求不变。模式CV2G模式。EV既可以有序充电也可以在高峰时段放电参与双向调节。模式A的负荷峰值是1160kW模式B降到1052kW模式C进一步降到946kW。相比无序充电V2G模式将微电网峰值负荷削减了18.4%。光伏消纳率从模式A的88%提升到模式C的96.8%。成本方面模式A的日运行成本最高约9180元模式B约8710元模式C约8499元。相比无序充电V2G模式日均节省约680元全年节省约25万元这是一个相当可观的经济效益。更重要的是V2G模式下EV集群的聚合放电功率基本都出现在电价高峰时段18:00-21:00响应了电价信号真正起到削峰填谷的作用。而有序充电模式下EV的充电功率则集中在夜间低谷电价时段虽然也优化了成本但对“削峰”的贡献不如V2G模式明显。5. 常见问题与调试心得代码跑不通、结果不合理这是每个做优化调度的人都会遇到的问题。这里我把自己项目过程中踩过的坑和排查思路整理一下帮你少走弯路。5.1 常见报错与解决方式速查表报错信息原因分析解决方法“No suitable solver for PSD constraint”Yalmip把某约束误识别为半定约束检查矩阵维度是否匹配尤其是sdpvar的维度定义避免隐式创建矩阵变量“Solver not found”未安装求解器或未添加Yalmip路径安装Gurobi/Cplex并执行gurobi_setup或addpath到求解器目录“Infeasible problem”约束过紧或系统功率不平衡逐步注释约束定位不可行源检查初始SOC与目标SOC是否矛盾求解时间过长二进制变量过多或场景过多增加MIPGap、削减场景数量、或把部分0-1变量松弛为连续变量“NaN or Inf in variable bounds”输入数据中有缺测或除零数据预处理中加入异常值检测用均值或插值填充缺失点EV聚合SOC越界EV集群总电量约束设置不合理检查参与调度的车辆进出场时间细分时变的聚合容量5.2 求解结果不合理时的排查思路如果求解结果出现“储能系统一直不充电”“EV从不放电”这类现象先不要怀疑求解器按顺序排查第一步检查目标函数中各分量的数量级是否一致。如果购电成本的系数是1.2元/kWh而储能损耗成本是0.001元/kWh那模型在优化时会偏向使用储能储能损耗约束几乎不起作用。可以通过打印目标函数各项的数值来检查确保各个成本分量在总成本中占比合理。第二步检查约束的“松紧”情况。有的约束写得太松形同虚设有的写得太紧直接把可行域压没了。我在调试时会给每个约束加上一个“影子价格”对偶变量输出Yalmip里用dual(Constraints(i))就能提取影子价格过大说明该约束在主导求解结果。第三步检查SOC初值与末值是否闭环。很多调度模型要求SOC周期末回到初值但如果初值设置不合理或者EV集群电量起始值太低会导致模型为了满足终值约束而做出反常决策。5.3 实操心得与效率提升技巧先将小规模算例跑通再扩展到全规模。我习惯先用5个场景、简化系统不含EV跑通整个流程确认逻辑无误后再逐步增加场景数和系统元素。这个习惯帮我避开了大量调试时间。善用并行计算赶场景生成。生成500个场景时逐时段逐场景的循环其实很慢。我把for循环改成parfor利用Matlab Parallel Toolbox在多核上跑场景生成时间从几分钟降到几秒。如果机器没有并行工具箱也可以用向量化写法代替循环。数据管理要规范化。把所有系统参数集中在一个结构体里管理而不是散落在各个脚本中。我用par结构体统一存储所有参数这样在调参和复现时不容易出错。另外把中间结果削减后的场景、预测误差统计存成.mat文件方便后续直接加载省去每次运行都重新生成数据的时间。关注求解器日志别忽略警告信息。Gurobi在求解时会输出Presolve、MIP Gap等日志。如果Presolve显示约束数量大量归零说明你的部分约束是冗余的如果MIP Gap长时间不下降说明二进制变量分支策略有问题。花点时间读懂求解器日志能帮你发现很多建模层面的问题。6. 扩展方向从单目标到多目标、从离线到在线代码跑通、结果验证完之后这套框架还可以往几个方向扩展我以个人经验给出参考建议。6.1 引入多目标优化当前模型是单目标最小化期望运行成本。实际工程中运行成本和碳排放往往存在冲突可以通过epsilon-约束法或加权法把碳排放目标引入模型。比如以碳排放最小为一个目标运行成本最小为另一个目标生成Pareto前沿。Yalmip对多目标模型的支持也很直接只需把两个目标函数线性加权或按epsilon约束逐步求解。6.2 从随机规划扩展到分布鲁棒优化场景法的局限是场景分布要能代表真实分布当历史数据不足时场景生成的准确性堪忧。分布鲁棒优化DRO在场景法基础上加了“最坏分布”的考量用Wasserstein距离或KL散度构建模糊集。这个方向在文献里很火但实现复杂度成倍增加。如果你有Gurobi 9.0以上版本DRO模型中的部分约束可以直接建模求解代码框架可以从当前版本改造得到。6.3 从离线调度走向滚动优化我当前实现的是“24小时前”的一次性离线调度实际系统中预测误差是不断更新的更好的方案是模型预测控制MPC滚动优化。每个小时更新一次预测数据只执行未来4-6小时的调度计划然后滚动前移。这种方案的Matlab实现也不复杂只需要把主程序包在一个循环里每次更新场景数据、重新求解。滚动优化的求解时间要求更高所以场景削减的重要性会更突出。这两个扩展方向本质上都是在“模型精度”和“计算效率”之间找平衡。我个人在实际操作中的体会是不要一上来就追求最复杂的模型先把基础框架跑通、把数据链路理顺再根据具体需求逐步增加模型复杂度。毕竟调度这件事最终是要上系统的算法的可用性比理论上的完美性更重要。