
做电力系统调度优化的人对“虚拟电厂”这个词应该都不陌生。特别是在“双碳”目标下风电、光伏装机量越铺越大电网的运行方式越来越接近“看天吃饭”系统对灵活性的需求已经从原来的“锦上添花”变成了“刚性约束”。但灵活性不会凭空掉下来储能是最直接的来源可储能贵而且每次充放电都在消耗寿命。这时候如果调度模型里只用固定成本系数算储能不考虑衰减算出来的“最优”方案很可能是拿未来换现在短期账好看长期账亏到心疼。所以我复现这篇SCI论文的时候最大收获不是把调度模型跑通而是看它怎么把“灵活性”和“储能衰减成本”放在同一个优化框架里做多时间尺度权衡。这篇博文就把我的复现思路、模型设计、Matlab实现和踩坑记录完整分享出来适合正在做虚拟电厂、储能调度、多时间尺度优化的同学参考。1. 高比例可再生能源并网虚拟电厂为什么绕不开“灵活性-储能成本”这对矛盾1.1 可再生能源出力特性给调度带来的“麻烦”到底是什么要理解虚拟电厂VPP的价值先得看它要解决的痛点。光伏在傍晚出力快速下降偏偏晚高峰负荷上升两个曲线一叠加净负荷曲线出现经典的“鸭形曲线”。风电则是“反调峰”夜里风大负荷低白天风小负荷高。这两个特点导致传统机组要么频繁启停要么长时间压负荷运行运行效率低、煤耗高而且系统在小时级、分钟级的时间尺度上都会出现供需缺口。这个缺口就是灵活性需求本质上要求系统里必须有能在短时间响应、能快速升降功率的资源。只靠传统火电硬扛代价是碳排放和调节成本飙升不硬扛就要靠储能和柔性负荷来补位。这也是为什么所有面向高比例可再生能源的调度研究都要把灵活性放在一个显眼的位置。电网调度不再是简单地“跟着负荷走”而是要主动安排“向上调节能力”和“向下调节能力”。比如光伏大发的中午需要向下调节能力来压出力、给储能充电晚高峰光伏退坡时又需要向上调节能力来顶上缺口。这两个方向的调节能力缺口只要存在一个系统就要面临拉闸限电或大量弃风弃光。所以高比例可再生能源并网之后灵活性问题本质上是一种“能力储备”问题而不是单纯的“电量”问题。1.2 虚拟电厂是怎么把零散资源“聚沙成塔”的虚拟电厂不是真实存在的发电厂而是通过通信、控制和聚合算法把分布式光伏、分散式风电、用户侧储能、电动汽车充电桩、空调负荷这些容量小、位置散、响应特性完全不一样的资源统一调度成一个“虚拟机组”参与电网运行。它的核心价值在于单看一个充电桩微不足道但聚合几千个充电桩响应能力就相当于一台中型调峰机组。从电网角度看VPP就是一个可以调节功率的外部资源从内部看它需要协调各类资源的出力、充放电和可削减负荷。一个完整的VPP优化模型通常包括分布式电源模型、储能模型、柔性负荷模型和上级电网交互模型。分布式电源模型处理光伏、风电的出力上限和预测误差储能模型处理SOC、充放电功率和寿命损耗柔性负荷模型把可转移负荷、可削减负荷折算成可调功率区间上级电网交互模型则描述VPP与外电网的购售电关系。把这四类模型拼装起来再叠加各种运行约束就构成了调度优化的基础。我复现时最大的体会是VPP调度和传统电厂调度最大的不同在于“资源的软约束太多”比如空调负荷不能无限制削减电池不能每次都用满容量光伏不能随便弃这些细节都要在模型里有所体现。1.3 为什么储能成本不能拍脑袋定一个固定值很多入门级的调度模型里储能成本往往只有一个充放电单位成本或者干脆设成0只约束SOC。这样确实简单但和现实差距非常大。储能电池的寿命和充放电循环深度强相关深度放电1000次可能就退役浅充浅放能撑3000次折算下来一次深度放电的老化成本可能比市电价格还贵。如果不把这个衰减成本写进优化目标调度器一定会拼命让储能做深度充放来“压电网成本”表面上风光利用率很高实际上电池用了一年半就报废整个项目经济性一塌糊涂。所以论文里专门做了衰减建模把每次充放电对寿命的损耗折算成运行成本跟着SOC和DOD动态变化。举个例子同一块电池在SOC从0.9放到0.1和从0.6放到0.4后者的损伤明显小得多但前者能给系统提供的调节电量也大得多。调度模型要回答的正是这个“多出力但更伤电池”和“少出力但寿命更长”的权衡问题。这个细节是我觉得整篇SCI最值得复现的部分也是“灵活性-储能成本”这对矛盾的核心解法。2. 多时间尺度调度框架与衰减建模的完整设计2.1 为什么非要做“日前-日内-实时”三层调度预测精度和调度周期是一对矛盾。提前24小时的预测对风电、光伏的出力趋势能看个大概但具体到某个小时的风速突变、云层遮挡误差可能很大提前1小时的预测精度明显提升但留给机组启停和储能规划的时间又不够。所以成熟的做法是把调度过程拆成多个时间尺度逐级修正。复现论文采用的三层结构比较典型日前调度以1小时为步长做未来24小时的资源优化配置确定储能预留、机组组合和可调度负荷的大方案日内滚动调度以15分钟或1小时为周期基于最新预测每4小时滚动更新一次修正风光出力和负荷偏差实时调度则做秒级到分钟级的功率分配处理偏差和突发波动。三层之间通过“计划值传递、偏差量纠正”的方式衔接上一层的结果是下一层的边界条件下一层只对偏差做局部调整避免重复优化导致抖变。调度层时间粒度主要目标决策变量日前调度1小时24点确定机组组合与储能计划机组启停、储能充放电方案日内滚动15分钟或1小时修正预测偏差储能修正功率、可调负荷调整实时控制秒级至分钟级平衡实时功率偏差储能快速出力、紧急切负荷这套结构对于“灵活性”的意义在于不同时间尺度需要的灵活性来源不同。日前阶段主要靠机组组合和储能预留来提供长时灵活性日内阶段靠储能快速爬坡和柔性负荷调节实时阶段靠储能和备用响应秒级波动。如果只做日前单阶段优化根本没法处理分钟级的风光波动如果只做实时优化又没法提前安排电池的SOC空间。多时间尺度调度不是“锦上添花”是让储能资源在不同时间尺度上都能被用起来的必要条件。2.2 储能衰减建模三种主流方法怎么选储能衰减建模有三种常见思路复现之前我专门捋了一遍。第一种是等效循环寿命法把不同放电深度下的循环次数折算成标准循环次数。磷酸铁锂电池在100% DOD下循环寿命约2000次在50% DOD下可能到6000次用一个幂函数拟合DOD-循环寿命曲线再按“累计等效循环次数/总循环次数”计算寿命消耗。第二种是能量吞吐量法认为电池总吞吐电能量是固定值每次充放电消耗对应的吞吐量除以总吞吐量就是老化比例。这个方法更简单适合做线性化。第三种是雨流计数法常用于电池寿命评估领域通过统计充放电循环的完整性和放电深度计算疲劳损伤准确但计算量大不适合直接嵌入优化模型。论文原文用的是等效循环法把老化成本按充放电功率和SOC状态折算成每个时段的线性或分段线性成本加进目标函数。好处是既能体现“深度充放更伤电池”的特点又能保持优化模型可解。具体折算时会引入DOD与循环寿命的关系曲线比如用式 (N_{cycle}(DOD)N_0 \cdot (DOD)^{-k}) 来拟合然后把单次充放电对寿命的消耗按1/N_cycle估算再乘以电池容量和更换成本得到单位功率的老化成本。这样做下来储能调度的经济性评估就真实很多不会为了省几分钱的购电费去牺牲电池寿命。2.3 目标函数和约束条件怎么“落笔”才能贴合复现模型这一节是核心。目标函数按论文里的做法可以写成系统运行总成本最小化包括外购电成本、分布式电源发电成本、储能老化成本、弃风弃光惩罚成本和灵活性不足惩罚成本。其中外购电成本采用分时电价灵活性不足惩罚用“无法爬坡的功率缺额乘以惩罚系数”来体现。约束条件分为四组。功率平衡约束即所有资源出力加总等于负荷分布式电源约束包括出力上下限和爬坡约束储能约束包括SOC递推公式、充放电功率限制、SOC上下限备用与灵活性约束即系统在任意时段必须具备足够的向上或向下爬坡能力来应对预测误差这个约束正是“灵活性”落地的关键。灵活性约束通常写成可用上调容量之和大于预测误差的置信区间或写成旋转备用约束。在实际代码里目标函数可以写成[ \min \sum_{t} \left( C_{grid,t} \cdot P_{grid,t} C_{deg,t}(P_{ch,t}, P_{dis,t}, SOC_t) C_{curt} \cdot P_{curt,t} C_{flex} \cdot P_{flex,short}\right) ]其中 (C_{deg,t}) 就是老化成本函数它与储能的充放电功率和当前SOC相关。这个函数的线性和凸性是求解速度的关键如果直接用二次函数MILP求解器不一定能处理通常需要做分段线性近似。我在复现时把SOC区间分成三段每一段对应不同的老化惩罚系数再通过Yalmip的sos2约束加入模型既保留了物理特性又保证了可解性。3. Matlab代码实现从数据到结果的可复现路径3.1 先搭好代码骨架再填模型复现SCI论文最忌讳拿到代码就一头扎进函数堆里改。我的习惯是先搭一个清晰的代码结构按“数据—参数—模型—求解—画图”五层拆开。项目文件夹里我通常这样组织data/存放风光出力、负荷、电价的历史序列或典型日数据CSV或MAT文件。params/参数初始化脚本统一管理所有常量。models/各类资源的目标函数和约束构建函数。solver/调用Yalmip和Gurobi的求解入口。results/保存结果和图表。这个结构的好处是改参数、换数据、升级模型时不会牵一发而动全身。尤其是做三阶段调度每个阶段都要复用基础数据如果参数散落在不同脚本里后期调试会非常痛苦。建议一开始就把所有参数集中到一个config.m里每个阶段脚本用run(config.m)加载。另外Matlab版本建议R2023a以上Yalmip要用新版求解器Gurobi或Cplex都行如果只是复现小规模算例也可以用内置的intlinprog顶着但求解速度和稳定性会差一些。3.2 参数初始化和典型日数据准备先看参数。我的复现案例假设一个包含200MW光伏、100MW风电、50MW/200MWh储能和50MW可调负荷的虚拟电厂与上级电网交互上限150MW。关键参数表如下参数取值说明储能功率/容量50MW/200MWh锂离子电池储能SOC范围0.1 ~ 0.9保护电池充放电效率0.95 / 0.95往返效率约0.9电池循环寿命5000次80%DOD用于等效循环折算风光预测误差15% / 10%日内/实时修正用分时电价峰谷1.2 / 0.4 元/kWh市场电价风光出力曲线我用论文公开数据近似生成把典型日归一化出力曲线叠加随机扰动再按装机容量放大。负荷曲线用地区典型夏季日曲线。这部分严格来说不是论文复现最核心的部分但数据选得不好后面所有结果都不可信。建议读者不要直接用随机数跑尽量找同地区公开数据集或者至少采用论文附录中的典型日数据。特别是储能初始SOC必须在前一天调度结束后自然生成不能随手给定否则第一天会出现“凭空放电”的问题。3.3 储能衰减成本函数怎么用Matlab写衰减成本函数。我写的时候用的是“分段线性化”方式避免直接在优化里用非线性函数。核心想法是把电池SOC区间分成三段每段对应一个老化成本系数然后通过Yalmip的binvar和sos2约束构造成本。也可以用一个简化的二次函数近似再用线性化工具包处理。给一个简化示例函数function deg_cost battery_degradation(P_bat, SOC, params) % 简化衰减成本与充放电功率的二次方和SOC偏离程度相关 % SOC_mid 是电池最健康的工作点一般取0.5 alpha params.degradation_coeff; % 元/MW^2 beta params.soc_penalty; % 元/pu^2 deg_cost alpha * (P_bat.^2) beta * (SOC - 0.5).^2; end说明实际SCI论文里衰减成本不会用这么简单的函数而是基于DOD循环寿命曲线做积分或分段。这里展示的是把“深度充放惩罚”和“SOC偏移惩罚”同时表达出来的思路可以加进目标函数也能作为约束加入。要注意系数α、β不是随便拍的后面第4.2节会说怎么标定。在Yalmip中如果直接用二次函数可以配合quadprog类求解器但如果是MILP建议提前用分段线性近似否则求解器很可能报错或陷入局部解。3.4 用Yalmip建模多时间尺度调度Yalmip是Matlab下的建模工具让优化模型从数学公式到代码的转换非常顺畅。三层调度里日前模型要建24小时连续整数变量日内滚动模型按当前时刻滚动建窗口实时模型则是单时段二次规划。代码骨架如下% 日前调度示例24小时时间步长1h yalmip(clear) P_ch sdpvar(24,1); P_dis sdpvar(24,1); SOC sdpvar(25,1); P_grid sdpvar(24,1); P_pv sdpvar(24,1); % 实际出力可能小于预测 u_ch binvar(24,1); % 充电状态 u_dis binvar(24,1); % 放电状态 Constraints []; % 功率平衡 Constraints [Constraints, P_grid P_pv P_dis P_load P_ch]; % 储能约束 for t 1:24 Constraints [Constraints, SOC(t1) SOC(t) P_ch(t)*eta_ch - P_dis(t)/eta_dis]; Constraints [Constraints, 0 P_ch(t) P_ch_max*u_ch(t)]; Constraints [Constraints, 0 P_dis(t) P_dis_max*u_dis(t)]; Constraints [Constraints, u_ch(t) u_dis(t) 1]; Constraints [Constraints, SOC_min SOC(t1) SOC_max]; end % 目标购电成本 充放电老化成本 弃光惩罚 Objective sum(P_grid .* price) sum(battery_degradation(P_ch - P_dis, SOC(1:24), params)); optimize(Constraints, Objective, sdpsettings(solver,gurobi));代码说明几个关键点用binvar保证充放电不同时进行用sdpvar声明连续决策变量目标函数里把衰减成本作为正则项加入。实际复现时还需要在循环里给P_pv设置上限约束并用预测值初始化。日内滚动和实时调度的代码类似只是时间窗口缩短并且把日前计算出的SOC计划作为固定参量传入。注意P_ch - P_dis这种写法在二次函数里会引入交叉项如果希望减少非线性建议把充放电功率分开声明目标函数写成两部分之和。3.5 结果怎么分析才算把论文复现到位跑完代码后至少要画四类图各时段电源出力堆叠图储能SOC曲线和充放电功率系统购电功率与实时电价的对比考虑衰减与不考虑衰减的成本对比图。最后一类图最能说明“灵活性-储能成本平衡”的价值——不考虑衰减时储能夜间充满、白天高峰期放出社会总成本显得很低考虑衰减后调度结果会让储能更多在浅充浅放区间运行老化成本下降虽然购电成本可能会略微上升但全年总经济性更好。画图用Matlab的plot和area即可但需要注意时间轴对齐。日内滚动和实时阶段的数据要按时间戳拼接不要只看断点之前的曲线。我一般先把所有结果落到同一个时间向量的结构体里再统一画图避免因为每个阶段时间步长不同导致曲线对不上。另外建议把SOC曲线的纵轴取值范围固定不然很难对比不同调度策略下的SOC轨迹。4. SCI复现过程中踩过的坑和解决记录4.1 求解器报错和线性化处理第一个坑是“Gurobi不收敛”或“Yalmip报错No suitable solver”。这通常是因为模型里带了非线性项比如SOC乘以充放电功率但求解器是MILP。解决办法是把储能功率的二次衰减成本项分段线性化或者采用Yalmip的implies逻辑离散。如果模型里只有连续变量可以用非线性求解器如ipopt但通常调度模型是MILP所以我强烈建议把所有非线性能转化为线性的地方全部转化。衰减成本用sos2约束或big-M分段都可以。另外整数变量多的时候求解会很慢先把binvar数量降下来比如把每个时段充放电状态合并成一个三状态整数变量0不出力1充电2放电这样不仅变量少还天然排除同时充放比u_ch u_dis 1效率高得多。我在复现的时候最初用了两个二元变量加一个约束结果24小时模型增加48个整数变量求解时间将近半小时改成三状态变量后求解时间降到几分钟。4.2 衰减成本系数怎么标定才不过头很多同学复现这种论文最迷茫的就是系数。我踩过的坑是第一次把老化成本系数设得过大结果储能整个调度周期几乎没有动作灵活性全交给上级电网从结果看一点不“绿色”设得过小储能又变成“无脑搬移电量”和没衰减模型几乎一样。标定思路是先用电池厂商给的循环寿命数据算单位MWh吞吐量的等效老化成本这个值就是α的数量级参考再通过一组单时段测试看调度模型在不同α下的储能利用率变化取“灵敏度拐点”作为最终值。另外SOC惩罚项β不要单独设很大否则SOC会被强制钉在0.5储能基本失去容量价值。最优方案应该是让SOC在0.2~0.8之间按电价动态浮动而不是死守某个“健康值”。衰减模型的目的是让储能“少做不划算的事”不是让储能“不做任何事”。4.3 三层调度衔接时SOC“跳变”怎么办日前计划给出了每小时SOC目标值但日内在执行时实际风光曲线与预测不同SOC会偏离计划。如果日内模型直接以日前SOC为目标会让储能频繁“纠正”功率出现反调节。这个问题的标准解法是日内滚动模型只对“日前SOC计划曲线”施加一个软约束目标函数里加一个SOC偏差惩罚项系数相对较小实时调度则完全以日内最新指令为参考不再强制追平日前。这个分层松绑的思路是保证三阶段调度稳定的关键。我最初强行把日前SOC作为硬约束结果日内模型无解改成软约束后马上正常。实际上日前计划的意义是“提供边界和方向”而不是“绑定每一刻的SOC”。只要日内偏差在允许范围内就不必追求完全还原日前路径否则调度指令会像“拉锯战”一样来回震荡。4.4 经常遇到的其他问题和排查手段这里整理一个速查表适合在调试阶段对照现象可能原因排查/解决求解器报infeasible功率平衡约束与变量范围矛盾检查初始SOC是否在范围内逐步放开负荷上限结果里储能同时充放电只设了功率上限没设互斥用三状态整数变量或充放电状态互斥约束衰减成本为负公式里充放电功率差值被错误平方将充放电功率分开定义不要用差值做平方实时调度曲线跳变时间窗口滚动重叠逻辑错误检查用于滚动的最新预测数据是否被下一阶段覆盖画图数据错位各阶段时间步长不同统一时间戳用分钟级时间轴重采样除了速查表还建议从一开始就跑小规模算例验证比如先做6小时单阶段调度确认结果合理再扩展24小时三层调度。我复现时前两次失败都是因为直接拿论文的大规模算例跑数据量一大问题被淹没在“不收敛”里很难定位。先小后大先简单后复杂是复现这类优化论文最朴素但最有效的策略。最后分享一个小技巧。我在做这种SCI复现时会把论文里的参数表、目标函数、约束条件先整理成一个文档把数学公式翻译成中文逻辑再动笔写Matlab。看起来多花了一两个小时但后面写代码时会非常快因为你已经把“每个约束为什么存在”想清楚了。复现不是为了把作者代码抄一遍而是把里面的工程权衡变成自己的判断力。这篇分享给的只是路径真正的“灵活性-储能成本”平衡还是要靠你自己手里的数据和参数去调出来。