基于Matlab+CVX的储能调峰调频优化建模与实现
2026/9/15 22:35:29 网站建设 项目流程

简介:面向电力系统研究人员与工程师,该资源基于MATLAB编程语言与CVX凸优化工具箱,构建了储能系统参与调峰调频的完整优化模型,可解决新能源并网场景下负荷波动与电网频率稳定问题。压缩包共10个文件,包括6个可运行的m脚本、2个mat数据文件、1篇TPWRS参考论文及1个md说明文档,整体体积179.8MB,主程序、调峰/调频单独执行脚本、负荷与PJM调频信号数据分层排列,便于按模块调用。已有243人参与学习。资源不只给出最终模型,还覆盖储能动态建模、凸优化问题转化、控制策略设计与经济性分析等关键环节,配合论文和说明文档,能帮助读者快速掌握从数学建模到仿真求解的完整链路,适合具备一定MATLAB基础并希望深入研究储能辅助服务场景的中高级学习者。

1. 储能调峰调频模型:先想清楚再写CVX

储能调峰调频模型,本质上是把“储能什么时候充、什么时候放、功率留多少给AGC调频”变成一个数学优化问题。很多人拿到“基于matlab编程实现的储能调峰调频模型matlab+cvx.rar”这类资源,第一反应是找里面的cvxrun.m直接跑,但忽略了一个反直觉的事实:调峰和调频对储能的需求是互相拖后腿的。调峰要的是能量吞吐能力,在负荷高峰持续放电数小时;调频要的是功率响应速度,频繁正负调节,而且SOC最好停留在中间区域。两个目标不写进同一个优化问题里,或者只把约束简单拼在一起,最终算出来的储能容量和功率往往要么偏大,要么在AGC考核时出现“有容量但调不出来”的尴尬。

这篇文章就干一件事:用Matlab+CVX把这个调峰调频模型从数学式写到可运行代码,再谈目标权重、求解器选择和结果验证。适合刚开始做储能电站规划、微电网经济调度的人,也适合已经有混合整数规划经验、想快速验证想法的人。下文默认你有基础的Matlab语法,但不需要提前装CVX。

2. 储能调峰调频模型的时间尺度、目标函数与约束怎么写

2.1 调峰目标:minmax削峰与购电成本模型

先定义调度周期。调峰常见的离散粒度是15分钟或1小时,一天对应T=96或T=24个时段。记第t个时段的原始负荷为 $P_{\rm load}(t)$,储能净功率为 $P_b(t)$,放电为正、充电为负,那么储能平抑后的系统净负荷是:

$$ P_{\rm net}(t) = P_{\rm load}(t) + P_b(t) $$

调峰有两个常用目标。第一个是“削峰填谷”,直接最小化净负荷峰值:

$$ \min_{P_b, y} \quad y $$

约束里加上 $P_{\rm net}(t) \le y$,这是一个标准的minmax问题。写成线性目标后,CVX可以直接处理,不需要额外工具。

第二个是“最小化购电成本”,更适合有分时电价的市场环境。目标函数为:

$$ \min \sum_{t=1}^{T} c(t) \cdot P_{\rm net}(t) $$

这里的 $c(t)$ 是时段购电价格。这个目标会自然地让储能去“低充高放”,但它不会主动约束峰值,所以实际工程中往往会再加上一个峰值约束或峰值惩罚项。两种目标没有绝对优劣,如果你要写调峰调频模型,第一个minmax目标更贴近“调峰”的本意,后文以它为主。

储能自身的约束必须完整。功率限值约束:

$$ -P_{\max} \le P_b(t) \le P_{\max} $$

SOC递推关系,先按理想储能处理效率为1:

$$ SOC(t) = SOC(t-1) - \frac{P_b(t) \cdot \Delta t}{E_{\rm rated}} $$

其中 $\Delta t$ 是单时段小时数,$E_{\rm rated}$ 是额定容量。再加上SOC上下限和周期末回位约束。到这里,调峰的模型框架已经成立。

