考虑需求响应的电热综合能源系统两阶段优化调度及Matlab实现

发布时间:2026/9/10 4:25:22
考虑需求响应的电热综合能源系统两阶段优化调度及Matlab实现 搞电-热综合能源系统优化调度的人一开始都很容易陷入一个误区把模型堆得特别复杂电、热、气、冷全都塞进去约束写了几百行结果求解器跑半个小时不出结果最后只好把论文里写的“日前-日内两阶段”从PPT里删掉因为代码根本没搭出两阶段框架。我刚开始做这个方向的时候也一样走了不少弯路。直到后来想明白一个问题所谓“多时间尺度优化调度”本质上不是把模型做大而是把决策分层——哪些变量需要在昨天就定下来哪些变量可以留到今天见招拆招。这个分层逻辑理顺了Matlab代码的架构自然就清晰了。这篇就基于“考虑需求响应的电-热综合能源系统两阶段日前日内多时间尺度优化调度策略”这个题把我从建模到代码实现的完整思路、以及调试过程中踩过的坑一次性说清楚给正在做类似方向的同学一个可以直接参考的框架。1. 先搞清楚优化对象这个系统里到底有哪些设备在参与调度1.1 电-热耦合系统的典型物理架构先说为什么要“电-热综合”。单独做电力系统调度或者单独做热力系统调度模型都能简单很多但现实中的设备恰恰是跨能流耦合的。最常见的耦合设备就是热电联产机组CHP它烧天然气或煤产生的高温蒸汽先用来发电发电后的余热进入热网供热。这种“以热定电”或“以电定热”的运行特性让电和热两套系统互为约束——如果只看电网你没法决定CHP的电出力如果只看热网你也没法决定CHP的供热量因为两者共用同一个蒸汽量。典型的电-热综合能源系统一般包含这么几类设备设备类型作用调度中的关键特性CHP机组联产电和热系统的核心电出力与热出力相互耦合需满足可行运行域约束有最小开停机时间和爬坡约束电锅炉将富余电力转化为热能效率通常在0.95~0.98之间启停灵活秒级响应是弃风消纳的利器储热罐存储热网中的热能有储/放热功率上限、储热容量上限充放热时存在热损失常规纯凝机组只发电不供热爬坡约束、出力上下限、最小开停机时间风电/光伏不可控新能源出力具有随机性和波动性日前预测误差较大电负荷/热负荷用户侧需求部分电负荷具备可调节能力即需求响应资源这个结构里最值得关注的是CHP。国内很多文献喜欢把CHP简化成“热电比恒定”的定比模型就是P_CHP k * H_CHP这种模型写起来简单但实际运行中CHP的热电比是可以在一定范围内变化的它的运行可行域是下图这样一个多边形区域AB-BC-CD-DE-EF围起来的区域包含背压工况和抽汽工况。用可行域约束替代定比约束更贴近实际求解难度也并没有增加太多只是需要在代码里多写几个线性不等式。1.2 需求响应在这个模型里的角色需求响应Demand Response, DR在这个系统里不是一个“可选项”而是参与优化调度的虚拟资源。在电-热系统里需求响应有两种参与方式。第一种是价格型需求响应用户根据电价信号的引导调整用电行为——比如电价高的时段减少用电电价低的时段增加用电。这种DR在模型里表现为价格弹性矩阵电负荷的调整量是电价变化量的函数通常写成ΔL_t E * L_0_t * (Δπ_t / π_0_t)其中E是自弹性系数和交叉弹性系数组成的矩阵。自弹性系数一般为负电价升高用户减少用电交叉弹性系数一般为正其他时段电价升高用户会把负荷转移到当前时段。这个矩阵的估计可以从用户调查数据或者历史负荷数据回归得到实际算例中常用 -0.2 作为自弹性系数、0.03 作为交叉弹性系数的经验值。第二种是激励型需求响应直接与用户签订合同约定在系统需要时可以削减或转移一部分负荷系统给予用户补偿。在模型里表现为可中断负荷或者可平移负荷约束0 ≤ ΔL_DR_t ≤ ΔL_DR_max_t即每个时段的DR削减量有上限。目标函数里相应要加入DR补偿成本项。我认为真正有用的需求响应建模是把它当作“等效储能”。用户削减了一段负荷相当于系统从负荷侧获得了一个“负功率”这个负功率和储热罐的充放热在数学上有着相似的约束形态——都有容量上限、都有时段耦合关系。这样理解写代码的时候就不容易乱。2. 两阶段调度框架怎么划分“日前”和“日内”的决策边界2.1 为什么需要两阶段预测精度决定调度精度如果风电、光伏出力预测是完美的负荷预测是完美的那就不需要两阶段了直接做一次日前优化把未来24小时每个设备每个时刻的出力定下来就行。但现实中的预测误差——尤其是风电功率预测日前误差可达15%~25%日内超短期预测能降到5%以内——导致日前制定的计划根本没法直接执行。这就是两阶段调度Two-Stage Scheduling存在的根本原因。日前阶段基于日前预测数据确定一些一旦定下来就不方便频繁调整的决策变量日内阶段基于最新的超短期预测数据和实时运行状态在日前计划的基础上进行修正追踪真实运行工况。一句话概括日前计划负责“保底”日内优化负责“纠偏”。2.2 第一阶段日前慢变量与长周期决策日前调度的主时间尺度一般是1小时决策窗口是未来24小时即T24个时段。在这个阶段要确定的决策变量包括机组的启停状态0-1整数变量启动/停机后由于最小开停机时间约束短时间内不能频繁切换CHP机组的电出力/热出力计划值电锅炉的启停与计划出力储热罐的充放热计划需求响应的基线削减计划即提前通知用户“明天下午3点到5点准备削减负荷”与大电网的购售电功率计划这些变量有一个共同特征——都是需要提前一天“承诺”的。尤其是机组启停和DR签约量一个是物理设备层面的承诺机组开起来不能立刻停一个是商业合同层面的承诺答应用户削减负荷不能随意改都不适合在日内频繁调整。日前阶段的优化目标通常是系统总运行成本最小化包括机组燃料成本CHP机组的燃料成本是电出力和热出力的函数启停成本购电成本从外部电网购电需求响应补偿成本2.3 第二阶段日内快变量与滚动修正日内调度的时间尺度一般为15分钟或5分钟决策窗口可以是未来4小时滚动时域优化或与日前一样覆盖未来24小时但步长更细。日内阶段的决策变量包括各机组出力的修正量在日前计划基础上调整电锅炉出力的修正量储热罐充放热功率的修正量实际DR削减量的调整但通常只在日前签约量上下一定范围内调整日内阶段的目标是最小化运行成本的修正量同时要保证跟踪日前计划的偏差尽量小。在代码实现层面这是通过在目标函数中加入偏差惩罚项实现的min Σ(燃料成本修正 购电成本修正 DR补偿修正) λ * Σ(实际出力 - 日前计划的偏差)²λ是惩罚系数取值大小决定了日内优化对日前计划的“忠诚度”。λ设得太大日内优化基本不敢偏离日前计划等于没做λ设得太小日前计划形同虚设机组启停状态可能无法满足实际负荷需求。实际调参经验是λ的量级应该与边际成本系数相当这样日内优化既有修正能力又不会任意偏离。2.4 两阶段之间的衔接逻辑两阶段之间的数据传递是代码架构里最容易被忽略的部分。我的做法是日前阶段结束后把以下结果保存到一个结构体schedule_day_ahead中——机组启停状态u_onoff、CHP电出力P_chp_da、CHP热出力H_chp_da、电锅炉出力P_eb_da、储热罐充放热功率H_st_da正为放热负为充热、DR削减量L_dr_da、购电功率P_buy_da。日内滚动优化时每一轮的输入包括上一轮日前计划中对应时段的值实时更新的超短期预测风电出力、电负荷、热负荷当前设备实际运行状态然后输出修正量ΔP_chp、ΔH_chp、ΔP_eb等实际执行值 日前计划值 修正量。P_chp_actual_t P_chp_da_t ΔP_chp_t这个衔接关系写清楚后代码的模块边界就非常清晰了。3. Matlab代码实现的核心架构从数据输入到结果输出3.1 代码模块划分与主要函数Matlab实现这套调度策略我推荐按下面的模块划分组织代码文件各模块职责单一方便调试和复用main_optimization.m % 主程序依次调用各模块 load_data.m % 读取负荷、风电、电价等基础数据 build_system_params.m % 定义系统设备参数 build_day_ahead_model.m % 构建日前优化模型YALMIP变量、约束、目标 solve_day_ahead.m % 求解日前问题 build_intraday_model.m % 构建日内滚动优化模型 solve_intraday.m % 求解日内问题 plot_results.m % 结果可视化我个人习惯把所有设备参数集中放在build_system_params.m里用一个结构体params统一管理。这样后面做敏感性分析比如改储热罐容量、改DR弹性系数时只需要修改这个文件中的参数值不需要在模型文件里到处找。3.2 用电热综合能源系统专用建模框架还是YALMIPMatlab里做优化调度主流的建模方式有三种直接用Matlab优化工具箱linprog线性规划、intlinprog混合整数线性规划、fmincon非线性规划。优点是零依赖、不需要额外安装缺点是把庞大的调度模型写成矩阵形式很痛苦尤其是热网节点网络约束多的情况下矩阵索引容易写错。YALMIP 外部求解器YALMIP是一个Matlab建模工具箱可以用接近数学表达式的形式描述变量和约束然后调用CPLEX、Gurobi、MOSEK等商业求解器。代码可读性远高于手写矩阵而且求解效率也不错。这是我最推荐的方式。MATPOWER等专用工具主要针对电力系统潮流计算虽然也能做优化但热网部分需要自己扩展不如YALMIP灵活。我见过一些同学坚持用intlinprog手写矩阵最后因为约束索引错了一位导致结果出现“负热产出”这种根本不可能出现的数值而查了半天。我的建议是只要模型的约束超过20行就直接上YALMIP。YALMIP的安装很简单把下载的文件夹解压后加入Matlab路径即可。求解器方面CPLEX和Gurobi是主流选择如果实验室没有商业求解器授权可以用SCIP或者CBC代替Matlab自带的intlinprog也可以作为兜底选项但求解大规模MILP会慢一些。3.3 日前优化模型的核心代码骨架下面给出日前优化模型的核心代码骨架基于YALMIP语法。这里展示的是经过我实际调试通过的简化版本完整版主要是增加更多设备约束和更精细的目标函数项。% 定义决策变量 u_chp binvar(1, T); % CHP启停状态 P_chp sdpvar(1, T); % CHP电出力 H_chp sdpvar(1, T); % CHP热出力 P_eb sdpvar(1, T); % 电锅炉输入电功率 H_st sdpvar(1, T); % 储热罐放热功率正放热负充热 SOC_st sdpvar(1, T); % 储热罐储热状态 L_dr sdpvar(1, T); % DR削减量 P_buy sdpvar(1, T); % 从电网购电功率 Constraints []; % 电功率平衡约束不考虑网络潮流简化为单节点 Constraints [Constraints, P_chp P_wind - P_eb P_buy P_load - L_dr]; % 热功率平衡约束 Constraints [Constraints, H_chp H_eb H_st H_load]; % 电锅炉产热效率eta_eb H_eb params.eta_eb * P_eb; Constraints [Constraints, 0 P_eb params.P_eb_max]; % CHP可行运行域约束以两段式线性近似为例 % 简化形式H_chp alpha_1 * P_chp beta_1 % H_chp alpha_2 * P_chp beta_2 Constraints [Constraints, H_chp params.alpha_1 * P_chp params.beta_1 * u_chp]; Constraints [Constraints, H_chp params.alpha_2 * P_chp params.beta_2 * u_chp]; % 电出力上下限 Constraints [Constraints, params.P_chp_min * u_chp P_chp params.P_chp_max * u_chp]; % 储热罐动态约束 Constraints [Constraints, SOC_st(:, t1) SOC_st(:, t) * (1 - params.eta_loss) - H_st(:, t)]; Constraints [Constraints, -params.H_st_max H_st params.H_st_max]; Constraints [Constraints, params.SOC_min SOC_st params.SOC_max]; Constraints [Constraints, SOC_st(:, T1) SOC_st(:, 1)]; % 周期约束 % DR削减量约束 Constraints [Constraints, 0 L_dr params.L_dr_max]; % 目标函数总运行成本最小化 Objective 0; for t 1:T % 燃料成本CHP二次函数近似为分段线性 Objective Objective (params.a * P_chp(t)^2 params.b * P_chp(t) params.c) * u_chp(t); % 购电成本 Objective Objective params.price_buy(t) * P_buy(t); % DR补偿成本 Objective Objective params.price_dr * L_dr(t); end % 求解 ops sdpsettings(solver, cplex, verbose, 2); optimize(Constraints, Objective, ops); % 提取结果 P_chp_da value(P_chp); H_chp_da value(H_chp); % ...如果你用的是linprog还需要自己把二次项处理掉但既然用了YALMIP二次目标函数是可以直接交给CPLEX处理的CPLEX支持MIQP省很多事。3.4 日内滚动优化的关键代码逻辑日内部分我采用的是滚动时域优化Rolling Horizon。所谓滚动就是每过15分钟重新求解一次未来4小时的优化问题并且只执行第一个时段的结果。Matlab里用循环模拟%% 日内滚动优化循环 for k 1:T_intraday_total % 每个滚动时刻 % k时刻的优化窗口为[k, kH_intraday-1] t_start k; t_end min(k H_intraday - 1, T_intraday_total); % 构建日内优化问题 [Constraints, Objective] build_intraday_model(t_start, t_end, ...); % 求解 optimize(Constraints, Objective, ops); % 只执行第一个时段结果 P_chp_execute(k) value(P_chp_intraday(1)); % 更新下一轮初始状态如储热罐SOC SOC_current value(SOC_intraday(1)); % 读取新一轮超短期预测数据 [P_wind_hat, P_load_hat, H_load_hat] get_forecast(k1, kH_intraday); end这里有一个非常重要的实现细节日内优化的决策时间段索引必须与日前保持一致。也就是说如果日前计划是1小时一个时段日内优化是15分钟一个时段那么在日内修正日前计划时需要把日前计划插值或者对应到日内的时间粒度上。我通常的做法是日前按24时段1小时日内按96时段15分钟日前计划在每个15分钟时段内取相同值即P_chp_da_15min(k) P_chp_da(ceil(k/4))。这样对应关系清楚不容易出错。4. 建模与求解中的关键细节可行域、爬坡约束与非线性处理4.1 CHP可行运行域的正确描述方式很多初学者的代码里CHP的约束就是P_min ≤ P ≤ P_max和H_min ≤ H ≤ H_max两条边界独立约束这就完全忽略了热电耦合关系。实际上CHP的运行范围在(P, H)平面上是一个凸多边形由若干个顶点定义。描述方式是用一组线性不等式A_region * [P; H] ≤ b_region以常见的抽汽式CHP为例可行运行域的顶点通常有4~6个。在Matlab代码里最稳妥的做法是直接把顶点坐标写在参数文件里然后用polytope或者简单的手写线性不等式组描述。% CHP可行运行域顶点坐标示例 vertices [0, 0; 100, 0; 100, 50; 60, 80; 0, 80]; % 求半空间表示 A*x b [A_region, b_region] vert2lcon(vertices); % 需要鲁棒控制工具箱或者自己写凸包算法如果你没有vert2lcon函数可以自己写一个基于凸包算法的函数或者简单点直接把顶点代入不等式推导出A、b矩阵。这些不等式中的每一行都表示可行域的一个边界条件写入约束时注意乘以机组的0-1启停变量确保停机时可行域收缩为原点A_region * [P_chp; H_chp] ≤ b_region * u_chp4.2 爬坡约束的时间粒度统一爬坡约束是另一处容易踩坑的地方。CHP机组的爬坡约束写作-P_ramp_down ≤ P_chp(t) - P_chp(t-1) ≤ P_ramp_up这里爬坡速率单位是MW/h如果你的日前模型时间步长是1小时那么不等式右边直接用爬坡速率即可。但如果日内模型步长是15分钟就需要写-P_ramp_down / 4 ≤ P_intraday(k) - P_intraday(k-1) ≤ P_ramp_up / 4很多同学在这里忘记除以4导致日内模型爬坡约束过紧求解结果出现机组无法跟踪预测负荷的情况。同样的逻辑适用于储热罐、电锅炉的功率变化约束。4.3 非线性项的处理二次成本函数与双线性约束CHP燃料成本一般是二次函数如C a * P² b * P c * H d。在MILP里直接处理二次函数有两条路一是用CPLEX/Gurobi直接求解MIQP如果你的模型规模不大这是最简单的二是做分段线性近似Piecewise Linear Approximation把二次曲线切成若干段每段用线性函数近似加上SOS2约束Special Ordered Set Type 2或用二进制变量选择线性段。我的建议是先用MIQP跑通流程如果求解速度不满足要求再换成分段线性近似。YALMIP对这两种方式都支持切换成本不高。用sdpvar建二次目标后直接求解如果换成PWL只需要把目标函数里的二次项替换成sdpvarbinvar的组合。还有一个常见的双线性约束是电锅炉的耗电量和产热量之间的关系如果效率恒定是线性关系H η * P没问题。但如果考虑效率随负荷率变化就变成了双线性约束H η(P) * P这时候要么拟合线性关系要么分段线性化。实际算例中电锅炉效率随负荷变化不大用恒定效率足够。4.4 储热罐的动态建模与周期约束储热罐的模型是典型的离散时间状态转移方程SOC(t1) (1 - η_loss) * SOC(t) - H_st(t) * Δt这里H_st(t)是放热功率正为放热负为充热。值得注意的是如果同时考虑充热效率和放热效率约束形式会变为分段形式SOC(t1) (1 - η_loss) * SOC(t) - H_st_discharge(t) / η_dis H_st_charge(t) * η_ch这就引入了一对互补变量H_st_discharge和H_st_charge加上两者不同时为正的约束。YALMIP里可以用implies写逻辑约束或者通过引入二进制变量来处理。如果只是做简化研究用单效率形式SOC(t1) (1-η_loss)*SOC(t) - η_st * H_st(t)也可行。周期约束SOC(T1) SOC(1)也是一个容易忽略但很重要的约束它保证调度周期结束时储热罐回到初始状态使方案具备可持续性。如果不加这个约束优化器倾向于在周期结束时把储热罐的热量全部放完获取最大收益。5. 实际调试过程中踩过的坑与解决手段5.1 模型不可行不是约束写错了就是平衡方程错了做调度优化最常见的异常就是求解器返回infeasible problem。如果你是第一次跑通代码出现不可行不要太惊讶这是常态。我的排查顺序是这样的检查功率平衡约束把各自的出力上下限求和看是否覆盖负荷变化范围。比如夜间热负荷高、风电出力大而电负荷低如果电锅炉最大耗电功率不足以消纳全部风电电功率平衡约束就可能无法满足。这时候需要放宽约束允许弃风或者提高电锅炉容量。检查储热罐SOC范围如果SOC初始值设定了而周期约束要求SOC(T1)SOC(1)那么在储热罐容量很小的情况下可能找不到可行解。解决办法是引入松弛变量允许周期约束有小偏差并在目标函数中加惩罚项。检查0-1变量与连续变量的耦合比如启停状态为0时出力必须为0这个逻辑如果漏写了某个变量求解器可能给出“机组停机但仍有出力”的荒谬结果甚至导致平衡约束崩掉。YALMIP有一个好用的小技巧在求解前用check命令检查每一个约束的残差optimize(Constraints, Objective, ops); check(Constraints);它会把每个约束的边界值打出来残差不为0的约束就是问题所在。5.2 求解时间过长整数变量太多是罪魁祸首日前24时段的MILP模型如果有2台CHP机组每台机组每时段1个0-1变量那就是48个二进制变量。这个规模对CPLEX来说是小意思几秒钟就能求出来。但如果你的模型规模变大——比如细化到96时段、机组数量增加到5台、再加上储热罐的充放二进制变量、DR的分段线性二进制变量——整数变量数量会轻松突破500个求解时间可能从几秒飙升到十几分钟甚至几小时。我的经验是优先优化模型结构而非依赖求解器检查是否存在冗余约束是否存在可以合并的变量。减少不必要的0-1变量储热罐充放热如果通过H_st的正负自然区分其实可以不引入二进制变量只要目标函数对充放热的“成本”或“收益”处理得当优化器自然会选择合理的充放策略不需要强制约束“不能同时充放”。但在某些场景下充放热价格不同就需要二进制变量。这个要根据具体模型权衡。设置求解器参数CPLEX的MIP gap容忍度从默认的1e-4放宽到0.01求解速度可以有数量级的提升。对调度问题来说1%的精度损失完全可接受。ops sdpsettings(solver, cplex, cplex.mip.tolerances.mipgap, 0.01);使用热启动warm start日内滚动优化时上一轮的优化结果可以作为下一轮优化的初始解大幅缩短求解时间。YALMIP里通过assign和optimize(..., solver, cplex, cplex.mip.limits...)的方式不太好做但CPLEX本身提供了通过x0参数指定初始解的功能利用滚动优化相邻时段解相似的特点能显著加速。5.3 需求响应模型调参的微妙之处需求响应环节的参数对结果影响非常大调节的时候要格外小心。价格弹性矩阵的对称性实际论文中通常假设交叉弹性是对称的即E_ij E_ji。但在代码里如果不加对称性约束优化结果会出现很奇怪的负荷转移路径——比如用户把负荷从电价高的时段转移到电价更低的时段这本身是合理的但如果弹性矩阵不对称可能出现循环转移导致负荷总量不守恒。所以建模时一定要保证sum(L_0_t) - sum(ΔL_t) 总负荷约束DR削减量的时段分布如果DR补偿价格设置过高比如高于机组的边际发电成本优化器就会倾向于大量削减负荷导致结果失真。合理的DR补偿价格应该参考系统的边际成本。我调试时一般先不加DR跑一遍系统边际电价然后以边际电价的1.2~1.5倍作为DR补偿价格的初始值。5.4 数值尺度问题兆瓦和千瓦混用会让求解器崩溃在综合能源系统里电功率用MW、热功率用GJ/h它们之间的量级可能差出几个数量级。如果某些变量的系数在1e0量级另一些在1e6量级YALMIP会给出数值警告CPLEX的求解效率也会大幅下降。一个非常实用的技巧是建模时全部采用统一的标幺值或者统一自然单位避免MW和kW混用。我一般统一用MW和MWh热功率统一用MWth兆瓦热这样电和热在数值上处于同一量级求解器的数值稳定性好很多。储热罐容量单位也换算成MWh而不是保留GJ以免出现1e3量级的跳跃。如果仍然遇到数值问题可以在sdpsettings里开启cplex.emphasis.numerical, 1让求解器重点处理数值稳定性。6. 结果分析与可视化让调度策略“看得见”6.1 结果分析的三张核心图代码跑通后分析结果时我一般会先画三张图第一张电力平衡图。横轴是时间24h或96时段纵轴是功率MW。堆叠显示CHP电出力、风电出力、购电、DR削减、电锅炉耗电与电负荷曲线对比。这张图能直观看出调度策略是怎么平衡电力的——哪些时段在买电哪些时段在消纳风电。第二张热力平衡图。显示CHP热出力、电锅炉产热、储热罐充放热与热负荷的匹配情况。重点关注储热罐的充放热动作是否与电负荷的峰谷时段有逻辑关系——典型策略是夜间风电大发时段电负荷低谷用多余电力通过电锅炉产热并储存在储热罐中日间电负荷高峰时段储热罐放热补充热负荷让CHP减少供热、增加发电。第三张需求响应削减量分布图。直接从图中能看出来DR资源是否被合理利用是否过度集中在某几个时段。6.2 用case对比体现两阶段的价值两阶段模型相比单阶段模型的好处不能光靠嘴说要设计定量对比。我的做法是设计三个场景场景A不考虑需求响应单阶段日前调度。场景B考虑需求响应单阶段日前调度。场景C考虑需求响应两阶段日前日内调度。对比三个场景的总运行成本和风电消纳率。如果场景C的成本低于场景B、弃风率也低于场景B就说明两阶段框架和需求响应机制确实发挥了作用。这种定量对比是论文审稿人最看重的内容。6.3 Matlab绘图的几个实用设置画功率平衡图时用area函数比用plot堆叠效果好很多figure; h area(t, [P_chp; P_wind; P_buy], LineWidth, 0.5); hold on; plot(t, P_load, k-, LineWidth, 2); % 负荷曲线单独画在上面 legend(CHP, 风电, 购电, 电负荷); xlabel(时间 (h)); ylabel(功率 (MW));注意area需要按列输入堆叠数据且矩阵顺序决定了堆叠的上下次序。如果显示结果发现堆叠图顺序不对把数据矩阵转置一下就好。还有一个非常容易被忽视的问题——坐标轴字体大小。Matlab默认的坐标轴字体在论文插图中偏小一般需要手动设置set(gca, FontSize, 11, FontName, Times New Roman);另外打印图片时建议用exportgraphics(gca, fig1.png, Resolution, 300)导出300dpi的图片保证期刊印刷清晰。7. 代码扩展方向怎么把这个框架改造成你自己的项目7.1 加入网络约束我上面这套模型是单节点模型即不考虑电网潮流和热网管道损耗只考虑功率平衡。如果你的项目需要加入网络约束可以在Matlab里引入电力系统的直流潮流模型DC Power Flow和热力系统的节点法模型Heat Network Model。注意这两种网络约束的引入都会显著增加变量数量和约束数量求解时间也会变长。建议先用单节点模型跑通整体逻辑再逐步加网络约束。电力的直流潮流模型约束形式为P_branch B * θ -P_branch_max ≤ P_branch ≤ P_branch_max热网部分稍微复杂一些需要引入节点温度变量、管道流量变量和热力平衡方程通常表示为非线性约束。在MATLAB里处理热网非线性约束时建议先做线性化近似否则求解速度难以接受。7.2 考虑不确定性场景法或鲁棒优化日前阶段的预测误差影响可以通过引入不确定性集合来处理。最常见的做法是场景法Scenario-Based Stochastic Programming即生成多个风电出力场景让日前优化在期望意义下最优。Matlab里生成风电场景可以用scenario generation的方法比如基于历史数据的蒙特卡洛抽样或拉丁超立方抽样。另一种思路是鲁棒优化构造风电出力的盒式不确定性集合让方案在所有可能的风电出力下都可调。鲁棒优化的代码实现主要是在目标函数和约束里引入对偶变量和不确定性预算约束逻辑上会比场景法复杂一些。7.3 把代码封装成可复用的函数库如果这个方向准备长期做建议把模型构建、求解、结果分析都封装成独立函数通过参数配置来切换不同的算例场景。这样后续做多场景对比、参数敏感性分析时只需要写一个循环调用这些函数而不是每次重新改主程序。一个典型的参数敏感性分析代码长这样for cap_st 100:100:1000 % 遍历储热罐容量 params.SOC_max cap_st; run_optimization_scenario(); % 调用主优化函数 total_cost(cap_st) result.cost; wind_curtailment(cap_st) result.curtailment; end然后用一句plot就能画出储热罐容量对总成本和弃风率的影响曲线这就是很好的论文素材。写作并调试这套电-热综合能源系统两阶段调度模型的过程中我最大的体会是“两阶段”不是简单的两次优化而是决策变量在时间维度上的分层。代码实现上的难点不在于求解器能解多大规模的问题而在于你是否把“哪些变量应该昨天定”、“哪些变量应该今天调”这个逻辑理清楚。理清了这一点YALMIP代码的架构就是水到渠成的事。另外不要迷信复杂的模型——把CHP可行域、储热罐动态、DR约束这几个核心环节做扎实用一个适中的算例跑通得到的调度结果已经足够说明问题。