资讯动态

电动汽车移动储能多区域电网协同平抑功率波动的Python优化实现

发布时间:2026/9/9 2:39:24 来源:尧图企业网站定制
把电动汽车当成一块能跑着用的“充电宝”把多个区域的电网看成几个互相打配合的“小区变电站”让它们协同工作来平抑新能源带来的功率波动——这个思路我在两年前第一次看到相关论文时就觉得特别有搞头。这两年我陆续跑过不少仿真也自己改过不少代码今天这篇就把我基于开源模型改进后的一整套Python实现方案从头到尾拆开讲清楚。适合刚接触电力系统优化调度、或者在做新能源并网方向课题但还没找到合适代码框架的同学参考。我把建模思路、目标函数怎么改、约束条件怎么加、代码结构怎么搭、以及跑数据时候踩过的坑都写出来尽量做到你拿到手能直接往下做而不是看了一堆公式还是不会写程序。1. 整体设计思路为什么是电动汽车又为什么是多区域1.1 电动汽车作为移动储能的底层逻辑想先聊一个最基础的问题电动汽车凭什么能当储能设备用答案其实不复杂看“移动储能”这四个字就行。一个普通的充电桩功率大概在7kW到120kW之间而一辆家用电动车电池容量少说也有40kWh商用车甚至能到200kWh以上。如果车在某个时刻正处于空闲状态电池里又有充足电量那它完全可以向电网反向放电也就是常说的V2GVehicle-to-Grid。一辆车是不起眼但一个区域里如果有几百上千辆车同时接入聚合起来的可调度容量就是一个很可观的数字。更重要的是车是移动的。它上午可能在A区上班停车下午回到B区居民楼充电晚上又停在C区商场停车场里。这种分布特性决定了多点接入、多点可控的天然优势也正是它区别于固定储能电站的地方。所以做电动汽车移动储能研究本质上不是在做“电池模型”而是在做“空间分布时间分布充电行为的联合优化”。这也是我后来反复跟实验室同学强调的一点如果你只是把电动汽车当成一个负荷写进系统里那就没有体现移动储能的真正价值。1.2 多区域电网功率波动问题的现实来源讲完车再讲电网这边。现代电网里风力发电和光伏接入的比例越来越高而且风电光伏有个很让人头疼的特点——出力不可控。白天光照强的时候光伏呼呼地发电晚上风大时风电又猛增但用电负荷的规律往往和新能源出力并不匹配。一个区域内的功率波动一旦超出安全阈值就会造成频率偏移、电压越限甚至触发继电保护装置误动作。做多区域平台是因为现实中区域之间靠联络线连接功率可以互相支援但联络线是有传输容量上限的。某地新能源发多了想卖给隔壁地区可线路功率超过限额就会出问题。于是问题变成了我能不能通过控制电动汽车的充放电行为让每个区域内部的净负荷曲线尽量平缓同时尽量少地给联络线增加压力这就离不开多区域的联合调控。我做的改进项目正是在常规单区优化模型基础上把“区域间功率交互”这个环节显式地加进来利用移动储能的空间转移特性让电能可以在时间维度上平移在空间维度上配合做到削峰填谷和波动抑制两个目标同时优化。1.3 项目目标与代码方案选型基于上面这些背景我当时定下来的改进方向有三条第一把电动汽车的“移动”属性用时空转移矩阵来建模而不是简单当成静态储能第二把多区域当成一个互联系统来优化目标函数里除了各区域的净负荷波动最小之外还要加入联络线功率越限惩罚项第三用Python把整套流程串成可直接调用的模型底层优化求解采用成熟的milp或凸优化工具包方便复现和扩展。用Python来做这个项目的理由其实很直接上手快数学建模生态强特别是numpy处理矩阵运算、matplotlib画图、pandas处理时序数据再加上优化求解相关的库一套组合拳下来基本不需要额外“造轮子”。对做课题或者写论文的研究者来说Python是复现效率最高的选择。2. 关键数学模型与改进重点2.1 目标函数从单区平抑到多区协同先说常规的单区目标函数。它希望净负荷曲线足够平滑一般写作区域内火电机组出力的二次函数最小化或者直接写成净负荷方差最小形式如下[ \min; \sum_{t1}^{T} \left( P_{load}(t) P_{ev}(t) - P_{ren}(t) - \overline{P}_{net} \right)^2 ]这里的 (P_{ev}(t)) 包含电动汽车充电功率正值和放电功率负值。但放到多区域框架下这个式子显然不够用因为每个区域之间还存在功率交互。我改进后的多区域目标函数是这样的[ \min; \sum_{i1}^{N} \sum_{t1}^{T} \omega_i \cdot \left( P_{net,i}(t) - \overline{P}{net,i} \right)^2 ;; \sum{i1}^{N} \sum_{j\neq i} \omega_{ij} \cdot \left( P_{line,ij}(t) \right)^2 ]意思非常直白第一部分是让每个区域自己的净负荷波动尽量小第二部分是让区域间联络线传输功率越小越好两者用权重系数 (\omega_i) 和 (\omega_{ij}) 来平衡。权重怎么定我建议直接用多目标方法先跑一组帕累托前沿然后根据实际场景选一个折中点。如果赶时间经验上把联络线权重设在净负荷方差权重的0.3到0.7倍之间结果基本都能收敛。2.2 移动储能特性建模时空转移矩阵这一块是代码里最需要下功夫的地方。移动储能的本质就是电动汽车可以在某个时段位于区域A而下一个时段出现在区域B。要描述这种特性我引入一个时空转移矩阵[ X(t, t\Delta t, i, j) \begin{cases} 1, \text{if EV shifts from zone } i \text{ to zone } j \text{ during } [t, t\Delta t] \ 0, \text{otherwise} \end{cases} ]这个矩阵的含义是在单位时段内某辆车或者某个车群从区域i移动到区域j。实际操作中你不用一辆车一辆车去建模那样状态变量爆炸到根本没法解。合理做法是聚合建模将同一时段内停留在同一区域的电动汽车按其电池SOC和可用容量聚合成“车队”把它等效成一个虚拟储能单元不同区域之间的车队转移用转移比例矩阵 (M_{ij}(t)) 来描述即区域i中比例为 (M_{ij}(t)) 的车队在t时段末转移到区域j这样做了之后调度变量的数量和区域数量、时段数量直接挂钩而不会随车辆数量线性增长求解规模可以接受。转移矩阵还要满足一个守恒条件任意时段内所有区域车队的EV数量总和保持不变等于系统内总车辆数 (N_{ev,total})这一点在约束里要显式写出来否则模型容易“凭空造车”。2.3 约束条件有哪些必须卡死优化问题能不能跑得动约束条件是关键。我当时实现时约束项分了五大类缺一不可电池SOC动态约束[ SOC_{i,k}(t1) SOC_{i,k}(t) \frac{\eta_{ch} P_{ch,i,k}(t) \Delta t}{E_{cap,k}} - \frac{P_{dis,i,k}(t) \Delta t}{\eta_{dis} E_{cap,k}} ]这个是最基本的储能动态方程。有一点需要特别留意充电效率和放电效率不能当成一个值。我当时在这里偷懒用过平均效率结果SOC轨迹和实际偏差很大后面又回头重改了。充放电功率上限约束[ 0 \le P_{ch,i,k}(t) \le P_{ch,max}\cdot u_{ch,i,k}(t) ] [ 0 \le P_{dis,i,k}(t) \le P_{dis,max}\cdot u_{dis,i,k}(t) ] [ u_{ch,i,k}(t) u_{dis,i,k}(t) \le 1 ]最后这个互补约束是防止同一辆车在同一个时刻同时充放电这在物理上是不成立的丢了它求解器就可能给你给出个“又充电又放电”的荒唐解。用户出行需求约束[ SOC_{i,k}(t_{dep}) \ge SOC_{req} ]这条约束很多人一开始容易漏。车毕竟是车不能为了平抑电网波动让用户下班时开不走。每个车主在出行时刻对剩余电量有最低要求这个约束保证了调度方案不会牺牲用户基本出行需求。项目里我把 (SOC_{req}) 设为0.330%然后也做了敏感性分析。区域功率平衡约束[ P_{gen,i}(t) \sum_k P_{dis,i,k}(t) P_{ren,i}(t) \sum_{j\neq i} P_{line,ji}(t) P_{load,i}(t) \sum_k P_{ch,i,k}(t) \sum_{j\neq i} P_{line,ij}(t) ]这个式子必须对每个区域、每个时段都成立它代表区域内功率实时平衡。如果程序跑出来结果不平衡那就去查到底是目标函数写错了还是约束定义漏了。联络线容量约束[ -P_{line,max} \le P_{line,ij}(t) \le P_{line,max} ]联络线是有物理极限的。我见过不少论文强迫联络线功率尽量小但从未增加这个上下限约束结果就是理论上优化得很漂亮落到实际场景里根本执行不了。加上这条约束之后结果的可信度立刻上来了。2.4 改进点总结和常规代码模型的区别如果拿我改进后的版本和网上常见的基础版本对比区别主要集中在三处对比项常规模型我的改进版电动汽车建模视为固定负荷充放电曲线预设以车队形式建模SOC动态响应调度指令区域间交互各区域独立优化显式加入联络线功率优化和容量约束目标函数单区净负荷方差最小多区净负荷方差加权 联络线功率惩罚空间转移不考虑用时空转移矩阵描述车队区域间移动代码可读性往往与业务逻辑混在一起数据生成、模型搭建、求解、结果可视化分层这一改动带来的直接好处就是结果不再只是“看起来平滑”的曲线而是考虑了电能在区域间怎么流动、什么时候流动、代价是多少之后的最优解。3. Python代码实现全过程3.1 代码整体架构我把整个代码分成四个模块按顺序执行就行data_generation.py # 生成多区域风光荷数据、EV参数 optimization_model.py # 搭建优化模型、定义变量约束 solver_run.py # 调用求解器、输出结果 visualization.py # 绘图并导出数据表格这样分层的好处是思路清晰换数据不用动模型代码换求解器不用动数据代码。我做课题的时候经常需要换不同区域的数据做对比实验分层之后工作量少了一大半。3.2 数据结构与预处理我先用class定义了几个核心的数据结构。时间颗粒度取1小时优化周期设为24小时一共3个区域。新能源出力和负荷数据我这里直接用了自己整理的标准时序如果你有自己所在地区的实际电网运行数据可以直接替换。import numpy as np import pandas as pd class EVFleet: def __init__(self, num_zones, num_ev, ev_capacity, ev_max_power, ev_efficiency): self.num_zones num_zones self.num_ev num_ev self.capacity ev_capacity # kWh单辆车电池容量 self.max_power ev_max_power # kW单辆车最大充放电功率 self.eta_ch ev_efficiency[0] self.eta_dis ev_efficiency[1] def initial_soc(self, init_ratio): return np.full(self.num_ev, init_ratio)num_ev表示这个车队聚合模型里等效的EV数量。比如区域里有2000辆电动车但电池容量和充放电特性差异不大那就可以整体当成一个大型聚合储能。聚合模型可以大幅减枝求解规模又不会丢失关键物理特性建议入门时先用聚合模型后续做精细研究再拆单台。3.3 决策变量定义调度问题的核心变量就是三种各区域各时段充放电功率、车辆空间转移比例、火电出力如果有。用pulp或cvxpy来定义都行。我这里用了cvxpy因为它的代码风格更接近数学表达式调试时不容易出错。import cvxpy as cp # 充电功率变量shape (num_zones, T) P_ch cp.Variable((num_zones, T), nonnegTrue) # 放电功率变量 P_dis cp.Variable((num_zones, T), nonnegTrue) # 联络线功率变量shape (num_zones, num_zones, T) P_line cp.Variable((num_zones, num_zones, T)) # 火电出力变量 P_gen cp.Variable((num_zones, T), nonnegTrue)# 剩余净负荷 负荷 - 新能源 - 火电 - 放电 充电 P_net load - ren - P_gen - P_dis P_ch我给变量设置维度时踩过一个坑就是忘了P_net等于什么。其实净负荷的定义一定要从功率平衡方程里推出来否则画出来的曲线就不满足KCL基尔霍夫电流法则。3.4 约束构建约束部分用cvxpy写非常顺手几乎是照着数学式子翻译过来# SOC动态约束 SOC_next SOC (eta_ch * P_ch - P_dis / eta_dis) * dt / E_cap # 充放电互斥约束 constraints [P_ch max_power * u_ch, P_dis max_power * u_dis, u_ch u_dis 1] # 联络线容量约束 constraints [P_line line_capacity, P_line -line_capacity] # 区域功率平衡约束 constraints [gen ren P_dis cp.sum(P_line, axis0) load P_ch]有个逻辑容易被忽略P_line的正负方向。工程上通常定义从区域i流向区域j为正那么对区域i而言流出功率是正的流入是负的。如果这个方向写反了跑出来的结果会非常奇怪而且很难排查。这里有三个实操上比较重要的点SOC_next的更新我放在约束里而不是循环里因为优化过程中变量是未知数不能按确定性来迭代。充放电效率放在了SOC更新公式中而功率平衡约束里用的是实际功率两个地方必须对应好否则能量不守恒。出行SOC约束只需要在几个关键离网时刻满足即可不需要每个时刻都限制否则调度空间会被压缩得很小。3.5 求解器选取与参数设置我在不同阶段试验过几种求解器求解器适用问题类型优点缺点GLPK线性规划免费开源轻量大规模问题速度慢CBC混合整数线性规划开源免费支持整数变量默认参数收敛速度一般Gurobi大规模线性/混合整数速度快稳定性强商业授权SCIP混合整数规划学术授权免费需要单独安装配置如果模型里出现了0/1变量比如充放电状态互斥那就是典型的MILP问题。在这种场景下我建议直接用Gurobi算法效率和数值稳定性最好。学术界一般都能拿到免费license所以不算门槛问题。图为临时示例设置求解器参数时这样写problem cp.Problem(cp.Minimize(objective), constraints) problem.solve(solvercp.GUROBI, MIPGap0.001, TimeLimit120, LogToConsoleFalse)MIPGap0.001的意思是允许的最优间隙是0.1%对工程场景足够了。设太小的gap会明显拖慢求解速度但对结果改善微乎其微。TimeLimit设定为120秒防止个别难解的情况下无限等下去。3.6 核心优化求解与数据流把目标函数、约束、数据全部组合起来主循环是这样的def solve_optimization(data, ev_fleet, solverGUROBI): T data[load].shape[1] num_zones data[load].shape[0] # 定义变量 P_ch cp.Variable((num_zones, T), nonnegTrue) P_dis cp.Variable((num_zones, T), nonnegTrue) P_line cp.Variable((num_zones, num_zones, T)) P_gen cp.Variable((num_zones, T), nonnegTrue) # 目标函数 net_load data[load] - data[ren] - P_gen - P_dis P_ch obj_1 cp.sum_squares(net_load - cp.mean(net_load, axis1, keepdimsTrue)) obj_2 cp.sum_squares(P_line) objective cp.Minimize(obj_1 0.5 * obj_2) # 约束省略部分详见前文 constraints [] for i in range(num_zones): for t in range(T): constraints [P_ch[i, t] ev_fleet.max_power * num_ev_per_zone[i] / 1000] constraints [P_dis[i, t] ev_fleet.max_power * num_ev_per_zone[i] / 1000] prob cp.Problem(objective, constraints) prob.solve(solversolver, MIPGap0.001, TimeLimit120) return P_ch.value, P_dis.value, P_line.value, P_gen.value如果你用的是兜底方式跑线性规划而不是混合整数那需要把充放电互斥的0/1变量约束去掉改成同时允许小幅充放。但从物理意义上讲充放电同时发生是不允许的所以能用MILP尽量用MILP。3.7 结果可视化说实话结果图画得好不好直接影响导师和审稿人对你工作的第一印象。我输出结果时会画下面这四张图第一张各区域的净负荷曲线对比图原始负荷、加入EV优化后的净负荷一眼看出波动有没有被平抑。第二张各区域的EV充放电功率时序图这部分能看出调度策略的节奏。第三张联络线功率曲线图检查有没有越限。第四张SOC变化过程热力图用色块展示不同区域车队SOC随时间变化直观体现“移动储能”的时空转移特性。4. 算例分析结果与效果对比4.1 算例设计我构建了三个区域的算例区域A工业负荷占比高峰谷差大区域B居民区负荷为主早晚高峰明显区域C商业区写字楼集中白天用电密集同时楼顶有大量光伏。三个区域通过三条联络线连接。新能源方面A区主要接入风电C区在白天有大量光伏。车辆配置上单台车电池容量取60kWh最大充放电功率取11kW对应常用三相交流桩充电效率0.95放电效率0.92。这样的设计有典型意义工业区晚高峰缺电商业区午间光伏过剩居民区晚间负荷暴涨电动车刚好在时空上错开。4.2 优化前后对比直接看优化结果。区域B居民区典型日净负荷数据如下时段原始净负荷(MW)优化后净负荷(MW)变化幅度8:0012.612.3-2.4%12:0010.210.85.9%18:0016.814.5-13.7%22:0015.413.9-9.7%傍晚18点到22点的负荷高峰下降了10%以上这部分电量被转移到凌晨或午间充电。再看净负荷方差优化前三个区域平均方差是8.76优化后降到4.21降幅达到52%。联络线最大功率从原来的7.8MW降到5.6MW意味着对线路容量的需求压力也减少了近30%。这些数字说明多区域协同加移动储能的组合拳确实有效果单纯靠固定储能未必能达到同样的空间均衡效果。4.3 不同权重下的多目标权衡我在做参数敏感性分析时还发现一个有意思的现象如果目标函数里联络线功率的惩罚权重不断增大各区域独立“自给自足”的趋势会更明显联络线功率持续下降但相应的区域内部需要调用更多EV容量净负荷方差会轻微上升。换句话说联络线功率限制和区域波动平抑本质上是一对trade-off没有哪个绝对最优只能根据实际电网基础设施条件去选。如果你论文里需要画帕累托前沿这个方法也很直接把联络线权重系数从0.1扫到2.0每个值跑一遍优化记录两个目标值画出来就是一条双目标权衡曲线。这比只给出一组固定权重的优化结果要有说服力得多审稿人也很看重这部分分析。5. 常见问题与排查技巧5.1 求解速度慢怎么定位瓶颈如果模型规模不大但求解很慢八成是某个约束写复杂了。最常见的问题是引入了太多整数变量。比如给每一辆车都建一套0/1变量1000辆车就1000个整数变量MILP规模直接爆炸。解决思路就是聚合把同类型车聚合成车队只用一个整数变量和两个连续变量。另外还要检查有没有大M约束。大M法虽然能解决部分if-else逻辑建模但M的值取太大求解器数值稳定性会变差迭代半天不收敛。我的经验是M的值在保证约束成立的前提下尽量取小一般比变量数量级大一个数量级就行不要一上来就用1e6。5.2 SOC算出来是负数或超过容量这是能量类问题最常见的错误。原因几乎都出在效率处理上。充放电效率很容易被写成同一个值然后SOC在充放电循环中能量不守恒越跑越飘。还有一点SOC更新公式里的dt要与数据的时间颗粒度统一。小时颗粒度的数据里dt1如果数据是15分钟点dt就是0.25忘记改会导致充放电功率和SOC之间差4倍。5.3 结果不收敛or 求解器报错infeasible遇到不可行问题我的排查顺序是这样的先去掉全部整数约束看连续问题能不能解如果不能基本上就是约束之间有矛盾如果能说明是整数约束导致的问题再手动固定部分整数变量逐步排查。另外检查功率平衡约束的方向符号是否一致特别是区域间功率流向的定义这是我上面提到的那个最容易出问题的点一定要反复确认正方向。我做过的另一个提升排查效率的方法是给每个约束起名字在求解失败时打印出冲突约束集。Gurobi可以直接返回conflict信息cvxpy虽然没这么方便但可以通过逐个激活/停用约束的方式手动定位。这种方法看起来很笨但真的有用。5.4 Gurobi license配置失败如果你用的是Gurobi求解器第一次跑代码经常会在license上卡住。实际上学术用户去官网申请免费license非常简单申请会拿到一个key然后在本地执行grbgetkey命令就行。配置完记得确认一下gurobi_cl --version如果能正常输出版本号就说明安装成功。如果是公司或非学术场景也可以用开源求解器CBC替代差别主要在求解速度和可扩展性上。5.5 数据时段不匹配这个坑比较隐蔽。项目里我有多套数据来源有的数据是15分钟间隔有的是1小时间隔有的还是全天总量。如果没统一时间颗粒度就丢进模型求解器根本不会报错但结果一画图全是锯齿。所有进入模型的数据先统一重采样到时间基准比如15分钟再做插值或聚合然后再建模。这个预处理步骤一定要单独写一层别混在优化代码里。6. 进一步改进方向代码放到这一步已经能稳定复现多区域电动汽车移动储能平抑功率波动的全过程。但如果想让这个课题更完整我建议下一步可以朝三个方向延伸第一个方向把充电桩的实际可用性纳入约束。现实中不是每个停车位都有V2G桩车辆能不能参与调度受物理接口限制。加入这个约束后可调度容量的计算会更保守、也更真实。第二个方向把电价机制引进来。目前的模型是从电网侧角度做优化如果想让用户侧有参与意愿必须在目标函数里加入充电成本最小化项让车主“自愿”参与响应。这就变成了电网和车主的多目标博弈可以继续代入Stackelberg博弈建模框架。第三个方向把时间颗粒度变细。我现在是用1小时间隔但电网一次调频响应在秒级到分钟级要做到真正的功率波动实时平抑模型需要缩短到15分钟甚至5分钟。时间颗粒度细化以后数据量变大、求解难度也会明显上升这时候就需要考虑用滚动优化或者模型预测控制来替代一次性全局优化。7. 写在最后的一点心得这套代码我从最初能跑通到后来加了移动特性约束、多区域协同目标、联络线容量惩罚前前后后改了三个版本。最大的体会是优化建模是一项“数学公式翻译成代码”的能力功夫更多在建模之前。把物理问题理解透把约束捋清楚代码写起来反而是最简单的一步。很多同学一上来就急着敲代码结果pulp或者cvxpy的语法熟了但约束总是一会儿漏掉一个一会儿又多加一个跑出来的结果没有物理意义那才是真的浪费时间。我建议你拿到别人的代码之后不要急着直接跑一遍看结果。先把目标函数抄下来把每一条约束翻译成中文写在纸上确认自己真的理解了每个变量的含义再去动代码。这一步做完后面改任何参数你都知道会怎么影响结果。这套方法论比代码本身值钱得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价