2.2 调频目标:用AGC调节容量做线性收益

调频和调峰的时间尺度不一样。AGC指令是秒级/分钟级的,而优化模型的粒度是15分钟,所以不能在校验里模拟每一秒的调节过程。常见做法是在每个15分钟时段内,把调频能力抽象成两个变量:上调节容量 $R_{\rm up}(t)$ 和下调节容量 $R_{\rm down}(t)$,单位都是MW。

上调节容量表示该时段储能“还能多向外输出多少功率”,包括减少充电和转为放电。受到两个因素限制:一是功率可用容量,二是SOC剩余电量。写成线性约束就是:

$$ R_{\rm up}(t) \le P_{\max} - P_b(t) $$

$$ R_{\rm up}(t) \le \frac{(SOC(t)-SOC_{\min}) \cdot E_{\rm rated}}{\Delta t} $$

注意第一式里,如果当前正在充电,$P_b(t)$ 是负数,那么 $P_{\max} - P_b(t)$ 会大于 $P_{\max}$,表示“少充一点就能释放功率”,物理含义正确。下调节容量对称:

$$ R_{\rm down}(t) \le P_{\max} + P_b(t) $$

$$ R_{\rm down}(t) \le \frac{(SOC_{\max} - SOC(t)) \cdot E_{\rm rated}}{\Delta t} $$

有了这两个变量,调频收益可以写成:

$$ F_{\rm fr} = \sum_{t=1}^{T} \lambda_{\rm agc}(t) \cdot \left( R_{\rm up}(t) + R_{\rm down}(t) \right) \cdot \Delta t $$

$\lambda_{\rm agc}(t)$ 是AGC调节里程价格,单位是元/MWh。整个目标里它是线性项,所以调频收益不会破坏问题的凸性。

2.3 统一变量:为什么这个模型适合用CVX来写

把调峰、调频放在一起,决策变量是:

$$ x = \left[ P_b(1..T), SOC(1..T), y, R_{\rm up}(1..T), R_{\rm down}(1..T) \right] $$

目标函数就是:

$$ \min \quad y - F_{\rm fr} / \text{scale} $$

所有约束都是线性等式或不等式。这是一个线性规划问题。CVX的优势不在于能解线性规划本身,而在于建模语言接近数学表达式,不用自己把minmax转成矩阵。写出来的代码几乎能对着公式核对,这对调参和排错非常重要。

需要注意量纲一致。$y$ 是功率,$F_{\rm fr}$ 是能量(功率乘时间),两者直接相减会出问题。要么把调峰目标也换成费用,要么把调频收益除以一个基准值做归一化。归一化的具体做法在第4章专门讲,这里先记住:多目标必须统一量纲或统一尺度。

3. Matlab+CVX实现储能调峰调频模型的完整代码与参数

3.1 CVX安装与求解器选择

写代码前先确认环境。CVX 2.2对Matlab版本支持比较久,从R2016b到R2024a都能用,新版本Matlab下安装也顺畅。安装步骤很简单:解压到任意目录,比如D:\cvx,然后在Matlab命令行切到该目录,运行cvx_setup,看到版本信息和求解器列表就说明装好了。

CVX自带的免费求解器有Sedumi和SDPT3。小规模线性规划够用,但如果T取96甚至288,变量到几百个,SDPT3可能偏慢。正式做项目时,建议再装Mosek或Gurobi,用cvx_solver mosek切换。它们都支持线性规划、二阶锥和整数规划,商用许可按年付费,但学术界有免费授权。基础模型只用线性规划,免费求解器也能跑,所以先不要在这个步骤卡住。

3.2 基础调峰模型的最小可运行代码

下面这段代码可以直接复制到Matlab里运行,它实现了最基础的调峰目标:最小化净负荷峰值,不考虑调频。

