ARTICLE DETAIL

资讯详情

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

光储充换电站调度优化:基于电价互动的Matlab建模与实现

光储充换电站调度优化:基于电价互动的Matlab建模与实现 最近在做光储充换电站的调度优化项目核心是把光伏、储能、充电桩、换电站的设备状态和用户的充电行为放在同一个优化框架里。这个课题最有意思的地方在于“互动”二字——用户的充电负荷不是一个给定的常数曲线而是会随着电价波动发生转移和响应。这样一来最优分时电价不再是前台给定的输入而是优化变量的一部分。我用Matlab完整复现了这套模型把代码从零调到可以稳定出结果过程中踩了不少坑这里把整个思路和实操细节整理出来希望对正在研究充换电站经济调度、需求响应或者Matlab能量管理优化的同学有直接帮助。先说明一下模型的适用范围园区级光储充换电站屋顶有分布式光伏站内配了固定式储能充电区有若干直流快充桩另外还有一个给电动两轮车或换电重卡准备的换电设备。调度周期一般取一天步长1小时如果数据充足也可以缩短到15分钟。目标是在满足用户充电需求的前提下通过优化分时电价和储能充放电策略让整个电站的日运行收益最大化。后面所有公式和代码都以这个场景为基础如果你们做的是虚拟电厂、车网互动或者工业园区综合能源优化这套思路同样可以迁移。1. 模型解决的问题为什么“固定负荷固定电价”会算错账1.1 传统充换电站调度模型的明显盲区多数论文里第一版模型或者很多课程设计里的示范代码会把充电负荷当成一条固定的时序曲线甚至直接把分时电价设成固定的峰平谷电价然后只优化储能什么时候充、什么时候放。这么做的结果是储能倾向于谷段充电、峰段放电光伏大发时段把能量存起来表面看起来收益不错但实际上用户的充电行为是死的。真实场景里用户看到下午2点光伏大发时段的电价很低就会把原本晚上7点的充电需求挪到下午。这种转移行为直接影响两条收益线一是充电服务费对应的时间分布变了二是光伏就地消纳的比例提高了。如果模型里没有这一步算出来的最优电价就只是“自以为最优”的电价实际执行起来会差很多。我曾经拿同一组光伏和储能参数分别按固定负荷和考虑负荷转移来算日收益的差异能到10%到20%这个量级在工程上绝对不可忽略。1.2 “互动”为什么能改变最优决策互动建模的本质是把用户的充电负荷看成电价的函数而不是独立变量。当电价升高一部分用户会把充电时间改到低价时段当电价降低又会吸引更多用户来充电。于是电价和负荷形成耦合电价高导致负荷下降负荷下降又反过来影响收益。只有把这个双向关系放进优化模型我们才能找到一个真正的“最优分时电价”——在这个电价下用户的响应行为、储能的充放电策略和电网购电曲线全部协调一致。这种思路和单纯的需求响应不同需求响应往往是给定一个电价或激励然后看用户的反应而这里电价本身就是优化出来的。它把运营者的定价权和用户的用电自由同时纳入了优化框架属于典型的双层优化思想但工程实现上如果数据量不大也可以用迭代加混合整数规划来处理后面会具体展开。举个例子电价从0.6元/kWh降到0.3元/kWh充电负荷可能会上升30%虽然单位售电收入下降但光伏消纳和充电量增加总收益不一定亏。反过来电价升到1.2元/kWh时只有急用车的用户会留下来充电负荷显著减少储能这时候就应该安排放电而不是继续等待更贵的电价。这些联动决策固定负荷模型完全给不出来。一句话总结这个模型解决的是“有定价权的充换电站运营商如何把电价这个杠杆和站内硬件资源拧成一股绳”的问题。2. 建模前的系统结构从物理拓扑到参数表2.1 典型光储充换电站的物理拓扑我复现时采用最简单也最常见的交流母线拓扑光伏阵列通过DC-DC变换器先稳压再经过逆变器接到380V交流母线储能电池通过双向变流器接在母线上既能充电也能放电直流快充桩通过整流模块从母线取电换电设备包含一套电池架和换电机器人它本质上也是一组可控充电负荷。站内所有设备的功率都汇总到交流母线上母线与外部电网之间只有一个关口表记录从电网购电或向电网售电的功率。这种拓扑的建模优势在于所有设备都可以用“从母线获取的有功功率”这一个变量来统一描述不需要关心内部整流逆变的具体控制细节。对优化调度而言关注的是有功功率平衡无功和电压问题可以放在二阶规划或潮流校验阶段再考虑。如果你们做的是直流母线拓扑比如光储充一体的直流微网建模思路差不多只是网损和变流器效率的表达式要调整。2.2 一张能直接用的参数表复现一个模型最快的方式是先按典型工程尺度把参数表建好再写公式和代码。下面是我在项目中实际采用的一组基准参数参数数值说明光伏额定容量100 kWp车棚光伏按朝向和倾角修正储能额定容量200 kWh磷酸铁锂电池初始SOC取0.5储能额定功率50 kW双向变流器限制充电桩数量/单桩功率4台 / 60 kW直流快充可调功率换电需求3组/小时高峰期需求可按转移率调整日最大充电需求800 kWh含快充和换电总量电网购电上限250 kW变压器容量限制购电价区间0.25 ~ 1.2 元/kWh优化出的分时电价边界用户价格弹性系数-0.3经验值可标定表格里的电价弹性系数是最关键也最容易拍脑袋的参数。现在正在做项目的话建议用历史充电数据和对应时段电价做个最小二乘回归直接估出弹性系数比盲目用文献值更可靠。我第一版就是用的-0.2后来对比实际数据发现用户对价格比想象中敏感改到-0.35之后负荷转移的幅度和实际情况就对上了。2.3 参数归一化的经验Matlab做优化时最怕变量数值差太多。比如电价是0.5量级负荷是800量级储能功率是50量级SOC是0到1量级。如果直接代入线性规划或混合整数规划求解器内部的数值容差设置很容易出问题经常出现“无解”或者“收敛慢”。我的习惯是把所有功率都除以一个基准值比如取100kW作为基准功率电价保留原单位但负荷、储能、光伏都变成标幺值。这样决策变量的量级基本落在0到10之间求解器跑起来稳很多结果出来后再乘回基准功率。这个技巧听起来简单但影响非常大。很多物理模型复现失败并不是公式错了而是数值病态。特别是在用intlinprog时约束矩阵的条件数太大求解器会直接给出一组很离谱的解比如某时段储能充电功率为负或者SOC越界。而做完归一化之后这类问题会少很多。我建议所有从外部读取的数据都在进入优化函数之前统一处理成结构体params中的标准化字段这样后续调试也方便。3. 元件级建模光伏、储能和充电换电的数学表达3.1 光伏出力的典型日系数法光伏出力我们不需要从光照辐射逐时计算除非要研究气象不确定性。复现这个模型时我采用归一化的典型日出力系数gamma_pv(t)用正弦曲线或者从实测数据提取的日曲线作为输入。表达式P_pv(t) P_pv_rated * gamma_pv(t)其中gamma_pv(t)在夜间为0日出后上升中午达到1附近。比如夏季典型日可以设定为gamma_pv(t) max(0, sin(pi * (t - 5) / 14))这里t是小时5点日出19点日落14小时日照。代码很简单t 1:24; gamma_pv max(0, sin(pi * (t - 5) / 14)); P_pv 100 * gamma_pv; % kW如果手上有真实的日辐射数据直接归一化后替换这个数组即可。这里要提醒一点用正弦曲线模拟光伏峰时段出力变化太快而真实光伏在中午前后通常有一个平台期。做优化时中午平台期的持续时间会显著影响储能和充电负荷的安排所以我后来用了一个分段函数把最大出力时段拉长到3个小时左右。具体形状可以根据项目所在地的日照情况调整不是固定的。3.2 储能电池的SOC与功率约束储能模型的核心是状态变量SOC。在一个调度周期内每个时间步的SOC按下面递推SOC(t1) SOC(t) [eta_ch * P_ch(t) - P_dis(t) / eta_dis] * dt / E_bat其中P_ch和P_dis分别是充电和放电功率eta_ch和eta_dis是变流器和电池的综合效率E_bat是电池容量dt取1小时。约束条件有三个必须同时满足功率上限0 P_ch(t) P_rated0 P_dis(t) P_rated充放电互斥P_ch(t) * P_dis(t) 0实际用二进制变量y(t)来建模P_ch P_rated * yP_dis P_rated * (1-y)SOC上下限SOC_min SOC(t) SOC_max通常取0.1到0.9磷酸铁锂保守一点取0.2到0.8。还有一个容易被忽略的约束是调度周期结束时SOC要回到初始值否则模型会把储能最后一小时的电量直接卖掉换收益导致结果失真。我一般加一个不等式约束SOC(24)与SOC(1)之差在±0.05以内。如果完全要求相等在某些工况下可能无解放松一点更稳妥。3.3 充电桩与换电负荷的建模细节充电桩的负荷包含两个部分必须满足的基础充电需求和可以参与响应的可转移充电需求。对于每台充电桩单时段最大充电功率是60kW车辆插入后可以调节功率这是常见的车桩协同方式。换电设备与充电桩不同它涉及到满电电池组的储备。如果把换电需求简化成一组功率需求那么换电设备在某个时段就是从母线取电给换下的电池充电。为了体现用户互动我允许一部分换电需求在时间上平移比如司机可以提前或延后1小时换电但不能超过2小时用整数变量表示延迟时段。这里补充一个实现技巧在Matlab里写约束时不要用循环去定义全部变量那样速度慢而且容易出错。我用矩阵方式一次性构造约束矩阵这样intlinprog跑起来非常有效率。例如储能SOC约束可以写成稀疏矩阵的形式配合现有的矩阵运算函数一起用。如果模型规模不大循环确实无所谓但当时间步长缩到15分钟、决策变量成倍增加时矩阵化写法的优势就非常明显了。4. 用户充电负荷与最优分时电价的互动机制4.1 价格弹性响应用户不是“死负荷”用户对电价的响应最经典的模型是价格弹性系数。相对简单的线性近似是P_charge(t) P_base(t) * [1 epsilon * (p(t) - p_ref) / p_ref]其中P_base(t)是参考电价p_ref下用户原本的充电负荷epsilon是价格弹性系数p(t)是当前时段的电价。epsilon通常为负电价高于参考值负荷下降电价低于参考值负荷上升。这个模型的好处是简单直接代码里就是一行数组计算缺点是它只反映本时段响应没有体现跨时段转移。跨时段转移需要用交叉价格弹性。用户会把某个时段的充电需求挪到其他电价更低的时段。比如定义Delta_P(t) sum_{s ! t} epsilon_{ts} * P_base(s) * (p(t) - p(s)) / p_ref这个矩阵化表达更接近真实行为但参数很难获取。工程上我建议折中先把负荷按用户类型分为“可转移负荷”和“刚性负荷”刚性负荷完全不受电价影响可转移负荷按价格弹性改变总量和各时段分布。这样做既保留了互动特征又不至于让参数标定变得不可行。在Matlab里实现时我会把P_base存成一个24行的列向量然后对每个时段计算响应后的负荷。要注意的是用户响应函数必须保证负荷不为负所以代码里要加max(0, ...)的截断。否则在某些极端电价下计算出来的负荷变成负值后面的功率平衡约束就崩了。4.2 把电价变成决策变量双层思路和迭代思路如果严格地把电价和负荷同时作为变量写进一个优化问题目标函数会出现电价乘以负荷的二次项变成非线性规划。Matlab的intlinprog解决不了直接的非线性Yalmip配合外部求解器可以处理但需要额外授权和安装复现门槛会提高。我在项目里采用了两阶段迭代思路外层把分时电价作为决策变量内层把用户响应后的负荷当成固定输入用混合整数规划优化储能并返回电站综合收益收益函数反馈给外层外层用粒子群或随机搜索更新电价。反复迭代直到电价不再变化。本质上这是用固定点迭代逼近双层优化的均衡解工程上很好用。另一种更简单的方法是把电价离散成若干档位例如从0.3到1.2元/kWh以0.05为步长产生一组候选电价组合对每个组合计算用户响应负荷然后用约束条件筛选出收益最高的组合。这种方法在离散空间较小时非常直观结果还可以作二维热力图来观察最优电价区域。我个人更推荐先把迭代思路跑通再根据项目需要加粒子群因为粒子群调参数也花时间如果电网约束不复杂离散搜索可能更省事。4.3 用户满意度与负荷转移限制互动模型最大的风险是把电价优化得太过极端为了追逐收益把电价定得很高用户全跑了第二天再也没有人来了。所以必须加用户满意度约束。我用两个办法限制第一个是充电量平衡约束一日内用户总充电量在基准值的0.9到1.1倍之间允许用户把充电需求在其他时段完成但不能凭空消失。这样既保证了服务量也限制了负荷削减幅度。第二个是最大转移比例约束任何一个时段的可转移负荷最多只能有30%被转移走剩余70%必须在本时段内完成充电。这样模拟了用户到站后等待时间有限的真实行为。换电需求类似延迟最大不超过2小时。实际标定时转移比例可以根据站内历史数据估算比如晚上6点到9点的高峰时段之前有多少订单会在电价调整后分流到其他时段这个比例就是转移率。完全没有用户行为数据时从0.2到0.4之间取一个保守值都比默认0要可靠。加了这两个约束之后优化出来的电价曲线会平滑很多不会出现为了多卖几度电而把某个时段电价拉到上限的激进行为。5. 整体优化模型目标函数、约束清单与求解器选择5.1 目标函数从售电利润到综合收益整体优化模型的目标是最大化电站日运行收益包括五个部分充电售电收入sum_t p(t) * P_charge(t)充电服务费收入sum_t k_service * P_charge(t)k_service是服务费单价与电价独立光伏余电上网收入sum_t p_fit * max(P_pv(t) - P_load_internal(t), 0)这里p_fit是上网电价购电成本sum_t p_buy(t) * P_buy(t)储能充放电损耗成本用等效度电成本乘以放电电量或者通过SOC变化折算。把这几项组合起来目标函数写成R sum_t [p(t) * P_charge(t) k_service * P_charge(t) p_fit * P_pv_sell(t) - p_buy(t) * P_buy(t) - c_bat * P_dis(t)]其中P_pv_sell是光伏上网功率P_buy是电网购电功率。如果想进一步考虑碳排放还可以在目标里增加碳成本项不过这会增加参数和约束建议先跑通基础版本再加。这里有一个容易忽略的逻辑充电售电收入里的p(t)和用户响应负荷P_charge(t)是耦合的所以目标函数里电价和负荷相乘会产生非线性项。处理办法是外层固定电价内层直接算收益这样整个优化问题被拆成两个阶段避免了非线性求解的麻烦。5.2 约束条件清单一个都不能少我在Matlab里把约束分成四组来写第一组是功率平衡约束P_pv(t) P_dis(t) P_buy(t) P_ch_total(t) P_charge(t) P_swap(t) P_pv_sell(t) P_ch_bat(t)。意思是从外部输入功率和站内电源等于所有负荷加上储能充电功率加上光伏上网。这里P_ch_total是储能充电功率P_charge是用户充电负荷P_swap是换电设备功率。第二组是设备约束光伏出力在0到额定值之间储能功率上下限和SOC上下限充电桩单时段功率不超过60kW换电设备功率不超过对应上限。注意充电桩的日充电总量必须在用户需求范围内。第三组是交互约束分时电价必须在允许的价格区间内比如0.25到1.2元/kWh同时为了不让电价曲线出现无意义的剧烈波动还可以加相邻时段电价差上限比如每小时电价变化不超过0.2元。这条约束在固定电价方案里不需要但在优化电价模型里非常重要。第四组是用户满意度约束日总充电量不低于基准值的0.9倍不高于1.1倍可转移负荷最大转移比例不超过30%换电延迟不超过2小时。这些约束直接对应前面互动机制里的内容。这四组约束缺一不可。我见过不少复现代码只写功率平衡和设备约束结果优化出来的电价一天之内从0.3跳到1.2又跳回0.3用户负荷也跟着乱跳明显不符合实际。其实只需要加一条电价变化速率约束就能稳定很多。5.3 intlinprog还是粒子群求解器选择对比求解策略需要根据约束的线性情况来选。如果电价已经确定用户响应后的负荷就固定此时储能充电、电网购电、光伏上网等问题是线性约束加少量二进制变量可以直接用Matlab的intlinprog求解。如果电价作为连续变量同时负荷响应是线性的可以用Yalmip建模后调用Gurobi或Cplex这类商业求解器处理二次约束。但这需要额外工具并不是所有环境都方便。如果想完全自己维护一个不依赖商业求解器的方案可以用粒子群优化外层电价内层用intlinprog解储能调度。粒子群负责搜索电价组合intlinprog负责在给定电价下得到最优储能策略两者交替。三种方式的适用场景我做个表方案适用场景优点缺点intlinprog 固定电价电价事先给定只优化储能求解快可证明最优电价不是最优Yalmip 商业求解器有授权需要严谨全局最优建模灵活扩展性强依赖外部安装粒子群 intlinprog想要完全可控的两层迭代不依赖商业求解器能联动电价收敛性依赖参数不能严格证明最优我自己复现标题里这个模型用的是第三种方案后面章节的代码也围绕这个方案展开。如果项目时间充裕建议先把固定电价版本的intlinprog跑通确保储能和功率平衡逻辑正确再叠加外层电价迭代。这样出错时更容易定位问题。6. Matlab复现过程与调试记录从能跑到跑对6.1 代码框架主函数、参数文件、结果画图分得清我建议把代码分成四个文件main.m主循环负责迭代电价和调用优化setup_params.m存放参数表里的所有数据和数组返回结构体paramsoptimize_operation.m输入电价和用户响应负荷调用intlinprog得到储能策略和收益返回结果结构体plot_results.m画电价曲线、负荷曲线、SOC曲线和收益构成图。这种分工的好处是每次调参不需要翻主程序的逻辑直接改setup_params.m就行。我复现时一开始把所有代码揉在一个脚本里结果每次改参数都要滚动半天后来拆开就顺畅多了。如果你们做的是课程作业或者论文复现也建议保持这个习惯因为后面写报告时直接引用函数名和变量名会很清晰。6.2 核心代码电价-负荷迭代与储能优化求解下面给一段简化版的main迭代代码方便理解整体结构% main.m 简化骨架 params setup_params(); % 初始电价先按谷平峰三段设置 p [0.3*ones(1,7), 0.6*ones(1,6), 1.0*ones(1,5), 0.6*ones(1,6)]; for k 1:20 % 最多迭代20次 P_charge user_response(p, params); % 用户响应负荷 result optimize_operation(p, P_charge, params); % 储能优化 p_new update_price(result, p, params); % 外层更新电价 if max(abs(p_new - p)) 1e-3 break; end p p_new; end plot_results(p, result, P_charge, params);user_response函数里就按价格弹性公式计算负荷转移optimize_operation函数里用intlinprog求解储能update_price函数根据当前收益梯度调整电价比如某个时段负荷过高就把电价提高一点负荷过低就降低一点。实际迭代时为了防止振荡我会用阻尼系数p_new p_old alpha * (p_suggested - p_old)alpha取0.3到0.5之间。如果不加阻尼电价很容易在两端来回震荡最终无法收敛。我第一次跑的时候alpha取1结果电价从0.4涨到1.1下一轮又跌到0.3负荷也跟着剧烈波动完全没法看。后来把alpha降到0.35大概10轮就稳定了。6.3 四个让人头大的复现坑第一个坑是无解。我最早把SOC必须在调度结束时回到初始值写成硬约束结果某些极端电价组合下模型直接无解。后来放宽为相差不超过5%同时让储能功率上限和充电功率上限互相匹配问题就解决了。如果还是无解先把用户满意度约束里的0.9倍下限放宽到0.8倍看看是不是需求过强导致的。另外要注意如果P_charge是用户响应后的负荷且充电功率可能为0那储能在负荷低谷时可能只能充电但若SOC已满会发生无解。所以要给SOC留出足够的调节空间。第二个坑是迭代发散。电价更新如果步长太大用户响应负荷会剧烈波动电价跟着剧烈波动。解决方法是加阻尼系数并且限制相邻时段电价变化幅度不超过0.1元/h。这两个措施一加上迭代稳定了很多。我实际测试中发现电价变化限制比阻尼系数更管用因为它直接限制了可行域。第三个坑是intlinprog的整数变量写法错误。充电桩的功率是否可调、储能是否正在充电这些都需要二进制变量。但二进制变量如果设置得太碎求解速度会非常慢。我的做法是只给储能充放电互斥和换电延迟这两个地方加整数变量充电桩功率直接作为连续变量处理因为快充桩可以连续调节功率。如果你们用的是更老旧的直流程控桩充电功率不可调那就要给每个桩的启停状态加二进制变量模型规模和求解时间都会上升。第四个坑是结果不合理并且自己没发现。比如储能在电价高的时段同时充电又放电看代码逻辑没毛病其实是因为功率平衡约束写成了导致出现了循环功率。我的排查方法是在每个时段检查功率平衡表达式的残差输出成表格一眼就能看出哪一行不平。另外要把电网购电功率单独打印出来看看是否出现神秘的购电负值。这些检查看起来枯燥但能省下大量后面画图解释的时间。6.4 结果合理性自查清单模型跑通之后不要急着写报告先做几项合理性检查电价曲线是否呈现“低谷低、高峰高”的合理分布而不是没有规律的跳点储能是否在低价时段充电、高价时段放电SOC曲线是否单调且落在0.1到0.9之间用户响应后的充电负荷转移方向是否符合弹性系数符号比如电价最高的时段负荷相对基准是否有明显下降日总收益是否高于不使用互动模型的基准方案如果低于说明互动建模没有给运营带来增益需要检查参数设置光伏消纳率是否合理光伏大发时段如果储能和充电负荷完全没有提升说明电价优化还不够激进。我复现完成后通常会和文献里的典型结果对比一下把电价上下限、弹性系数和负荷基准调整到接近论文场景看看收益量级是否匹配。如果差得太多优先检查价格弹性系数和用户转移比例的取值这两个参数是结果敏感度最高的地方。弹性系数调高0.1可能就会让某个时段的电价从0.8变成0.6储能的充放电行为也会跟着变。这不是bug而是模型本身就对该参数非常敏感所以标定参数时一定要多花心思。最后再分享一个我自己的调试习惯每次跑完最优电价迭代我都会把中间每一轮的电价变化画在同一张图上观察收敛过程而不是只看最终结果。如果迭代轨迹呈螺旋振荡说明阻尼系数太小如果从第三轮开始曲线就不再变化说明电价初始值离最优解很近。这种“看过程”的习惯能让你快速判断模型是否健康比看一堆数值指标要直观得多。
返回列表