ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于Matlab+Yalmip的分布式储能容量优化配置方法

2026/9/12 3:10:34 拓冰建站 浏览量
基于Matlab+Yalmip的分布式储能容量优化配置方法 最近在帮几家企业做分布式储能的前期测算大家问得最多的一个问题出奇一致光伏和负荷曲线都在手里储能到底该配多大配小了削峰填谷不明显收益掰扯不明白配大了投资回收期越拉越长老板看着成本表直皱眉。这个问题放到专业语境里就是“考虑产销者的分布式储能容量配置策略”而且大多数情况下我们都是直接在 Matlab 里完成数据整理、优化建模、求解和结果可视化。这篇文章就把我实际做测算时用的思路和代码框架完整摊开包括目标函数怎么建、约束怎么写、求解器怎么选以及哪些地方最容易踩坑。无论你是做园区光储项目方案的工程师还是在学校研究分布式储能规划的研究生这套方法都能直接拿去过一遍先用典型日数据跑通模型再根据实际场景慢慢加约束、加敏感性分析。不用一上来就追求完美先把最核心的逻辑表达清楚后面扩展才不会乱。1. 配储容量策略的核心逻辑这是一个“先想清楚收益再决定装多大”的问题1.1 产销者到底指什么为什么储能决策会变复杂“产销者”prosumer这个词是由生产者和消费者组合出来的概念。放在电力系统里最典型的就是那些装了屋顶光伏的工商业用户白天光伏出力超过负荷余电上网晚上负荷高又得从电网买电。同一个主体在不同时间点分别扮演“发电方”和“用电方”所以叫产销者。为什么这个身份会改变储能配置的思路因为储能的价值不再只是单纯的消纳光伏而是同时作用在两条线上一条是减少从电网买电另一条是提高自发自用比例甚至参与电力市场交易。光伏大发时段你可以把多余电量存下来等电价高或者负荷大的时段再放出去相当于人为地平移了一部分电能。如果只把储能当成“电池”来配不考虑产销者自身负荷曲线与光伏曲线的匹配程度配出来的结果往往要么偏大要么偏小。在实际工程项目里我们还要面对峰谷电价、上网电价、变压器容量限制、并网要求、储能循环寿命等多重因素。容量配置策略表面上看是算一个“多少千瓦、多少千瓦时”实际上是在回答一系列问题储能在什么时刻充放电、每天循环多少次、一年下来能替代多少高价电、又能通过售电回收多少成本。这些运行层的细节反过来说明了为什么需要用一个优化模型来做容量配置而不是凭经验拍脑袋。1.2 表面是容量决策背后是运行策略耦合如果直接问“储能配多大”很多人第一反应是算一个公式光伏装机乘一个比例或者按负荷峰值乘一个比例。但这么做往往吃亏。因为储能容量和运行策略是强耦合的——同样的 100 kWh 电池如果运行策略合理一年能帮你转移的电量可能很差很多。举例来说一块 50 kW / 100 kWh 的储能如果每天固定晚上充满、早上放空收益主要来自峰谷价差但如果负荷高峰出现在中午后两点光伏又在这个时段多发适合的调度策略就应该是“午间充电、下午放电”而不是机械地按照“充一放一”的常规套路。而这些运行决策又直接决定了储能最终能创造多少价值进而影响你觉得“配多大容量划算”。所以正规的做法是把容量配置建模成一个嵌套的优化问题外层确定额定功率和额定容量内层在给定的容量下对每个典型日做充放电策略优化算出最优运行收益。把内层收益带回外层再比较不同容量组合下的总成本。用数学规划的语言来说这其实就是一个两层优化问题不过在大多数工程简化模型里可以把它合并成一个包含容量变量和调度变量的大规模数学规划一次性求解。1.3 为什么选择 Matlab 来做这件事这几年 Python 在数据处理和深度学习领域很火但电力系统优化这块Matlab 的生态和积累依然很深。Yalmip 这个建模工具箱尤其好用它让优化模型的表达方式非常接近数学公式本身不需要把问题手动改写成矩阵形式。同时 Matlab 自带的数据处理、绘图、报表输出能力也能让项目交付更顺畅。我自己跑这种容量配置优化时基本流程是用 Excel 或 Python 把光伏和负荷数据处理成典型日序列导入 Matlab用 Yalmip 搭建优化模型调用求解器求解再写几个绘图脚本把 SOC、充放电功率、购售电曲线画出来。整个过程思路清晰也方便后续在模型里加储能寿命衰减、需求响应价格等更复杂的模块。2. 数学模型到底怎么写目标函数、约束条件和求解器选型2.1 目标函数把全生命周期成本压缩到一年储能容量配置的优化目标一般习惯写成“年化总成本最小化”。这样把投资成本、运行成本和收益统一到同一个时间尺度上比较起来最直观。年化总成本大体由四块组成储能的投资年值额定功率乘以单位功率成本加上额定容量乘以单位容量成本然后乘以等额年值系数年运维成本通常和容量或者循环电量挂钩年购电费用从电网买电的电费支出年售电收益余电上网或者参与现货市场带来的收入作为负成本计入目标函数。如果用数学公式表达就是min F CRF x (Cp x P Ce x E) Com x E Σ weight(k) x Σ [ρbuy(h) x Pbuy(h,k) - ρsell(h) x Psell(h,k)]其中CRF 是等额年值系数计算方式是 r(1r)^n / ((1r)^n - 1)r 是折现率n 是储能寿命。Cp 是单位功率投资成本Ce 是单位容量投资成本P 和 E 分别对应额定功率和额定容量。Com 是单位容量的年运维成本。后面那个求和式则是在所有典型日上累加购电支出、减去售电收入weight(k) 表示第 k 个典型日在一年中出现的天数。这里要注意的是目标函数看起来只是在算经济账但通过运行变量 Pbuy 和 Psell 的优化储能的自发自用行为、峰谷套利行为都会被自然而然地带出来。只要购电电价高于售电电价模型就倾向于先让光伏满足本地负荷而不是主动买电再卖电做无意义的循环。2.2 约束条件功率平衡、SOC边界和首末端一致性目标函数建好以后还要给模型加上一组物理和运行约束否则求解器可能会给出“理论上不可能实现”的方案。最基本的包括以下几类功率平衡约束任一时刻光伏出力加上放电功率加上购电功率必须等于负荷加上充电功率加上售电功率。这个约束的本质是能量守恒是整个模型的中枢。充放电功率限制充电功率和放电功率都不能超过储能额定功率并且都是非负的。这里也会引申出一个工程细节要不要加“不能同时充电和放电”的约束。理论上由于充放电都有损耗模型在正常电价下不会选择同时充放但如果遇到某些极端价格波动或者求解器数值精度不足可能会出现微小同时充放的现象。工程上想彻底避免就得引入二进制变量来互斥但这样会把线性规划升成混合整数线性规划求解难度有所上升。SOC 递推约束下一时刻的电池电量等于当前电量加上本时段净充电量。如果时间步长是 1 小时那么每步储能电量变化就是充电功率乘以充电效率减去放电功率除以放电效率。SOC 上下限约束为了延长电池寿命一般不允许电量随便放到 0 或充到 100%工程上常用 10% 到 90% 作为边界。上下限都写成额定容量乘以一个系数的形式这样容量变量和电量变量就被联立在一起。首末端 SOC 一致性约束为了模拟“每一天都是独立运行周期”通常会要求储能每天的初始电量和日末电量相同。这个约束让模型不能“吃老本”——不能第一天把电池放空、第二天又靠初始电量占便宜必须保持可持续的日循环。2.3 求解器选型MILP 是常规解法启发式算法要慎用上面这套模型如果所有变量都是连续的本质上就是一个线性规划问题一旦加入“防止同时充放电”的二进制变量就变成了混合整数线性规划。对于 MILP工程上最常用的求解器是 Gurobi 和 CPLEX两者性能都很强而且高校都有学术授权渠道。如果没有商业求解器SCIP、CBC 这类开源求解器也能跑只是大规模问题的求解速度会慢不少。我在实际测算中通常直接用 Yalmip 建模求解器设置为 Gurobi。原因是 Yalmip 的语法几乎和数学公式一一对应写起来不费劲后面想切换求解器也只需改一行配置。至于遗传算法、粒子群这类启发式算法也不是不能用但它们在处理 SOC 递推这样带强耦合时序约束的问题时经常陷入局部最优而且每次运行结果可能不一样。所以在能做数学规划求解的情况下优先用数学规划把启发式算法留给那些非线性太强、无法线性化的扩展问题。3. Matlab 代码实现一套可以直接改着用的基础框架3.1 先做数据整理再写模型顺序千万别反过来很多新手一上来就打开 Matlab 写优化模型结果写到最后发现数据格式不对回头改代码改到怀疑人生。我的习惯是先把数据处理成“典型日矩阵”的格式每一列是一个典型日每一行是一天 24 个小时光伏和负荷各生成一个 HxK 矩阵再单独定义一个权重向量用来表示每个典型日包含的天数。典型日怎么来最简单的做法是按季节划分比如春夏秋冬各取一个代表日。更精细一点的方法是用 k-means 算法对全年 8760 小时的光伏-负荷组合做聚类选出 4 到 12 个典型日。聚类的好处是能保留全年不同天气类型和负荷模式的差异又不至于让优化模型规模爆炸。我自己做可研的时候通常先用 4 个典型日跑出初步结果再用 12 个典型日验证结果的稳定性如果两者差异不大说明这个配置策略对数据细节不敏感可信度比较高。3.2 核心代码注释与关键模块解析下面这段代码是整套容量配置模型最核心的部分采用 Yalmip 建模调用 MILP 求解器。实际使用中需要把 PV 和 Load 替换成你自己的数据。%% 分布式储能容量配置基础框架Yalmip MILP % 需要预装Yalmip工具箱以及可用的MILP求解器Gurobi/CPLEX/SCIP等 % 输入矩阵说明 % PV(H,K) 每个典型日各小时的光伏出力单位 kW % Load(H,K) 每个典型日各小时的负荷单位 kW % weight(K) 每个典型日在一年中代表的天数例如 90 90 90 95 % 输出P_opt, E_opt 分别为最优额定功率和额定容量 clear; close all; clc; dt 1; % 时间步长单位小时 H 24; K 4; % 24小时4个典型日 PV rand(H,K) * 50; % 示例数据实际请替换为聚类后的光伏曲线 Load rand(H,K) * 80; % 示例数据实际请替换为聚类后的负荷曲线 weight [90 90 90 95]; % 每个典型日对应的天数 % 成本与效率参数单位均为元、kW、kWh Cp 1200; % 储能功率成本元/kW Ce 800; % 储能容量成本元/kWh r 0.06; % 折现率 n 10; % 储能寿命年 CRF r * (1 r)^n / ((1 r)^n - 1); Com 0.02 * Ce / 10; % 年运维成本按容量折算 eta_ch 0.92; % 充电效率 eta_dis 0.92; % 放电效率 % 分时电价单位元/kWh这里先给一个简单示例 rho_buy ones(H,1) * 0.6; % 购电价格 rho_sell ones(H,1) * 0.35; % 售电价格 % 决策变量定义 P sdpvar(1,1); % 储能额定功率kW E sdpvar(1,1); % 储能额定容量kWh Pch sdpvar(H,K); % 充电功率 Pdis sdpvar(H,K); % 放电功率 Ebat sdpvar(H1,K); % 电池电量状态kWh首行对应初始SOC Pbuy sdpvar(H,K); % 购电功率 Psell sdpvar(H,K); % 售电功率 Constraints []; Objective 0; for k 1:K for h 1:H % 功率平衡光伏放电购电 负荷充电售电 Constraints [Constraints, PV(h,k) Pdis(h,k) Pbuy(h,k) ... Load(h,k) Pch(h,k) Psell(h,k)]; % 充放电功率限制 Constraints [Constraints, 0 Pch(h,k) P]; Constraints [Constraints, 0 Pdis(h,k) P]; Constraints [Constraints, Pbuy(h,k) 0]; Constraints [Constraints, Psell(h,k) 0]; end for h 1:H % SOC递推假设自放电率忽略不计 Constraints [Constraints, Ebat(h1,k) Ebat(h,k) ... (eta_ch * Pch(h,k) - Pdis(h,k) / eta_dis) * dt]; end % SOC上下限约束取额定容量的10%到90% Constraints [Constraints, 0.1 * E Ebat(:,k) 0.9 * E]; % 首末端SOC一致这里取初始SOC为额定容量的20% Constraints [Constraints, Ebat(1,k) 0.2 * E]; Constraints [Constraints, Ebat(H1,k) 0.2 * E]; % 累积运行成本购电费用减去售电收入 Objective Objective weight(k) * ... sum(rho_buy .* Pbuy(:,k) - rho_sell .* Psell(:,k)); end % 加上投资年值和运维成本 Objective Objective CRF * (Cp * P Ce * E) Com * E; % 求解设置 ops sdpsettings(solver, gurobi, verbose, 2); sol optimize(Constraints, Objective, ops); % 结果输出 if sol.problem 0 P_opt value(P); E_opt value(E); fprintf(最优配置结果P%.2f kW, E%.2f kWh\n, P_opt, E_opt); else disp(求解失败); disp(sol.info); end3.3 结果可视化SOC曲线和充放电功率曲线怎么画求解完成之后只给一个功率和容量数字说服力是不够的。做项目汇报时我一般会同时输出两类图一类是典型日的储能充放电功率曲线另一类是 SOC 曲线。这两张图能直观说明储能一天是怎么跑的也能用来验证模型结果是否符合物理直觉。比如如果 SOC 曲线突然出现不正常的跳变多半是某个约束写错了或者单位没对齐。下面这段代码画第一个典型日的充放电功率曲线和 SOC 曲线k_plot 1; % 想查看第几个典型日 t 1:H; figure; subplot(2,1,1); plot(t, value(Pch(:,k_plot)), LineWidth, 1.5); hold on; plot(t, value(Pdis(:,k_plot)), LineWidth, 1.5); legend(充电功率,放电功率); xlabel(时刻/h); ylabel(功率/kW); title(典型日充放电功率); subplot(2,1,2); plot(0:H, value(Ebat(:,k_plot)) / max(E_opt,1e-6) * 100, LineWidth, 1.5); xlabel(时刻/h); ylabel(SOC/%); title(典型日SOC变化曲线);画图之后我还会把目标函数里的各项成本拆出来打印一遍比如年购电成本、售电收入、投资年值、运维成本分别看看哪一项对最终结果影响最大。这远比只看一个总成本数字更有用。3.4 从单用户配置扩展到多产消者群体单个产销者的模型跑通之后下一步经常要做的是“共享储能”或“社区储能”的场景。这时多个产销者共用一个储能系统决策变量就多了两层第一层是储能总容量和总功率第二层是每个产销者对储能的充放电权利分配。建模时目标函数要改为整个群体总年化成本最小同时还要加入公共变压器容量约束、各用户单独计量约束以及利益分配机制。在这个扩展场景里我会把上述代码做成一个子函数每次传入某类用户的负荷和光伏曲线以及分配到的储能份额单独算出该用户的运行收益外层再用一个循环或另一个优化层去搜索总容量和分配比例。虽然计算时间会成倍增加但换来的结论对园区级项目非常关键与其给每栋楼各配一套储能不如集中建一套大的共享储能利用不同用户的峰谷错位效应相互错峰整体容量能下降不少。下表是我在某个园区项目里的测算对比仅作为思路展示不代表任何具体报价场景额定功率额定容量年化总成本相对独立配置成本每个用户独立配置合计 185 kW合计 430 kWh46.8 万元基准共享储能优化配置120 kW280 kWh35.2 万元下降约 25%共享储能但固定比例分配130 kW320 kWh38.6 万元下降约 17%4. 案例测算与结果解读4.1 参数设置与基础数据为了讲清楚结果怎么读我在这里放一个典型的工商业产销者案例。某工厂屋顶铺设了 100 kWp 光伏全年发电量大约 11 万 kWh工厂年用电量约 32 万 kWh。园区峰谷电价差距约 0.7 元/kWh具体分为峰时 1.0 元、平时 0.6 元、谷时 0.3 元。上网电价按 0.35 元/kWh 计。储能投资成本按当前行业常见水平估算单位功率成本 1200 元/kW单位容量成本 800 元/kWh。折现率取 6%寿命按 10 年计算充放电效率均为 92%。先做一下年化系数计算CRF 0.06 x 1.06^10 / (1.06^10 - 1)算下来大约是 0.1359。也就是说如果容量投资是 100 万元折算到每年约 13.59 万元。这个换算能帮我们把一次性投资放到年度经营现金流里比较是项目测算非常关键的一步。4.2 不同配置方案的经济性对比为了说明优化配置的差别我在同一个数据基础上对比了四个方案无储能、按固定比例简单配置、优化模型推荐配置、以及过度配置。结果都是示意性数据重点看趋势。场景额定功率额定容量年投资运维成本年电费净支出年化总成本无储能--0 万元18.6 万元18.6 万元固定 30 kW / 60 kWh30 kW60 kWh2.1 万元14.7 万元16.8 万元优化配置 40 kW / 110 kWh40 kW110 kWh3.8 万元12.4 万元16.2 万元过度配置 80 kW / 200 kWh80 kW200 kWh6.9 万元11.9 万元18.8 万元从这张表能很清楚地看到储能容量不是越大越好。过度配置虽然把电费净支出降低到 11.9 万元但投资和运维成本上升得更快反而导致年化总成本比无储能还高。优化配置方案把年化总成本压到 16.2 万元比固定配置又省了 0.6 万元左右。这就是做容量优化配置的价值——它解决的不是“能不能省钱”的问题而是“在什么位置省钱最划算”的问题。4.3 结果怎么读功率比、临界价差和灵敏度拿到最优结果 P40 kW、E110 kWh 之后不要只报数字。我一般会再往下挖三个维度第一是功率容量比。这个例子中 P/E 大约是 0.36也就是说储能时长接近 2.75 小时属于目前工商业储能常见的“2到4小时”区间。如果结果出现 0.1 以下的功率容量比通常意味着储能只在极少数时段放电收益更多来自容量电价而非峰谷套利如果超过 1那更像功率型储能收益逻辑完全不同。第二是临界峰谷价差。把峰谷价差从 0.7 元往下扫看到某个价差条件下最优容量突然归零那个点就是储能投资的临界价差。这个参数放在项目谈判里非常关键因为很多客户会问“如果电价政策调整了这套配置还划不划算”。第三是灵敏度分析。我通常会分别调整光伏装机、负荷峰值、电池成本、折现率四个输入参数上下浮动 20%重算最优容量。如果某个参数变化一点最优容量就剧烈波动说明这个项目对那个参数高度敏感交付时就要重点提示风险。5. 常见坑位与排查心得5.1 求解失败与不可行问题用 Yalmip 求解时最常遇到的问题是模型不可行也就是sol.problem不等于 0。常见原因有三个。第一个是 SOC 边界约束和首末端一致性约束互相冲突。如果额定容量 E 被优化器试探到一个特别小的值比如接近于 0那么Ebat(1)0.2E和Ebat(H1)0.2E会让电池电量几乎全程被压在极低水平SOC 上下限又要求电量在 0.1E 到 0.9E 之间两者很容易形成矛盾。解决办法是给 E 加一个合理下限比如至少 10 kWh或者把初始 SOC 约束放宽为Ebat(1,k) 0.2E这样的不等式让模型有更多自由度。第二个原因是数据单位不统一。光伏可能给的是 MW负荷给的是 kW功率平衡方程两边差了 1000 倍模型自然无解或者给出离谱结果。我在跑数据之前一定会统一单位全部转成 kW 和 kWh再检查一遍典型日的负荷峰值和光伏峰值是否在合理范围。第三个原因是时间步长和容量单位混用。如果原始数据是 15 分钟一个采样点那么 SOC 递推里的 dt 就是 0.25 小时而不是 1。漏改 dt 会导致模型以为 15 分钟跑完了一小时的电量结果 SOC 频繁触界最终配置结果失真。5.2 Yalmip 跟求解器环境配合的坑Yalmip 本身只是一个建模接口不带求解器。如果没装 Gurobi 或 CPLEX它默认会去找 Matlab 自带的线性规划求解器或者用一些基础的开源求解器跑小型问题可能没问题但一旦变量数量上去速度会慢到怀疑人生。我在配置环境时一般会按下面这个顺序处理先安装 Yalmip然后把求解器安装目录加入 Matlab 路径。Gurobi 的学术许可以学校邮箱申请CPLEX 也有类似渠道。如果暂时申请不到商业求解器可以先用 SCIP 或者 CBC它们能处理 MILP只是大规模场景下性能差距比较明显。另外提醒一句Matlab 版本太老可能和新版 Gurobi 不兼容建议在装求解器之前先看一下官方兼容性列表。5.3 典型日数据不足导致配置结果偏激进用 4 个典型日跑优化很多时候结果会偏激进因为典型日代表的是“平均状态”或者“聚类中心状态”那些极端连续高温天、阴雨天、节假日低负荷天都被平均掉了。储能容量在这种模型里往往偏低因为它每天都能按照理想模式充放一次实际运行中却不一定有这么多机会。所以我一般至少会用两种方式做校验。第一种是增加典型日数量比如从 4 个增加到 12 个比较结果是否稳定第二种是取全年 8760 小时数据直接做一次运行模拟固定容量后评估全年逐时 SOC 变化和收益情况这一步不是优化而是仿真验证。如果仿真年收益和优化模型估算的收益差超过 10%说明典型日选取不够合理需要重新聚类。5.4 从测算到落地的额外建议模型和代码再完善也只是把物理世界简化成了数学问题。到了真正落地阶段还要额外考虑几个模型里没体现的东西。比如电池循环寿命衰减如果每天满充满放一次电芯衰减速度会比浅充浅放快很多这会影响全生命周期的收益估算。更精细的做法是用雨流计数法统计放电深度分布再折算等效循环次数不过在容量配置阶段我更建议先用一个简单的等效循环折算系数去判断结果是否稳健不必一上来就上复杂模型。再比如动态电价行情。现在很多地方现货市场价格波动越来越大固定峰谷电价假设已经开始失真。如果项目所在地区有现货市场建议在模型中引入几个典型电价场景把容量配置结果对电价场景的敏感性列出来否则算出来的“最优”很可能只是特定历史电价下的最优。最后说两句自己的体会模型第一次跑通的时候我也一度觉得“就这么简单”。但后来拿结果跟实际项目对比发现差距往往不在求解器精度而在输入数据的质量和对运行约束的理解。光伏预测偏差、负荷波动、电价变化任何一个环节数据没处理好优化结果都会偏差很大。所以我现在的习惯是先用一个最简单的基础模型把代码链路跑通再逐步加约束、加场景每次只改动一个变量确保每一步结果都可解释。这个方法放到储能容量配置里很管用放到其他类似的工程优化问题里也是一样。希望这套框架能帮你少走点弯路。