ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

共享储能参与工业用户日前经济调度的MATLAB+YALMIP建模与优化实现

共享储能参与工业用户日前经济调度的MATLAB+YALMIP建模与优化实现 今年在做园区级综合能源项目时接触到一个很有意思的框架多个工业用户不各自投建储能而是共同租赁一座共享储能电站以“容量租赁按充电量计费”的方式参与日前调度。配合MATLAB里面的YALMIP工具箱和求解器把这套机制写成优化代码后无论是做方案预演、论文复现还是帮企业做投资测算都非常顺手。这篇文章就从调度模型、数学建模、MATLAB代码实现到调参排错完整过一遍这种“共享储能电站参与工业用户日前优化经济调度”的做法。先说清楚这套代码能解决什么问题。传统的用户侧储能是“自己建、自己用”但工业用户往往面临几个现实障碍一是初始投资高一台中小型储能电站动辄上百万回收期长二是负荷曲线和储能容量未必匹配装了不一定吃满三是运维能力不足电池健康管理、安全消防这些都得额外投入。共享储能相当于把“自建资产”变成“购买服务”用户只需支付容量租赁费就能在需要时调用电站的充放电能力。电网侧也能从这种聚合调度中获益——多个用户的负荷曲线叠加后共享储能的利用率更高削峰填谷的效果也比单点分散配置更明显。因此这类问题的核心不是“储能怎么选址”而是“在分时电价机制下每个用户怎么安排自己的购电计划、怎么调用共享储能的充放电功率才能让总电费最低”。这篇内容在写作时我完全按照一个能在MATLAB里跑通、能出结果、能改参数的项目标准来组织。适合电力系统或电气工程方向的研究生参考复现也适合做综合能源规划、用户侧储能的工程师用来做方案评估。对有一定MATLAB基础、但还没系统接触过优化建模的朋友我会尽量把每一步的原理和代码逻辑都拆开讲清楚。1. 项目整体设计与数学模型搭建1.1 为什么用“共享储能日前调度”的组合框架要理解这套模型得先把“日前调度”放在整个运行时间轴上定位。日前调度指的是在每个运行日的前一天根据第二天的负荷预测、分时电价、储能状态等信息确定第二天每个时段的运行计划。它不同于实时调度——实时调度需要在分钟级或秒级响应日前调度则是一个基于预测的“前瞻性优化”时间尺度通常是15分钟或1小时一个时段优化周期覆盖完整的一天。对这个项目而言选择“日前优化”有两个直接好处一是工业用户的生产计划往往在前一天已经排定负荷曲线预测相对准确二是分时电价方案提前公布谷段、平段、峰段的价差是确定的这给了储能套利一个清晰的操作窗口。那共享储能在里面扮演什么角色呢可以把它理解成一个“由第三方运营的公共电池”多个工业用户共同租赁它的容量和充放电功率。模型里通常有两种租赁模式一种是容量租赁用户按kW或kWh支付固定的月租或日租费用获得一定比例的充放电能力使用权另一种是按充放电量计费用户每次调用储能按照实际充放电量支付服务费。实际项目中两种模式常常混合使用比如某个用户租赁了1MW/2MWh的容量份额同时每次充电或放电还需要缴纳每kWh约0.1元到0.2元的服务费。从数学建模角度这种混合计费方式会让目标函数同时包含固定项和变量项但YALMIP处理起来没有难度。为什么不是让每个用户单独建储能呢答案在容量利用率和投资门槛。一个典型的中型机械加工厂白天负荷高、夜间负荷低但深夜的谷电时段它可能根本不用电如果自建储能夜里给电池充完电后白天只能放一次或两次容量利用率有限。而另一个化工厂可能是连续生产、负荷平稳它自己的储能容量需求和机械厂错峰互补。共享储能把多个用户的负荷叠加起来后电池可以在不同时段服务不同用户整体周转次数和收益都更高。对于用户来说不需要一次性掏出几百万元做固定资产投资相当于把重资产变成了运营费用。这就是“共享”二字的经济学意义。1.2 目标函数与经济性变量的数学解析建模的第一步是把经济目标翻译成数学表达式。在这个项目中工业用户的目标是最小化当天的总费用包含以下几个组成部分第一是从电网购电的费用。在日前优化中用户向电网购买的电量按分时电价结算。设调度周期分为T个时段通常取96个时段每个时段15分钟第t个时段的电网购电功率为P_grid(t)对应时段的电价为price(t)则购电成本表示为 sum(P_grid(t) * price(t) * Δt)。这里的Δt是时段长度如果单位是小时15分钟对应0.25小时。第二是共享储能的租赁费用。容量租赁费可以建模为一个固定项即用户租用的储能容量C_rent乘以单位容量租赁价格。在日前优化中这个量一般是给定的不参与优化变量变化但仍然计入目标函数。如果采用的是按充放电量计费则需要增加一个变量项充电电量乘以充电服务费单价加上放电电量乘以放电服务费单价。第三是储能电池的循环退化成本。这一点很多初学者容易漏掉。电池每充放一次容量都会有一定衰减这种衰减在经济上应该折算成成本。常用的简化建模方式是设定一个单位充放电量的退化成本系数c_degrad单位是元/kWh然后把退化成本写成 sum(c_degrad * (P_ch(t) P_dis(t)) * Δt)。这样做虽然不是电化学级别的精确老化模型但对于日前经济调度来说已经足够而且代码实现非常简单。第四如果项目考虑需求响应或需量电费还可以把需量电费加入目标函数。不过这里为了保持项目的基础性可以先把这部分留着作为后续扩展方向。因此完整的日前经济调度目标函数可以写成minimize sum(P_grid(t) * price(t) * Δt) C_rent * rental_rate sum(c_degrad * (P_ch(t) P_dis(t)) * Δt) sum(service_fee_ch * P_ch(t) * Δt service_fee_dis * P_dis(t) * Δt)用YALMIP表达这个目标函数非常直接无非是把各个求和项用sum()函数叠加。要注意的是单位统一。如果电价单位是元/kWh功率单位是kW那么功率乘以Δt得到的是kWh再乘以电价就得到元。建议在代码开头做好单位换算避免后续结果出现量级错误。1.3 约束条件的物理含义与数学表达目标函数只是“想省钱”但省钱不能突破物理限制和合同限制。对共享储能参与工业用户调度这个问题约束条件按照对象可以分成三类用户的功率平衡约束、共享储能的运行约束、以及耦合二者的容量/功率分配约束。先说用户功率平衡约束。在任意时段t用户内部必须满足功率平衡关系从电网购电功率加上共享储能放电功率等于用户负荷加上共享储能充电功率。写成公式就是P_grid(t) P_dis(t) P_load(t) P_ch(t)其中P_load(t)是用户在第t时段的负荷预测值作为已知参数输入。这个约束是整个电气系统的“能量守恒定律”也是所有调度模型的基础。我见过不少初学者把负荷直接当成购电量来处理那就意味着没有储能参与调节模型就退化了。然后是共享储能的运行约束。这部分包含三个经典约束。第一个是充放电功率上下限0 ≤ P_ch(t) ≤ P_ch_max0 ≤ P_dis(t) ≤ P_dis_max。这个P_ch_max、P_dis_max由共享储能分配给该用户的功率上限决定通常在租赁合同中规定。第二个是充放电互斥约束同一个时段内不能同时充电和放电否则会造成能量在电池内部循环拉低效率。在连续线性规划中这个约束需要用额外引入的二元变量来表达即 P_ch(t) ≤ M * u(t)P_dis(t) ≤ M * (1 - u(t))其中u(t)是0-1变量,M是一个足够大的常数。第三个是SOC动态约束和容量约束SOC(t) SOC(t-1) (η_ch * P_ch(t) - P_dis(t) / η_dis) * Δt / C_rentSOC_min ≤ SOC(t) ≤ SOC_max其中η_ch是充电效率η_dis是放电效率C_rent是用户租赁的储能容量。这个递推式的物理含义是电池当前的电量等于上一时刻电量加上充电电量乘以效率减去放电电量除以效率。很多代码写出来结果不合理问题往往出在效率放的位置不对——充电是把电网的电存进电池有损耗所以存储进去的能量要打折扣放电是把电池的电送出来也有损耗所以电池实际消耗的电量要比用户得到的电量多。耦合约束解决的是“多个用户共享一座电站”的核心问题。每个用户在时段t的充放电功率之和不能超过共享储能电站的总功率限制每个用户的SOC容量之和不能超过共享储能电站的总容量。在单用户模型中这个耦合约束退化为简单的不等式但在多用户协同调度中这是最关键的全局约束。最后如果有变压器容量限制还可以加一个约束P_grid(t) ≤ P_trans_max。这在实际项目中很重要因为工业用户受制于接入变压器的容量不可能无限从电网取电而储能在一定程度上可以缓解变压器过载问题。2. MATLAB代码实现与算法选型2.1 建模工具选择为什么推荐YALMIP在MATLAB里做优化调度工具选择上有几条路一是直接用MATLAB自带的优化工具箱linprog、intlinprog写矩阵形式的约束条件二是用YALMIP这个建模工具箱把优化问题以更接近数学表达的方式写出来三是用商业求解器的MATLAB接口比如Gurobi、CPLEX的MATLAB API。对于这个项目我的建议是YALMIP Gurobi/CPLEX的组合如果本地没有商业求解器用MATLAB自带的intlinprog也可以跑小规模算例。为什么选YALMIP因为它把“建模”和“求解”分开了。你可以用sdpvar定义优化变量用constraint []语句一条条地把约束加进去最后调用optimize()函数求解。这种方式有几个实际好处第一代码可读性好和你前面推导的数学公式几乎一一对应后期改模型加约束特别方便第二支持混合整数规划能处理充放电互斥这类二元变量问题第三求解器可以随时切换同样一套模型今天用Gurobi明天换Cplex不需要改动模型描述部分只需要在optimize()里指定求解器参数就行。安装方面多说一句。YALMIP本身是纯MATLAB工具箱去官网下载后添加到路径即可不需要编译。Gurobi需要安装对应的MATLAB接口CPLEX的处理方式类似。如果只是想先跑通代码验证逻辑用MATLAB自带的intlinprog也完全够用只是大规模多场景算例的求解速度会慢一些。2.2 核心代码架构与参数配置讲解整个MATLAB脚本可以按功能分成五个模块按照执行顺序排列模块一数据与参数初始化。包括负荷数据load_data一个T维向量单位kW、分时电价priceT维向量单位元/kWh、以及各种常数储能容量、功率上限、效率、SOC边界、时段长度dt。建议把参数放在脚本开头集中定义方便批量修改。%% 参数初始化 T 96; % 调度时段数 dt 0.25; % 时段长度小时 load_data ...; % 用户负荷曲线1xT 向量单位kW price ...; % 分时电价1xT 向量单位元/kWh C_rent 1000; % 用户租赁的储能容量kWh P_ch_max 250; % 最大充电功率kW P_dis_max 250; % 最大放电功率kW eta_ch 0.95; % 充电效率 eta_dis 0.95; % 放电效率 SOC_min 0.1; % SOC下限 SOC_max 0.9; % SOC上限 SOC_init 0.2; % 初始SOC c_degrad 0.05; % 退化成本系数元/kWh rental_rate 0.6; % 容量租赁单价元/kWh/天模块二优化变量定义。用YALMIP的sdpvar定义连续变量用binvar定义0-1变量。充电功率、放电功率、购电功率、SOC都是连续变量充放电状态指示变量u是二元变量。P_ch sdpvar(1, T); % 充电功率变量 P_dis sdpvar(1, T); % 放电功率变量 P_grid sdpvar(1, T); % 电网购电功率变量 SOC sdpvar(1, T); % 荷电状态变量 u binvar(1, T); % 充放电状态指示变量这里有一个细节SOC变量其实可以由P_ch和P_dis通过递推公式唯一确定不一定要显式定义为独立变量。但显式定义SOC并把它放入约束中代码的可读性和后期调试便利性会好很多代价只是多几个变量求解器完全不在乎。我建议显式定义。模块三约束条件组装。在YALMIP里约束用一个数组变量加起来的写法非常简洁。注意初始状态要单独拎出来处理。Constraints []; % 功率平衡约束 Constraints [Constraints, P_grid P_dis load_data P_ch]; % 充放电功率上下限 Constraints [Constraints, 0 P_ch P_ch_max]; Constraints [Constraints, 0 P_dis P_dis_max]; % 充放电互斥约束 M 1000; % big-M系数 Constraints [Constraints, P_ch M * u]; Constraints [Constraints, P_dis M * (1 - u)]; % SOC动态约束 Constraints [Constraints, SOC(1) SOC_init (eta_ch * P_ch(1) - P_dis(1) / eta_dis) * dt / C_rent]; for t 2:T Constraints [Constraints, SOC(t) SOC(t-1) (eta_ch * P_ch(t) - P_dis(t) / eta_dis) * dt / C_rent]; end % SOC上下限约束 Constraints [Constraints, SOC_min SOC SOC_max]; % 变压器容量约束可选 % Constraints [Constraints, P_grid P_trans_max];模块四目标函数与求解。目标函数按照1.2节的表达式构建然后调用optimize函数。求解器方面可以指定使用Gurobi或intlinprog。Objective sum(P_grid .* price) * dt ... % 购电费用 rental_rate * C_rent ... % 容量租赁费 c_degrad * sum(P_ch P_dis) * dt; % 退化成本 ops sdpsettings(solver, gurobi, verbose, 1); result optimize(Constraints, Objective, ops);跑完以后检查result.problem是否为0。如果是0说明求解成功否则需要根据返回的problem标志查错误类型。这一步是调试的基础。模块五结果提取与可视化。优化完成后用value()函数取出每个变量的数值然后绘制负荷与购电功率曲线、储能SOC曲线、充放电功率曲线等。P_ch_opt value(P_ch); P_dis_opt value(P_dis); P_grid_opt value(P_grid); SOC_opt value(SOC); figure; subplot(3,1,1); plot((1:T)*dt, P_grid_opt, b, LineWidth, 1.5); hold on; plot((1:T)*dt, load_data, r--, LineWidth, 1.5); legend(购电功率, 原始负荷); ylabel(功率/kW); subplot(3,1,2); stairs((1:T)*dt, P_ch_opt, g); hold on; stairs((1:T)*dt, P_dis_opt, m); legend(充电功率, 放电功率); ylabel(功率/kW); subplot(3,1,3); stairs((1:T)*dt, SOC_opt, k, LineWidth, 1.5); ylabel(SOC); xlabel(时间/h);2.3 整数变量规模控制与求解速度优化共享储能调度的整数变量主要来自充放电互斥约束的0-1变量。在一个纯单用户、T96的算例中只有96个二元变量这个规模对任何求解器都是小菜一碟。但如果有十个用户、各自独立建模、再联合调度二元变量会到960个混合整数规划求解速度就会明显下降。这时候有几个实用技巧第一个技巧是减少不必要的整数变量。比如在某几个时段内如果电价序列已经可以明确判断出是“绝对充电”或“绝对放电”时段可以不引入互斥变量直接锁定充放电状态。但这个判断本身不一定可靠实际中更常用的是先不加互斥约束跑一次LP松弛看哪些时段出现了“既充电又放电”的违背现象只对这些时段增加互斥变量可以大幅降低整数变量数量。第二个技巧是合理设置求解器容忍度。Gurobi中有一个MIPGap参数默认是1e-4对于这类调度问题完全不需要这么严格。设为1e-2甚至1e-3求解时间往往能快几倍而目标函数值的差异小到可以忽略。做工程评估不是发表顶尖学术论文不需要那个0.01%的精度。第三个技巧是对连续变量做缩放处理。如果功率和电价的量级差异很大可能导致求解器数值精度问题。比如功率上千kW电价零点几元购电费用可能到几万元SOC又是0到1的小数这些变量之间的尺度差异会让单纯形法的基矩阵条件数变差。建议把功率单位改成MW或者把储能容量缩小到MWh量级让目标函数各项都在同一个数量级。3. 算例设计、结果分析与经济性评估3.1 算例数据构建负荷、电价与共享储能参数为了让代码达到“可复现”的标准我准备了一套典型的工业用户算例数据。这里用的是标准化处理过的模拟数据实际项目运行时应替换为真实的负荷采集数据和电网发布的分时电价。工业用户的负荷曲线设定为白天8点到20点处于生产高峰期负荷在800kW到1000kW之间波动夜间22点到次日6点负荷骤降至200kW左右早晚各有一个短暂的过渡段。这种形态在机械加工、电子制造、食品加工等行业都非常典型属于二班制或三班制的生产节拍。分时电价采用国内常见的一般工商业分时电价结构谷段23:00-7:00电价0.35元/kWh平段7:00-10:00、15:00-18:00、21:00-23:00电价0.70元/kWh峰段10:00-15:00、18:00-21:00电价1.20元/kWh。峰谷价差达到0.85元/kWh这意味着储能每完成一次“谷充峰放”循环每度电有约0.8元左右的毛套利空间扣除效率和退化成本后仍然有足够的经济动力。共享储能参数设置为用户租赁容量C_rent 1000kWh最大充放电功率P_ch_max P_dis_max 250kW这意味着电池从空充到满大约需要4小时属于典型的“2小时以上持续型”储能配置。充电效率和放电效率都取0.95SOC运行区间[0.1, 0.9]初始SOC取0.2。容量租赁费率按每天每kWh 0.6元计电池退化成本按每kWh循环0.05元计。把这些数据代入模型目标函数里购电成本和租赁费、退化成本就都有了明确的数值基础。顺便说一句初始SOC取0.2而不是0更接近实际运行状态——出于安全和寿命考虑储能不允许完全放空而且前一天的调度可能剩余一些电量。3.2 优化结果解读储能该在什么时候干活跑完优化后结果呈现的规律非常清晰符合物理直觉。储能会在凌晨谷电时段充电因为这时电价只有0.35元/kWh充满电的成本很低。然后如果SOC上限允许它会在上午第一个峰段放电一次在下午第二个峰段再放电一次前提是上午放完电后中午平段或下午平段的剩余时间内能补充一部分电量。具体来看优化出的充放电策略凌晨0点到4点之间充电功率保持在250kW左右持续充电约4小时将SOC从0.2充到接近0.9上午10点到12点放电功率250kW持续约2小时把SOC从0.9放到0.4左右中午平段电价较低时这段时间是否充电取决于电价和容量如果平段电价与谷段价差不足以覆盖退化成本可能选择不充电等到下午峰段直接放电到SOC下限。傍晚峰段再放一次。整个过程由求解器根据电价差自动决策目标函数值就是最小化的当日总费用。对比“不配置储能”的基准场景只看购电费用这一项配置共享储能后明显下降了因为部分高峰负荷由储能承担用户从电网购买的峰段电量减少转移到了谷段。把租赁费、退化成本都算进去后项目的日总费用仍然低于基准场景差额就是共享储能带来的净收益。以这套算例的规模日净收益大约在几百元量级一个月的收益足以覆盖容量租赁费。当然具体数值会随用户负荷曲线、电价差和租赁费率变动但模型给了你一个精确计算每一项收益和成本的工具。3.3 多用户协同场景下的结果变化单用户模型跑通后可以把它自然扩展成多用户协同版本。核心变化在于每个用户有自己独立的负荷曲线和租赁容量份额但共享储能电站的总运行状态SOC、总充放电功率是所有用户行为的叠加结果。这种情况下需要将SOC变量、充放电功率变量按用户维度扩展成矩阵同时在目标函数中汇总所有用户的购电费用、租赁费用和退化成本。多用户协同带来的一个直接收益是容量互补假设用户A是白天高负荷、夜间低负荷用户B是夜间高负荷比如连续生产的化工厂那么在用户A的夜间低谷期用户B可以充分利用共享储能容量充电整体储能的日循环次数就提高了。这在单用户自建储能模式下是不可能实现的——用户A根本用不上夜间的储能容量。模型跑出来的结果会显示协同调度的总费用比每个用户单独租用同等容量再独立调度更低多出来的收益就是共享的价值所在。如果想继续加深这个项目还可以把《关于加快推动新型储能发展的指导意见》里提到的“共享储能商业模式”作为背景在论文或报告中讨论容量租赁定价机制、跨用户利益分配策略比如用Shapley值法分摊总收益这部分可以作为后续扩展的研究内容。但对代码本身来说先把单用户和多用户调度跑通是第一步。4. 常见问题、调试技巧与调参避坑指南4.1 YALMIP求解器无法找到的排查方法我第一次跑这类模型时遇到了一个非常典型的报错YALMIP提示“No suitable solver for the problem type”或者说找不到求解器。原因是YALMIP根据问题的数学结构自动选择求解器如果模型中包含二元变量就必须有支持混合整数线性规划的求解器。如果MATLAB环境里只有linprog线性规划求解器没有intlinprog的调用配置或者没有安装Gurobi/CPLEX接口就会报这个错。排查方法是三步走第一步检查是否安装了合适的求解器。在MATLAB命令窗口运行yalmiptest它会检测所有可用的求解器并列出白名单。第二步确认问题类型。在代码里显式指定求解器比如ops sdpsettings(solver,gurobi)或者ops sdpsettings(solver,intlinprog)不要依赖YALMIP自动选择。第三步如果确认安装了Gurobi但仍然报错大概率是MATLAB路径没有包含gurobi的mex接口文件去Gurobi安装目录下运行Gurobi的MATLAB setup脚本即可。4.2 求解结果中充电和放电同时出现的处理办法在加了互斥约束后理论上不应该出现“同一时段既充电又放电”的结果。但如果big-M系数取值不当或者求解器的整数变量容忍度设置过松偶尔会出现非常微小功率的同时充放现象比如充电功率0.001kW、放电功率0.002kW同时存在。这在数值上虽然不违反硬约束因为M足够大但在物理上不合理也会造成目标函数虚高。处理办法是在代码中用一个小阈值过滤对优化结果里的P_ch和P_dis小于比如0.1kW的数值直接赋零。更根本的办法是检查big-M的取值。M不能太小——如果M小于某个时段可能出现的最大充电功率就会限制可行域M也不能太大——太大会导致数值稳定性变差。一般经验是取最大功率的2到5倍即可我用的是1000对这个算例已经足够。另外要留意SOC递推约束的数值误差。因为充电效率和放电效率的存在SOC的递推公式里每一步都会引入小数误差96个时段累加下来末时刻的SOC和用公式手算的结果可能有微小偏差。这种偏差在可接受范围内但如果你的场景对SOC末端值有硬性要求比如调度周期结束SOC必须回到初始值以应对第二天运行就需要额外加一个末端SOC约束SOC(T) SOC_init加了这个约束相当于要求储能当天“从哪里来、回哪里去”不会把电量耗干留给第二天。这个约束在工程上很重要尤其是连续多天滚动调度时否则第二天的初始SOC就成了不确定量。4.3 参数敏感性分析与调参建议最后分享一个调参经验共享储能项目的经济性高度依赖于三个参数做敏感性分析时优先盯这三个。第一个是峰谷电价差。这是储能套利的经济基础。如果峰谷价差低于0.5元/kWh扣掉效率和退化成本后套利空间会非常有限。模型里可以写一个循环让峰段电价从1.0元/kWh逐步变化到1.5元/kWh观察最优调度策略和总费用的变化趋势。第二个是容量租赁费率。租赁费率本质上是共享储能运营商的定价策略用户做决策时可以对不同租赁费率计算自己的最优费用找到“租比自己建更划算”的费率区间。第三个是储能效率。η_ch和η_dis从0.9提升到0.95看起来只是5个百分点的变化但经过“充电一次、放电一次”的双重折算有效吞吐量提升了接近10%对目标函数的边际影响非常可观。我做这类项目时的一般工作流程是先用一组基准参数跑通模型、确认结果符合物理直觉然后做单参数敏感性分析画出曲线或表格最后挑两到三组典型参数组合做对比场景。这种分析报告拿给企业决策层看比单纯给一个最优解要直观得多也更容易说明共享储能的商业模式可行性。我在实际测试中还有一个体会这个模型最好从单用户版本起步确认代码没问题后再扩展多用户。单用户版本的调试速度快物理直觉清晰出错了能很快定位是在约束写错、参数单位不对还是求解器配置问题。多用户版本一旦跑起来各种跨用户耦合的约束会让问题边界模糊排查难度成倍上升。如果能在单用户阶段养好“计算前先手推一遍预期结果”的习惯后面的路会顺很多。
返回列表