最近在复现一篇某顶级SCI期刊上关于虚拟电厂多时间尺度调度的论文,核心就是标题里那句话:高比例可再生能源并网下,灵活性需求与储能成本到底怎么平衡。这个题目学术味很重,但落到代码和模型层面,它其实是一套“多时间尺度决策 + 储能衰减建模”的组合拳,可以用Matlab实现并拿到与实际系统吻合度较高的调度结果。
我花了两周多时间把这个完整框架跑通,从模型搭建、数据构造、求解器配置到结果分析,中间踩了不少坑,也理清了很多之前看文献时一头雾水的细节。这篇博文就按我的实际复现过程来写,尽量把每一步为什么这么做、参数怎么定、代码怎么写、结果怎么看都讲清楚。想直接上手复现的同学,看完基本可以照着搭出自己的一套VPP调度框架。
1. 项目整体思路与模型框架拆解
1.1 高比例可再生能源并网到底难在哪
先说背景。所谓“高比例可再生能源并网”,并不是光伏风电多装几台那么简单。电网的负荷是随时间变化的,而风电光伏的出力又有强烈的随机性和间歇性。当可再生能源渗透率超过30%甚至更高时,整个系统面临的核心矛盾已经不是“发电量够不够”,而是“能不能在正确的时间把正确的电量送到正确的地方”。
举个最直观的例子:夏天午后光伏大发,电网消纳不了就要弃光;傍晚光伏骤降,负荷又进入晚高峰,系统必须在两三个小时内快速爬坡顶上缺口。这种快速、大幅度的净负荷变化,在电力系统里叫爬坡需求,对应的是系统的灵活性资源储备。传统火电机组爬坡速度慢、调节范围小,靠它应付高比例新能源场景非常吃力。
这时候虚拟电厂(VPP,Virtual Power Plant)的概念就出现了。它不建新的物理电厂,而是把分布式光伏、风电、储能、可调负荷等分散资源通过通信和调控平台聚合起来,对外表现为一个可控的“电厂”。储能系统在其中承担的是灵活性调节的核心角色——既可以快速充电消纳过剩新能源,又可以快速放电支撑负荷缺口。
但储能不是免费的。电池有初始投资成本,运行中还有充放电效率损耗,更关键的是每一次深度充放电都会加速电池衰减,缩短寿命。于是问题就来了:为了满足系统灵活性需求,今天让储能多出力,短期调度成本可能好看,但电池寿命缩短,长期算总账反而亏;反过来,省着用储能,灵活性问题又解决不了,弃风和切负荷的惩罚成本会飙升。
这就是选题里说的“平衡灵活性与储能成本”。这篇SCI提出的方案,就是用多时间尺度调度框架,把“灵活性”从小时级规划一直落实到秒级控制,同时在每个尺度上都显式地建模储能衰减成本,让调度决策自动在灵活性和寿命之间找到最优平衡点。
1.2 多时间尺度调度:为什么必须分三层
一开始我也想过,为什么不直接做一个全时段的全局优化模型?理论上可行,但实际一跑就会发现两个问题:
第一,可计算性。如果把一年8760个小时全部纳入一个混合整数规划模型,决策变量里再带上机组启停、储能充放电状态等0-1变量,问题规模会爆炸。即便用顶级的商用求解器,求解时间也常常超过可接受范围。
第二,预测误差的时间特性不同。光伏出力的超短期预测误差很小,但长期预测误差大;负荷也是类似规律。如果只用一个时间尺度的预测数据来做决策,要么盯得太远导致策略保守,要么盯得太近导致没有提前给机组和储能留出足够响应余量。
所以主流做法就是分层调度,通常分三层:日前调度、日内滚动调度和实时修正控制。
- 日前调度:在当天0点前,基于次日光伏、风电、负荷的预测曲线(通常是小时级分辨率),安排机组的启停计划、储能的充放电计划、联络线交换功率计划。这一层的目标是全局经济性最优,决策变量主要是机组状态和储能每小时SOC目标。
- 日内滚动调度:每隔15分钟或1小时滚动更新一次预测,基于更精确的短期预测数据,对日前计划进行修正。比如日前预测明天下午两点光伏出力是120MW,实际上午看只有100MW,日内调度就要调整储能的充电计划。
- 实时修正控制:秒级到分钟级,根据实际出力和负荷的偏差,利用储能等快速响应资源消除功率波动。这一层不追求经济优化,追求的是“跟踪偏差最小化”。
三层之间的时间尺度和决策变量性质不一样,代码实现上也是完全独立的优化模块,但上层计划要给下层留出可行域范围。比如日前调度定了储能SOC曲线,日内调度只能在允许的SOC偏差范围内调整;实时控制只能在储能功率上限内动作。这个约束关系是复现中最容易漏掉的地方,后面我会专门说。
1.3 储能衰减建模:单位充放电成本的“幕后黑手”
常规的调度模型里,储能成本往往只算一个简单的度电成本,比如“充电0.3元/kWh,放电0.5元/kWh”。但这么做有一个明显的漏洞:储能电池的寿命损耗不是线性变化,而是和充放电深度(DOD)、循环次数、温度、充放电倍率强相关。同样的1kWh放电量,在SOC 20%-80%区间放出来和从100%放到0%,对电池寿命的伤害完全不同。
这篇论文采用的衰减建模思路可以归纳为两类模型的结合:
第一类是循环老化模型。它认为电池每经历一次充放电循环,容量就会有一定比例不可逆损失,而这个损失速率和放电深度有关。工程上常用这种形式:
[ N_{\text{life}}(DOD) = N_0 \cdot (DOD)^{-\alpha} ]
其中 (N_0) 是参考条件下的循环寿命,(\alpha) 是经验指数(通常在0.8~2之间)。也就是说,假设某电池在100% DOD下能循环3000次,那么在50% DOD下循环次数可能超过6000次,整体释放的总电量反而更高。这个非线性关系直接决定了调度策略:让电池浅充浅放,长期成本更低。
第二类是日历老化模型。即便完全不充放电,电池也会随着时间自然衰减,衰减速率与温度、SOC水平相关。越高的环境温度和越高的平均SOC,日历老化越快。用公式表示就是类似阿伦尼乌斯形式:
[ Q_{\text{loss}} = A \cdot t^{0.5} \cdot \exp\left(-E_a / (R\cdot T)\right) \cdot SOC_{\text{avg}}^{\beta} ]
复现的时候,我把两类模型合并成单位时间步的“储能寿命损耗成本”,直接加到目标函数里。这样做的好处是,调度的目标函数里储能出力不再是零成本,每一次充放电都有真实的边际成本。而且这个成本是SOC的函数,SOC越低放电,放电对应的衰减越大,调度器就会自动规避深度放电,反而倾向于保持SOC在中间区间运行。
注意:衰减模型的参数在不同文献里差异很大,复现时最好以你自己目标系统的电池规格书为基准。我这里用的只是相对合理的缺省值,具体工程应用中建议做一次模型校准。
2. Matlab代码实现的整体架构
2.1 代码模块划分
整个Matlab工程我按功能拆成了六个模块,这样跑实验方便,出问题也好看定位:
VPP_scheduling/ ├── data/ % 原始数据与处理脚本 ├── model/ % 模型构建:目标函数与约束 ├── solver/ % 求解器调用与结果回收 ├── scenarios/ % 场景生成与削减 ├── analyze/ % 结果分析与绘图脚本 └── main.m % 主程序入口我强烈建议不要把所有逻辑写在一两个大脚本里。多尺度调度涉及很多中间变量和状态传递,脚本一旦超过几百行,改一个参数排查半天的场景经常出现。分模块写,每个函数只干一件事,调试效率和代码复用率都会高很多。
主程序 main.m 的伪代码流程是这样:
% 1. 读取并处理基础数据 [Load, PV, Wind] = load_data(); % 2. 让场景生成 [Scen_PV, Scen_Wind, Prob] = generate_scenarios(PV, Wind, N_scen); % 3. 日前调度 [Plan_Day, SOC_target] = solve_day_ahead(Load, Scen_PV, Scen_Wind, Prob); % 4. 日内滚动调度 [Plan_Intra, SOC_adj] = solve_intraday(Load, PV, Wind, SOC_target); % 5. 实时控制 [Ctrl_Real, Power_EE] = solve_realtime(Load_act, PV_act, Wind_act, SOC_adj); % 6. 结果分析与指标统计 result_analysis();2.2 建模语言与求解器选择
决策变量里有0-1变量(机组启停、储能充放电状态),这是一个混合整数线性规划(MILP)或者混合整数二次规划(MIQP)问题。Matlab自带的 linprog 只支持连续线性规划,intlinprog 支持MILP但性能一般。我更建议使用 Yalmip 做建模层,然后对接 Gurobi 或 Cplex 这类商用求解器。
为什么用Yalmip?原因很简单:代码可读性高,建模效率快。你可以直接用sdpvar、binvar、optimize这些高层指令,不用手动拼矩阵A、b、Aeq、beq,大大减少出错的概率。而且Yalmip对约束、目标函数、参数传递的支持非常直观,后面要改模型结构也方便得多。
以下是日前调度模型的核心变量定义:
% 时间步长 N_T = 24; % 日前调度,小时级,24个时段 N_G = 2; % 传统机组数量 % 决策变量 P_g = sdpvar(N_G, N_T, 'full'); % 机组出力 u_g = binvar(N_G, N_T, 'full'); % 机组启停状态 P_ch = sdpvar(1, N_T, 'full'); % 储能充电功率 P_dis = sdpvar(1, N_T, 'full'); % 储能放电功率 u_ess = binvar(1, N_T, 'full'); % 储能充/放状态 SOC = sdpvar(1, N_T+1, 'full'); % 储能带电状态,含初始值 P_pur = sdpvar(1, N_T, 'full'); % 从主网购电 P_sell = sdpvar(1, N_T, 'full'); % 向主网售电这里要特别注意的是储能充放电状态的定义。如果不加状态变量而只约束功率非负,很容易出现同一时刻既充电又放电的最优解——这在物理上不合理,但在数学模型里求解器可能钻空子。标准的处理方式是引入0-1变量 (u_{ess}),当 (u_{ess}=1) 时允许放电,禁止充电;(u_{ess}=0) 时相反。约束写成:
% 互斥约束 P_ch <= (1 - u_ess) * P_CH_MAX; P_dis <= u_ess * P_DIS_MAX;这类约束被称为互补约束,是储能调度模型里最基础的防呆设计。
2.3 约束条件的完整清单
约束条件是模型的核心骨架。我梳理了一遍,完整约束有七大类:
第一,功率平衡约束。每个时段,系统总负荷一定等于机组出力、新能源出力、储能放电、购电之和,减去储能充电和售电:
for t = 1:N_T Constraints = [Constraints, ... sum(P_g(:,t)) + PV(t) + Wind(t) + P_dis(t) + P_pur(t) == ... Load(t) + P_ch(t) + P_sell(t)]; end第二,常规机组出力上下限与爬坡约束。这里面爬坡约束是很多初学者容易漏的。机组不是想加出力就瞬间加上来,每分钟爬坡速率有限,对应到小时级调度就是相邻时段出力差不能超过某个值。
% 出力上下限 Constraints = [Constraints, P_gmin <= P_g(:,t) <= P_gmax .* u_g(:,t)]; % 爬坡约束 Constraints = [Constraints, P_g(:,t+1) - P_g(:,t) <= RAMP_UP]; Constraints = [Constraints, P_g(:,t) - P_g(:,t+1) <= RAMP_DN];第三,储能SOC递推方程。这是所有储能模型的“命门”:
[ SOC(t+1) = SOC(t) + \eta_{ch} \cdot P_{ch}(t) \cdot \Delta t - \frac{P_{dis}(t) \cdot \Delta t}{\eta_{dis}} ]
对应代码:
Constraints = [Constraints, SOC(1) == SOC_INIT]; for t = 1:N_T Constraints = [Constraints, ... SOC(t+1) == SOC(t) + ETA_CH * P_ch(t) * DELTA_T - P_dis(t) * DELTA_T / ETA_DIS]; end Constraints = [Constraints, SOC_MIN <= SOC <= SOC_MAX];这里 (\eta_{ch}) 和 (\eta_{dis}) 是充放电效率,一般取值在0.9~0.95之间。注意这段递推公式里的时间尺度 (\Delta t),在日前是1h,在日内滚动调度如果时间步长是15min,就要用0.25h。
第四,与主网交互功率约束。VPP与外部电网的联络线功率通常有上限,这个约束决定了VPP可以向主网“求助”的程度。
第五,备用容量约束。为了应对预测误差,系统必须在运行点基础上保留上备用和下备用容量。通常要求备用容量不小于新能源预测误差的某个置信区间。这部分在高比例可再生能源场景里特别重要,因为光伏风电预测误差远大于负荷预测误差。
第六,储能电池衰减约束。这一块是论文的亮点,也是我前期比较头疼的部分。为了把它线性化处理,我把每步的循环衰减成本表示成“放电深度变化量”的函数,再将非线性关系用分段线性逼近表示。
第七,状态变量初值约束。比如日前调度结束时SOC要回到初始值附近,日内滚动调度中SOC初始值必须继承日前计划等。这个约束不做,滚动调度就没法衔接起来。
2.4 目标函数的设计细节
目标函数是整个调度策略的“指挥棒”。不同权重代表不同的优先级。这篇论文的目标函数可以拆成四块:
[ \min \quad C_{\text{op}} + C_{\text{carbon}} + C_{\text{start}} + C_{\text{deg}} + C_{\text{pen}} ]
- (C_{\text{op}}):常规机组运行成本,通常用二次函数 (a u + b P + c P^2) 表示,我把二次项用分段线性化处理,避免引入二次约束导致求解变慢。
- (C_{\text{carbon}}):碳排放成本,与购电量和机组出力挂钩,体现VPP降碳的目标。
- (C_{\text{start}}):机组启停成本,每次启动或停机都会产生的固定成本,这个成本的存在是为了防止机组在相邻时段频繁切换启停状态。
- (C_{\text{deg}}):储能衰减成本,就是前面说的循环老化和日历老化合并后的等效成本。
- (C_{\text{pen}}):弃电惩罚成本,当系统消纳不了新能源时会产生弃风弃光,这部分以惩罚项的形式进目标函数。
这里最值得聊的是 (C_{\text{deg}}) 的实现。我参考的模型没有直接用“电池寿命年数”这种宏观指标,而是把衰减成本折算到每一次充放电动作上。每个控制时段的容量损失近似为:
[ C_{\text{deg}}(t) = c_{\text{rep}} \cdot \frac{Q_{\text{loss}}(t)}{Q_{\text{total}}} ]
其中 (c_{\text{rep}}) 是电池完全更换成本,(Q_{\text{loss}}(t)) 是本时段内因充放电循环造成的容量损失百分比,(Q_{\text{total}}) 是电池全寿命可释放的总容量。这个折算逻辑很直观:调度让电池多出一分力,就要在目标函数里多承担一分“折旧费”。折旧费的边际水平就是储能系统对外提供灵活性服务的真实成本。
你可以试着调一下 (c_{\text{rep}}) 这个参数,会发现一个很有趣的规律:当 (c_{\text{rep}}) 设定得很小时,储能几乎每天都是一个循环满充满放,充分发挥调节能力;当 (c_{\text{rep}}) 调大后,调度策略自动变得“温和”,储能倾向于浅充浅放、更多依赖常规机组调节。这个灵敏度分析,做PPT汇报的时候非常有用。
3. 实操过程:从原始数据到仿真结果
3.1 数据准备与场景生成
复现论文时最头疼的就是找不到原文的原始数据。很多论文只在附录里给了部分曲线,不会公开完整数据集。我的处理方式是混合使用公开数据集和合成数据:
风电出力曲线、光伏出力曲线采用某公开微网数据集中的典型日曲线,负荷曲线则是根据某配电网典型商业负荷形态合成。坦白说,只要能体现“高比例新能源+强波动”特征,数据来源并不影响方法复现的完整性。
数据里有几个关键参数需要提前定好:
| 参数 | 取值 | 说明 |
|---|---|---|
| 可再生能源渗透率 | 40%~80% | 指光伏风电装机容量占峰值负荷比例 |
| 储能容量 | 2 MWh / 4 MWh / 8 MWh | 用于灵敏度分析 |
| 储能额定功率 | 0.5 MW / 1 MW / 2 MW | 功率与容量比通常1:2左右 |
| 风、光预测误差标准差 | 10%~15% | 生成误差场景 |
| 场景数量 | 15个 | 生成后通过同步回代削减 |
场景生成这一步,我用了蒙特卡洛方法生成大量新能源出力场景,再用概率距离削减法把场景数量压缩到15个左右。为什么非要削减?因为场景太多模型会膨胀到难以求解,削减后既能保留不确定性特征,又能在可接受时间内求解。
场景削减的代码框架:
% 1. 基于预测值叠加正态扰动生成N_scen个场景 for s = 1:N_scen Scen(:, s) = Base + mu + sigma .* randn(N_T, 1); end % 2. 用快速前向选择法削减场景 [Reduced_scen, Prob] = fast_forward_selection(Scen, N_keep);削减后的场景概率会成为日前调度目标函数里的权重系数,每个场景对总成本的贡献按出现概率加权。这样求解出来的日前计划不是针对单一预测曲线最优,而是针对一堆可能场景的期望成本最优,这就是随机规划思想。
3.2 日前-日内-实时三层代码串接要点
三层调度的代码串接是整个工程最硬的部分,也是最容易出Bug的地方。我的具体策略是:
日前调度求解后,保存结果结构体plan_day,里面最重要的是每小时SOC目标曲线SOC_target(1:24)和机组启停计划。
日内阶段以15分钟为步长,每4小时滚动一次。每次滚动优化的窗口长度为4小时(即16个时点),先从plan_day里提取当前时刻和未来4小时对应的SOC目标,把它作为软约束——允许日内结果在SOC目标附近偏移,但偏移会带惩罚成本,只有发现明显预测偏差时才允许偏移。
实时控制阶段我用的思想是模型预测控制(MPC),每5分钟触发一次,优化未来15分钟(其实严格说是每5分钟一个点,一共3个点)的储能动作,目标是在满足功率平衡的前提下,让储能实际放电跟随日内计划的设定值,同时把频率偏差镇定在一个小区间内。
整个三层结构的数据流可以归纳成一句话:上层给下层赋值,下层反馈误差修正上层。代码工程里这对应的是“变量状态在函数间传递”,如果你喜欢用面向对象的写法,这个逻辑会更清晰。
3.3 求解复杂度与耗时控制
实际求解中我发现两个影响速度的关键因素:
一是二进制变量数量。储能充放电互斥变量、机组启停变量、分段线性化引入的辅助变量,每增加一个时间步,二进制变量线性增长。日在24时段大概有50个左右二进制变量,求解器毫无压力;到了日内16时点窗口加上机组状态,变量数量也不算大;但如果把场景数加到30个以上,求解时间会成倍上涨。我的建议是场景数控制在10~20个,精度和速度之间最划算。
二是分段线性化精度。常规机组成本二次函数分段线性化的段数越多,精度越高,但辅助变量也越多。实测6段就能保证相对误差小于0.5%,没必要用10段以上。
我用的平台配置:Windows上跑Matlab R2022b + Yalmip + Gurobi 10.0,日前调度一次求解耗时大约3秒,日内一次约0.5秒,实时控制一次约0.1秒。这种性能完全够做仿真实验和参数扫参了。
3.4 结果分析:到底平衡了什么
跑完脚本,我重点看了三条曲线:储能SOC曲线、系统爬坡缺口、总成本。
在包含衰减成本的情况下,SOC曲线明显比不含衰减成本时“温和”。直观表现就是:储能SOC始终保持在20%~85%之间运行,极少低于15%或高于90%;充放电切换次数也减少了,每天大约10~12次,而普通模型可能跑出20多次。
这个现象背后的逻辑值得琢磨。调度器发现深度放电会带来很高的衰减成本后,会主动选择“预充电策略”——在光伏大发时段提前把SOC充到80%~90%,然后在负荷尖峰时段小电流长时间放电,避免在SOC低位时切去大功率放电。这其实模拟了真实运行中电池管理系统的建议操作方式。
另一个有意思的结果是“灵活性指标”。我定义了一个灵活性指标,等于VPP实际可调节能力与系统净负荷变化需求之比,数值越大说明灵活性越充裕。实验显示:加入衰减成本后,灵活性指标峰值可能略降(因为储能不敢满充满放),但系统整体弃风弃光率反而下降了。原因在于日前计划更准确,日内追偏需求更小,整体协调效率更高了。
三组对比实验的结果统计如下:
| 实验设定 | 弃风弃光率 | 储能等效循环次数/日 | 单日总成本(万元) |
|---|---|---|---|
| 不含衰减成本 | 4.2% | 1.37 | 8.61 |
| 含衰减成本,电池更换价1.5倍 | 5.1% | 0.71 | 8.79 |
| 含衰减成本,储能容量翻倍 | 2.3% | 0.52 | 9.02 |
第二行成本虽然上升了,但电池寿命显著拉长;第三行说明在储能容量足够充足情况下,衰减约束对系统运行效率的影响会明显减小。这就是“灵活性”和“成本”之间的权衡曲线。
4. 常见问题与排查技巧实录
4.1 求解器返回Infeasible怎么办
这是所有人第一次跑模型时几乎都会碰到的问题。本身可能不是模型错,而是约束参数之间互相矛盾。常见原因有三个:SOC上下限设得不够宽、储能初始SOC与第一个时段需求冲突、备用容量约束设置的备用率过高。
我的排查方法:把模型里的硬约束逐个注释掉,二分定位到具体哪一条约束导致无解。发现是备用约束以后,再把备用率从10%逐步降到6%,或者增加一台快速机组参与提供备用,问题就解决了。
4.2 衰减约束非线性太强,Gurobi直接报错或求解极慢
衰减模型原公式里包含指数和对时间开方项,直接放进去就是一个非线性混合整数优化,求解难度陡增。处理方式是分段线性化加辅助变量约束,本质上是用线性函数去逼近非线性曲面。误差控制方面,我用等距分割法,在0%~100% DOD区间内分10段,相对误差在1%以内,完全满足调度精度要求。
4.3 日前计划的SOC在日内完全对不上
这个坑实际跑仿真时特别容易踩。原因在于日内调度的时间起点不一定等于日前的整点,而你的初始SOC如果直接用日前计划的整点值,和日内预测出的实际条件可能不匹配。解决办法是允许日内第一时段的SOC在日前计划值附近有可调范围,而不是写死。
具体做法:在日内模型里加入约束
Constraints = [Constraints, SOC_start >= SOC_target_intra - SOC_TOL]; Constraints = [Constraints, SOC_start <= SOC_target_intra + SOC_TOL];SOC_TOL我设成容量值的5%,足够调节又不会过度偏离上层意图。
4.4 实时控制阶段出现功率越界
因为实时控制用的是当前实测数据,但前几天用的预测数据可能有偏差,导致实时阶段一上来就触碰储能的功率上限或SOC限值。解决思路是引入“功率指令限幅模块”,实时控制输出的储能功率参考值先经过一个限幅函数:
P_ref = max(P_MIN, min(P_MAX, P_ref_raw));同时检查SOC剩余容量能否支撑这个P_ref,不够就下调。这个“防暴走”逻辑虽然朴素,但在实际工程里非常有用。
5. 进一步扩展与个人体会
整个复现做下来,我最大的体会是:论文里的模型并不神秘,真正的难点从来不在某一层的公式推导,而在于把“多时间尺度”和“跨时间尺度的衰减”这两条时间轴搞清楚。一个是决策频率的轴,一个是电池生命周期的轴,两条轴在目标函数里交汇,就形成了这篇论文的核心创新。
如果你想把这套框架用到自己的研究里,我觉得可以考虑三个扩展方向:
第一个方向是多虚拟电厂协同。把单个VPP的模型扩展到多个VPP,加入VPP之间的交易关系。这样每个VPP的储能成本结构不同,灵活性水平也不同,整个系统的调度会从单目标优化变成纳什均衡或者主从博弈问题,复杂度立刻上一个台阶。
第二个方向是考虑需求响应。目前可调负荷只是作为固定负荷来建模,如果把空调、热水器这类柔性负荷的偏移意愿和补偿价格加进模型,等于又给系统增加了一个灵活性来源,储能承担的调节压力会进一步降低,衰减成本也会更小。
第三个方向是结合碳排放约束。高比例新能源的底层逻辑是降碳。把系统碳排放配额和碳交易价格引入目标函数,观察储能衰减成本和碳成本之间的耦合关系,这可能是一个很适合发文章的切入点。我之后也打算在这个方向上进一步验证。
代码复现过程里,我个人觉得最有价值的习惯是“每跑一个参数组合,都保留一份中间结果文件”。实验后期对比不同参数配置下的成本和SOC轨迹时,这份存档能帮你省掉一整天重跑的时间。还有一个小建议:储能衰减模型里的所有物理量务必统一单位,千瓦、兆瓦、千瓦时、兆瓦时乱混,是仿真结果离谱的第一大元凶。
这套Matlab代码我已经整理成模块化版本,从数据到求解再到绘图一条流水线打通。回头想想,从最初连Yalmip建模都不太熟悉,到现在能独立复现这类多尺度调度问题,中间也不过就是一遍遍改模型、看报错、对比结果的过程。技术上的成长没有捷径,把一篇论文从公式读到代码跑通,比看十篇文献都管用。希望这篇记录对打算钻研虚拟电厂调度的你也能有点实在帮助。