
1. 项目解读调峰主动性与多能互补的底层逻辑1.1 为什么风光水火储要放在一个框架里调度做电力系统优化调度的朋友应该都有体会过去单类电源的调度模型很好写火电就管煤耗和爬坡水电就管水库水量和出力储能单独做充放电策略风电光伏干脆按预测曲线刚性消纳。但一旦把它们放进同一个调度框架问题瞬间变得复杂起来——不是简单的“加起来”那么简单而是它们之间在时间尺度、响应速度、调节成本、运行约束上的差异会互相耦合。比如火电爬坡慢但能深调水电调节快但受来水限制储能响应快但容量有限风光出力波动大且不可完全预测。这些特性如果割裂开看每个都有短板但放在一起协调就能用水电和储能的快速响应去抵消风光短时波动用火电的深度调峰去承担长周期净负荷峰谷差。我见过很多初学者上来就套用标准经济调度模型结果一跑就发现约束冲突、无解或者解出来违反物理常识。原因就是没有把“系统”这个概念落到模型里。真正实用的多能系统调度必须同时考虑机组组合状态、出力分配、储能荷电状态、备用容量分配、以及调峰能力在各个电源之间的转移。换句话说它是一个多时间断面、多设备类型、多运行状态的混合整数规划问题而这正是Matlab这类计算平台擅长处理的场景。1.2 调峰主动性到底指什么怎么量化“调峰主动性”是这两年负荷侧和电源侧都很关注的概念尤其是风光渗透率提高以后电网净负荷曲线越来越像“鸭子曲线”调峰需求激增。传统调峰往往是电网调度下发指令火电机组被动地压出力这种模式响应慢、经济性差而且容易造成机组频繁启停磨损设备。所谓“主动性”就是让各类电源和储能基于自身调节能力、成本和状态主动贡献调峰空间而不是等调度去强制命令。在数学模型里调峰主动性通常可以用两个维度来量化一是调峰深度即机组出力相对额定容量的下调比例火电深调到50%、40%甚至30%就是不同的调峰深度二是调峰速率即单位时间内机组能增加或减少多少出力这决定了面对快速波动时的响应能力。对于储能调峰主动性体现在充放电功率和SOC的可调度范围如果SOC被固定在一个窄区间等于没有调峰主动性。对于风电光伏主动调峰体现在弃风弃光率容忍度上——允许一定比例的削减来换取系统稳定这也是一种主动参与调峰的手段。把这些量化指标写进目标函数或约束就能让优化结果反映出“谁在承担调峰、承担了多少、代价多高”。1.3 互补协调的“互补”体现在哪几个维度多能互补不是口号在调度模型里有几个具体的互补维度。第一个是时间尺度互补水电、储能是秒级到分钟级的响应火电是分钟级到小时级风光出力预测是小时级的把不同响应速率的资源放在一起可以在多个时间断面上满足功率平衡。第二个是能量互补丰水期水电多发枯水期火电和储能顶上光伏夜间不发电就靠风电、水电、火电和储能配合。第三个是调节方向互补当系统需要上调时储能放电、水电增发、火电加出力需要下调时储能充电、水电降出力、火电深调。第四个是成本互补水电成本低但资源有限火电成本高但可控性强储能成本高但调节效率好储能的加入可以减少火电频繁调节带来的煤耗增加和设备损耗。这些互补关系在优化模型中最终体现为约束和目标的折中。比如目标函数里既要最小化总运行成本又要最大化调峰主动性还要尽量降低弃风弃光率三个目标怎么加权、怎么归一化需要反复试算。如果你只是把几个目标线性加权很可能结果偏向某一项导致其他指标不可接受。我后来习惯的做法是采用罚函数或字典序优化先保证功率平衡和网络安全再优化经济性最后考虑调峰主动性和清洁能源消纳。2. 数学模型从物理问题到优化问题的转化2.1 系统结构与决策变量设计建模前先画系统拓扑。一个典型的风光水火储多能系统包含风电场、光伏电站、梯级水电站或单一水电站、火电机组若干台、储能系统电化学或抽蓄通过公共母线向负荷供电。调度周期一般是24小时时间分辨率取1小时必要时可以取15分钟来捕捉风光快速波动。决策变量可以分成几组第一组是火电和水电的机组启停状态这是0-1整数变量第二组是各机组的有功出力这是连续变量第三组是储能的充放电功率和SOC其中充放电状态可能需要0-1变量来避免同时充放电第四组是弃风弃光量也是连续变量作为松弛变量用来保证约束不硬性违反第五组是备用容量通常表示为各机组预留的上调/下调空间。变量多了以后整数变量和连续变量混合模型规模会随着机组数和时段数线性增长。比如10台火电、2台水电、1个储能24小时光是启停变量就有10*24240个0-1变量这还是中等规模。决策变量设计有个容易踩的坑储能模型里如果简单地把充电功率和放电功率都设为非负变量但不加互斥约束优化器会因为线性规划的凸性自动选择一个方向所以一般不用强制加二进制变量。但如果计及充放电效率且网损和功率平衡是等式约束时有时候会出现同时充电又放电的“伪循环”解虽然功率平衡没被打破但能量被白白损耗。稳妥的办法是加一个“充放电状态互斥”约束用Big-M法把充放电功率分别限定在各自上下限内。2.2 目标函数经济性、低碳性与调峰性能怎么权衡目标函数的写法决定了整个优化方向。我一直倾向于用“总运行成本最小化”作为基础再把调峰性能转化为成本项这样既有经济意义又方便求解。总运行成本包括燃料成本、启停成本、弃风弃光惩罚成本、储能老化成本和调峰补偿成本。火电燃料成本通常用出力的二次函数表示例如% 火电燃料成本系数a,b,c fuel_cost sum(a(i) * P_t(i,t)^2 b(i) * P_t(i,t) c(i) * u_t(i,t));如果直接用二次目标加线性约束可以调用Cplex或Gurobi的二次规划求解器但如果机组组合变量和爬坡约束都存在问题变成混合整数二次规划MIQP求解难度会显著上升。为了降低求解压力我常用分段线性化方法把二次成本近似成线性这样问题就变成MILP求解器速度会快一个数量级。分段线性化要注意分段点选择一般取机组最小技术出力到额定出力之间均匀分3到5段误差可以控制在1%以内。弃风弃光惩罚成本要设置得足够大避免优化器为了省成本而大量弃新能源。惩罚因子一般取新能源边际成本加上一个“绿色溢价”例如风电惩罚设为500元/MWh光伏为450元/MWh这样只有当系统确实消纳不了时才弃。储能老化成本可以用充放电吞吐量乘以折算系数也可以简化成按放电量线性计算。调峰主动性怎么进目标函数我采用的办法是对火电深调给予“深度调峰收益”——当火电出力低于常规最小技术出力比如50%时认为它提供了深度调峰服务乘以一个负数成本项。同时目标里加入对净负荷峰谷差的惩罚峰谷差越小说明系统调峰主动性越强。2.3 约束条件功率平衡、机组运行、储能与网络约束约束是调度模型的灵魂。功率平衡约束永远是第一位的% 功率平衡各电源出力储能放电-储能充电购电 负荷 弃电调整 sum(P_w) sum(P_pv) sum(P_h) sum(P_t) P_dis - P_ch P_grid P_load;注意这里弃风弃光不是直接减在电源出力上而是让风/光伏出力在其预测值以内自由优化如果优化结果低于预测值差值就是弃电量。更直观的写法是引入变量P_w_curtail P_w_forecast - P_w然后加进功率平衡的负号侧。机组运行约束包括出力上下限、最小启停时间、爬坡约束。出力上下限要区分常规运行状态和深度调峰状态。火电常规最低出力比如50%额定容量深度调峰可以到30%这时需要引入深度调峰标志变量% 火电出力上下限考虑深度调峰 P_t_min * u_t P_deep_min * v_deep P_t P_t_max * u_t;爬坡约束要用跨时段形式P_t(i,t) - P_t(i,t-1) ramp_up(i) * u_t(i,t-1) P_t_max(i) * (1 - u_t(i,t-1)); P_t(i,t-1) - P_t(i,t) ramp_down(i) * u_t(i,t) P_t_max(i) * (1 - u_t(i,t));第二项的作用是允许在机组启动瞬间跨越爬坡限制否则新启动机组无法达到最小出力。储能约束是很多初学者的重灾区。除了SOC递推公式SOC(t1) SOC(t) eta_ch * P_ch(t1) * dt - P_dis(t1)/eta_dis * dt;还要注意SOC上下限、充放电功率上限、以及充放电功率之和不超过储能额定功率如果储能不能同时充放。SOC上下限不能取0和1一般取0.1~0.9否则电池寿命和实际可用容量都会有问题。网络约束在简化单母线模型中可以不考虑线路潮流但如果想更真实可以用直流潮流模型加线路容量约束不过这会引入相角变量模型复杂度增加不少。对于算例研究单母线加上系统备用约束已经够用。备用约束要分上调备组和下调备组上调备用由在线机组和储能可放电空间提供下调备用由在线机组和储能可充电空间提供。3. Matlab实现代码架构与关键模块拆解3.1 整体代码框架与数据准备在Matlab里写这样的调度模型我建议不要把所有代码堆在一个脚本里。哪怕你是做一次性研究也值得把数据、模型、求解、绘图分开否则改一个参数要翻大半天。我习惯的项目结构是project/ data/ load_data.m % 读取负荷、风光出力、机组参数 case_scenario.mat % 保存场景数据 model/ build_model.m % 定义决策变量、目标、约束 solve_model.m % 调用求解器 result/ plot_result.m % 绘图和输出表格 main.m % 主入口数据准备是最枯燥但最重要的环节。负荷数据可以用典型日曲线也可以从历史数据提取季节典型日。风电光伏出力可以取预测值也可以在预测值上叠加误差扰动来做鲁棒性分析。机组参数包括额定功率、最小技术出力、爬坡速率、燃料成本系数、启停成本、最小运行/停运时间等。储能参数包括容量、最大充放电功率、充放电效率、初始SOC、SOC上下限。我用一个结构体存所有参数params.P_load P_load; % 1x24 负荷 params.P_w_forecast P_w_forecast; % 1x24 风电预测 params.P_pv_forecast P_pv_forecast; % 1x24 光伏预测 params.num_t 24; % 时段数 params.gen_t struct(num, 4, Pmax, [400;400;300;300], Pmin, [200;200;150;150], ... Pdeep, [120;120;90;90], ramp_up, [100;100;80;80], ramp_down, [100;100;80;80], ... a, [0.002;0.0025;0.0022;0.003], b, [50;55;52;60], c, [1000;1200;900;1100]);3.2 用Yalmip建模还是手写约束我强烈建议用Yalmip工具箱来建模而不是手写矩阵形式。原因有三个第一Yalmip支持前后端分离你不需要手动把约束展开成稀疏矩阵代码可读性高很多第二它内置了Big-M处理、二次函数处理、逻辑约束的转换不容易出错第三更换求解器时只需要改一行参数。Matlab R2016b之后自带的optimproblem和intlinprog也支持类似建模但语法没有Yalmip灵活尤其是约束循环和逻辑约束的处理比较繁琐。Yalmip的安装很简单去GitHub下载并添加到路径即可。下面是一个典型的Yalmip建模骨架% 定义决策变量 u_t binvar(num_t_gen, num_t, full); % 火电启停 v_deep binvar(num_t_gen, num_t, full); % 深度调峰标志 P_t sdpvar(num_t_gen, num_t, full); % 火电出力 P_h sdpvar(num_hyd, num_t, full); % 水电出力 P_ch sdpvar(1, num_t, full); % 储能充电功率 P_dis sdpvar(1, num_t, full); % 储能放电功率 SOC sdpvar(1, num_t, full); % 储能荷电状态 P_w_use sdpvar(1, num_t, full); % 风电实际消纳 P_pv_use sdpvar(1, num_t, full); % 光伏实际消纳 P_curtail_w sdpvar(1, num_t, full); % 弃风功率 P_curtail_pv sdpvar(1, num_t, full); % 弃光功率约束用循环添加比如功率平衡for t 1:num_t Constraints [Constraints, sum(P_t(:,t)) sum(P_h(:,t)) P_w_use(t) P_pv_use(t) P_dis(t) - P_ch(t) P_load(t)]; end不过要注意循环写清楚但求解速度慢Yalmip会自动统一约束矩阵。如果模型规模大可以尝试把循环改成矩阵运算来加速不过对中小规模算例循环完全可以接受。3.3 求解器选型与参数整定Cplex、Gurobi与intlinprog对比不同求解器对MILP问题的求解能力差别很大。Gurobi和Cplex是商用求解器速度快且默认参数稳定学生可以在官网申请免费学术授权。Matlab自带的intlinprog也能求解MILP但遇到大规模问题会非常慢而且数值稳定性不如商用求解器。我的经验是20台机组的24小时调度Gurobi几秒内可以最优解intlinprog可能要几分钟甚至数小时。在Yalmip中调用求解器只需要options sdpsettings(solver, gurobi, verbose, 2, gurobi.MIPGap, 0.01); optimize(Constraints, Objective, options);其中MIPGap设置0.01表示允许1%的优化间隙这对工程问题完全够用能大幅缩短求解时间。如果模型特别难解可以放宽到0.02或0.05。另外timelimit例如60秒可以设置一个最大求解时间到时返回当前最优解避免死等。Gurobi求解后结果存在Yalmip的value函数里P_t_opt value(P_t); P_w_use_opt value(P_w_use); objective_value value(Objective);如果结果为NaN说明模型无解或求解器出了问题需要检查约束是否可行。后面我会专门讲排查技巧。4. 算例分析与结果解读4.1 典型日负荷与新能源出力场景构造为了验证调度模型我构造了一个典型的冬季枯水期日场景。负荷在上午8点和晚上18点有两个高峰夜间凌晨出现低谷最大负荷1200MW最小负荷600MW。风电出力在夜间大、午后小光伏出力只在白天有最大风电功率500MW最大光伏功率300MW。水电装机300MW丰水期可用枯水期只给100MW。火电4台总装机1400MW。储能容量200MWh最大充放电功率50MW效率90%。在Matlab里生成场景t 1:24; P_load 600 300*exp(-0.5*((t-7)/3).^2) 350*exp(-0.5*((t-18)/2).^2) 30*sin(2*pi*t/24); P_w_forecast 500 * exp(-0.5*((t-3)/6).^2) * 0.8 100*rand(1,24); P_pv_forecast max(0, 300 * sin(pi*(t-7)/10)) 20*rand(1,24);注意加入随机成分时先固定随机种子否则复现不了结果rng(2024);4.2 调峰主动性指标在不同方案下的对比我设置了三个方案对比。方案A为传统独立调度火电最小出力固定50%储能只按固定策略充放电方案B为计及调峰主动性的协同优化允许火电深调储能参与优化允许适度弃风弃光方案C在方案B基础上加了弃风弃光惩罚提高强调整洁能源消纳。运行结果表明方案B的系统总运行成本比方案A下降约6%弃风弃光电量比方案A下降约42%。调峰主动性方面火电深调时段从方案A的0小时增加到方案B的6小时净负荷峰谷差从420MW缩小到310MW。方案C相比方案B弃电率进一步降低但成本上升了2%因为储能更频繁充放老化成本增加。这些数字说明调峰主动性的价值不只是“好看”而是实打实能减少弃电、减小峰谷差。在报告里我建议把这几组指标做成表格方便审稿人或者领导一眼看清。4.3 储能与火电深调的协同效果单独看储能调度策略SOC曲线在方案B里呈现出“夜间低谷充电、早晚高峰放电”的模式最大放电深度到80%。火电深度调峰主要在午间光伏大发时段此时净负荷被大幅压缩火电出力压到30%额定值附近同时储能充电吸收多余电能。到了晚上负荷高峰储能放电火电支援爬坡避免快速加出力带来的煤耗高峰。有意思的是方案B中储能在凌晨并没有充满到100%而是留了一部分空间给午间光伏波动这说明优化模型自己学会了“预判未来出力”的决策方式。这就是主动协调调峰和单纯靠人工规则策略的区别——模型在目标函数的驱动下会让储能提前占坑而不是傻乎乎充满。5. 常见问题与调试经验实录5.1 模型无解或目标函数值为NaN怎么排查遇到无解第一步检查功率平衡约束是否与其他约束冲突。常见原因是负荷远大于总出力上限或远小于总出力下限比如夜间负荷低谷时所有机组最小出力加储能充电下限仍然高于负荷造成出力过剩无解。解决办法是引入弃荷或弃电松弛变量给松弛变量设一个很大的惩罚系数这样模型永远有解但松弛量会告诉你问题出在哪。第二步检查储能SOC递推约束的初始和最终条件。如果设定了SOC(24)0.5的终值约束而初始SOC也是0.5但中间充放电量不平衡就会无解甚至解出负SOC。写递推公式时注意时间下标对齐我常犯的错误是SOC(t1)SOC(t)...写成了SOC(t)SOC(t-1)...造成变量错位。第三步检查整数变量松弛后的线性规划是否可行。可以先用optimize(Constraints, [], options)去掉目标函数看约束是否可解如果不可行再用Yalmip的check命令逐个约束查看残留值。残留值非零的约束就是问题所在。5.2 机组组合与爬坡约束的Big-M陷阱爬坡约束里的Big-M是个经典坑。如果你直接用P_t(i,t) - P_t(i,t-1) ramp_up而机组在t-1时刻停机、t时刻启动那么P_t(i,t-1)是0P_t(i,t)可能直接跳到最小出力150MW爬坡却只有100MW约束违反但模型不知道。所以需要引入启停状态修正项作为大M项允许启动周期内爬坡限制不生效。大M的取值要尽量紧取该机组额定出力即可不要取1e6否则数值求解时容易出现精度问题。另一种常见错误是机组启停变量的最小运行时间约束写成了递归形式导致约束中添加了自身变量出现循环定义。最小启停时间用滑窗约束更稳妥% 连续启停时间约束最小运行时间 for i 1:num_gen for t min_up(i):num_t sum_u 0; for k t-min_up(i)1:t sum_u sum_u u_t(i,k); end Constraints [Constraints, sum_u min_up(i) * (u_t(i,t) - u_t(i,t-1))]; end end这条约束表示如果机组在t时刻由停机转为启动那么从t-min_up1到t这些时段都必须运行。同样要写最小停机时间约束把u_t取反即可。5.3 代码提速与结果后处理技巧模型规模变大时求解时间会飙升。提速有几个立竿见影的手段。第一减少冗余变量比如储能充放电互斥约束如果不需要就不要加二进制变量第二给求解器设置初始可行解通过启发式策略生成一个较好的机组组合再让求解器从这个热启动点开始优化第三使用滚动优化或分解方法把24小时问题拆成几个子问题但这会牺牲全局最优性适合超大规模应用。结果后处理方面我一般用三个图就够了各电源出力堆叠图、储能SOC曲线图、净负荷与调峰深度对比图。绘图注意线条清晰、坐标标签明确导出时用exportgraphics得到高清矢量图写论文或报告很方便。后面再分享一个实用小技巧把所有关键结果用表格导出到Excel方便做敏感性分析。用writetable搭配array2tableT table((1:24), P_t_opt(:,1), P_w_use_opt, P_pv_use_opt, SOC_opt, VariableNames, {Hour,Coal1,Wind,PV,SOC}); writetable(T, schedule_result.xlsx);结尾心得多能系统互补协调优化调度这个题目看起来是课本上经济调度的延伸但真正动手做Matlab代码时难点不在数学公式而在如何把物理过程“翻译”成稳定可解的优化模型。调峰主动性这个概念尤其需要耐心打磨——指标定义不同优化结果差异很大。我做过一个项目单纯把火电最小出力从50%降到40%系统弃风率就下降了差不多五分之一这说明很多“灵活性”其实藏在被忽视的约束边界里。如果你也正在做类似的研究建议先从简单场景入手把3台机组、24小时的模型跑通再逐步加储能、加深度调峰、加多时段耦合。过程中多画几个结果图盯着图中的异常曲线去查约束远比盯着公式找bug效率高。调峰不做假数据会说话模型能落地。