% 储能调峰调频模型 - 基础调峰(最小化净负荷峰值) T = 96; % 一天96个时段,15分钟一个点 dt = 0.25; % 每个时段对应小时数 E_rated = 10; % 储能额定容量,MWh P_max = 2.5; % 充放电功率上限,MW SOC_0 = 0.5; % 初始SOC SOC_min = 0.1; % SOC下限 SOC_max = 0.9; % SOC上限 % 构造一个演示用负荷曲线,单位MW P_load = 10 + 5*sin((1:T)/T*2*pi) + randn(1,T)*0.5; cvx_begin quiet variables P_b(T) SOC(T) y minimize( y ) subject to SOC(1) == SOC_0 - P_b(1)*dt/E_rated; for t = 1:T -P_max <= P_b(t) <= P_max; % 功率限值 SOC_min <= SOC(t) <= SOC_max; % SOC限值 P_load(t) + P_b(t) <= y; % 最小化净负荷峰值 if t > 1 % SOC递推关系,理想储能效率为1 SOC(t) == SOC(t-1) - P_b(t)*dt/E_rated; end end SOC(T) == SOC_0; % 周期末回到初始SOC cvx_end % 画图对比 plot(1:T, P_load, 'b', 1:T, P_load+P_b, 'r') legend('原始负荷', '储能平抑后负荷') xlabel('时段'); ylabel('功率/MW')

这段代码的核心思路是:用辅助变量y代替“求最大值”这个非光滑目标,通过minimize(y)P_load(t)+P_b(t)<=y两条语句完成minmax转换。SOC(1)单独赋值是为了避免循环里引用SOC(0)越界。

这里假设效率为1。实际储能充电效率 $\eta_c$ 和放电效率 $\eta_d$ 都低于1,模型要改成充电功率 $P_{ch}$ 和放电功率 $P_{dis}$ 两个非负变量,SOC递推关系变成:

$$ SOC(t) = SOC(t-1) + \frac{\eta_c P_{ch}(t)\Delta t}{E_{\rm rated}} - \frac{P_{dis}(t)\Delta t}{\eta_d E_{\rm rated}} $$

这时必须加一个很小的充电/放电惩罚项,比如0.01*sum(P_ch+P_dis),否则模型可能同时充放电。原因是minmax目标本身不会惩罚“一边充一边放”的低效行为。

3.3 在代码里加入调频约束与目标

在基础调峰基础上,加入上/下调节容量变量,并把调频收益计入目标。为了让代码简洁,下面直接用充放电分离变量,效率按95%处理。

% 储能调峰调频模型 - 调峰+调频容量 eta_c = 0.95; % 充电效率 eta_d = 0.95; % 放电效率 lambda_agc = 80; % AGC里程价格,元/MWh scale_fr = P_max; % 归一化基准,避免量纲混乱 cvx_begin quiet variables P_ch(T) P_dis(T) SOC(T) y variables R_up(T) R_down(T) minimize( y - lambda_agc * sum(R_up + R_down) * dt / scale_fr + ... 0.01 * sum(P_ch + P_dis) / P_max ) subject to for t = 1:T 0 <= P_ch(t) <= P_max; 0 <= P_dis(t) <= P_max; if t == 1 SOC(1) == SOC_0 + (eta_c*P_ch(1) - P_dis(1)/eta_d)*dt/E_rated; else SOC(t) == SOC(t-1) + (eta_c*P_ch(t) - P_dis(t)/eta_d)*dt/E_rated; end SOC_min <= SOC(t) <= SOC_max; P_load(t) - P_ch(t) + P_dis(t) <= y; % 上调节容量约束 R_up(t) <= P_max - P_dis(t) + P_ch(t); R_up(t) <= (SOC(t) - SOC_min) * E_rated / dt; R_up(t) >= 0; % 下调节容量约束 R_down(t) <= P_max - P_ch(t) + P_dis(t); R_down(t) <= (SOC_max - SOC(t)) * E_rated / dt; R_down(t) >= 0; end SOC(T) == SOC_0; cvx_end % 查看结果 fprintf('优化后的净负荷峰值: %.2f MW\n', cvx_optval); fprintf('平均上调节容量: %.2f MW\n', mean(R_up)); fprintf('平均下调节容量: %.2f MW\n', mean(R_down));

这里的调频约束写法值得展开。R_up(t) <= P_max - P_dis(t) + P_ch(t)表达的是:当前放电越多,可继续上调节的空间越小;当前充电越多,取消充电释放的功率越大,上调节空间反而大。R_up(t) <= (SOC(t)-SOC_min)*E_rated/dt则把SOC限制转换成功率限制,因为SOC剩余电量除以剩余时间就是平均功率。两者同时成立,才真正表示“既有功率余量,也有电量余量”。

3.4 求解失败时的排查参数

CVX运行后如果出现Status: Failed或者Infeasible,优先检查三类问题。

第一,数值尺度。E_rated*dt/dt得到的功率量级可能和负荷功率差几十倍。比如E_rated=10MWh, dt=0.25,SOC相关约束里的(SOC-SOC_min)*E_rated/dt会到36MW,而P_max只有2.5MW,CVX可能出现数值告警但结果仍可用。解决方法是把功率单位统一成标幺值,或者把所有约束都用MW表示。

第二,初始SOC和终止SOC的可行性。如果SOC_0=0.1,SOC_min=0.1,且SOC(T)必须回到0.1,但P_b在整个周期内被要求大量充电,模型必然不可行。给SOC_0留出裕量,一般设0.5比较稳。

第三,求解器容忍度。默认误差是1e-4,有的模型会得到Solved/Inaccurate,这时可以设置cvx_precision high或者调整求解器参数。如果是用Gurobi,还可以在求解前执行cvx_solver_settings('OutputFlag', 1)查看详细迭代。

4. 调峰与调频协同优化的权重调节与分层策略

4.1 加权目标函数的系数标定

第3章代码里用了lambda_agc * sum(R_up+R_down) / scale_fr作为调频收益项,但lambda_agc取多少直接决定优化结果偏向调峰还是调频。如果凭感觉设,跑出来的SOC轨迹会很怪:收益系数设大,储能整天在SOC上下边界来回顶,调峰效果很差;设小,调频容量形同虚设。

我常用的标定方法是先做两次单目标求解。第一次只最小化y,得到基准峰值y0。第二次只最大化sum(R_up+R_down),得到基准调频容量fr0。然后构造归一化目标:

$$ \min \quad \frac{y}{y0} - \alpha \cdot \frac{\sum(R_{\rm up}+R_{\rm down})}{fr0} $$

其中 $\alpha$ 是一个无单位的权重,0到1之间。这样两个目标的量级都在1左右,$\alpha$ 的含义变成“愿意放弃多少调峰能力来换取调频容量”。代码实现很简单,在CVX里把目标写为:

cvx_begin quiet minimize( y/y0 - alpha * (sum(R_up+R_down)/fr0) ) ... cvx_end

实际标定alpha时,先取0、0.1、0.2、0.3、0.5、0.7、1.0跑一组,观察“峰值增加比例”和“调频容量提升比例”。当alpha从0.3升到0.5时,如果峰值只增加1%,调频收益却提高20%,说明这个区间非常划算;如果峰值直线上升而调频容量增长趋缓,后面再加大权重就没意义了。

4.2 分层优化的实现:先算SOC轨迹再切功率

加权法适合方案比选,但实际工程中,AGC系统往往不允许储能把SOC轨迹修改得过于自由,因为AGC下发指令时只关心当前可调容量。所以另一种常见做法是分层:

  1. 第一层跑调峰模型,得到参考SOC轨迹 $SOC_{\rm ref}(t)$;
  2. 第二层固定SOC轨迹,把调频容量作为约束,优化功率分配的细节;

这里有一个简化技巧:第二层不需要重新求解完整优化问题,而是根据调峰功率和SOC计算一个“可调频裕量”。伪代码如下:

% 假设P_b_opt和SOC_opt来自第一层调峰解 for t = 1:T R_up_avail(t) = min(P_max - P_b_opt(t), ... % 功率裕量 (SOC_opt(t) - SOC_min) * E_rated / dt); % 电量裕量 R_dn_avail(t) = min(P_max + P_b_opt(t), ... (SOC_max - SOC_opt(t)) * E_rated / dt); end

这种方法牺牲了一部分调频收益,因为SOC轨迹没有为调频留出中间位置,但胜在实现简单,而且符合调度员“调峰计划优先、调频裕量校验”的操作习惯。如果希望SOC轨迹同时兼顾调频,就在第一层目标里加入一个“SOC偏离中间值”的软约束,让储能平时尽量停在0.5附近,再预留快速调节空间。

4.3 两种策略的结果对比与适用场景

策略目标函数调峰效果调频容量适用场景
调峰优先最小化净负荷峰值峰值最低偏低,只靠自然余量配网侧削峰填谷、线损治理
调频优先最大化AGC调节里程收益峰值可能升高高,SOC被压到中间区间独立储能参与调频市场
归一化加权y/y0 - alpha*fr/fr0可调可调容量规划、投资测算
分层优化固定SOC轨迹再算调节裕量接近调峰最优已有调度计划、需AGC考核

从这张表能看出,没有“完美”的储能调峰调频模型,只有适合当前业务目标的取舍。写代码时我建议把单目标函数、加权目标、分层目标全部封装成三个脚本,共用参数文件和负荷数据文件,这样比写一个带开关的大函数更易维护。

5. 让储能调峰调频模型从能跑变成能用的三个验证技巧

5.1 用CVX的status和gap判断结果是否可信

跑完CVX后不要只看结果图。先用disp(cvx_status)查看求解状态。Solved是最理想的情况,说明求解器在默认容差内找到了最优解。如果你看到Solved/Inaccurate,说明目标值可能存在数个小数的误差,通常不影响工程判断,但如果SOC约束很紧,建议调高精度。

线性规划求解器在最优解处会输出对偶间隙,CVX默认不显示,但可以通过cvx_optvalcvx_slack间接检查约束是否打满。一个最简单的方法:把某个关键约束的右侧值改小1%,重新求解,如果目标值变化很小,说明该约束不是瓶颈;如果目标值剧烈变化,说明这个约束决定了整个方案的边界。在做储能容量规划时,这个技巧能帮你快速找到“卡脖子”的是功率还是容量。

5.2 功率与SOC曲线的一致性检查

做完优化,先画SOC曲线,再画功率曲线,逐时段核对符号关系。SOC下降的时段,P_dis必须大于零;SOC上升的时段,P_ch必须大于零。由于模型里加了充放电惩罚项,正常求解结果不会出现同段充电又放电,但如果惩罚系数设成0,就可能看到P_chP_dis同时大于零的情况。

检查代码:

% 检查同一时段是否同时充放电 bad_idx = find(P_ch > 0.01 & P_dis > 0.01); if ~isempty(bad_idx) warning('以下时段同时充放电: %s', num2str(bad_idx')); end

同时充放电虽然在某些市场规则下是真实存在的(比如储能同时报调峰和调频计划),但线性模型里没有引入这些市场细节时,同时充放电只会虚增成本,必须排查。

5.3 灵敏度分析的快速做法

调峰调频模型最终要给容量决策提供依据,所以E_rated和P_max不能只算一个点。写一个简单循环,把E_rated从2扫到20,步长2;P_max从0.5扫到5,步长0.5,跑同一个加权重构模型,记录每组参数下的净负荷峰值和调频容量。最后画一张二维热力图,X轴是E_rated,Y轴是P_max,颜色是综合评分,直接能看到“再增加容量已经无收益”的拐点。

这个扫描只需要几行代码,但结果比单独跑一次模型有说服力得多。具体做法是把第4章的加权求解封装成一个函数[y_peak, fr_total] = run_store_model(E_rated, P_max, alpha),然后在循环里调用。如果循环耗时较长,优先覆盖P_max的细分,因为调频能力对功率更敏感,E_rated的步长可以放宽到2MWh。记住一点:储能调峰调频模型的价值不在那一个最优解,而在多个方案之间的边际效果对比。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询