尧图精选

基于多时段动态电价的电动汽车有序充电策略及Matlab实现

🕒 发布时间:2026/10/2 15:28:44 📁 来源:尧图网络
去年给一个小区做充电桩接入评估时物业负责人看着变压器容量报告问我几十台桩如果同时充我这里到底扛不扛得住 我算了一笔账假设全是7kW交流慢充20台同时开工就是140kW的纯增量负荷再叠加傍晚居民做饭、空调这些常规用电峰时段总负荷直接逼近甚至越过配变上限。这件事的本质不在桩本身而在什么时候充。把充电行为从无序的即插即充转成有序的错峰充电核心抓手就是电价信号。今天我就把基于多时段动态电价的电动汽车有序充电策略从头到尾拆一遍并给出可以直接运行的Matlab实现代码覆盖电价构造、优化建模、求解器配置和结果分析适合正在做充电桩/微电网/需求响应相关课题的同学参考。1. 无序充电的代价以及有序充电到底在优化什么1.1 一个典型的晚间负荷叠加场景先看一组很现实的数字。某小区配电变压器额定容量400kVA晚间基础负荷曲线大致在18:00-21:00出现峰值约220kW凌晨2:00-6:00是低谷约60kW。如果小区内有20辆电动汽车全部无序充电每辆7kW同时率算80%那就是112kW的叠加负荷。峰时总负荷变成220112332kW功率因数按0.95折算后约315kW变压器负载率接近80%以上再叠加一点温度升高或三相不平衡就非常危险。更麻烦的是这112kW恰好叠加在居民晚间峰值上这不是用电总量的矛盾而是时间上的撞车。这类问题在工程上叫作负荷尖峰它的危害不只是变压器过载。馈线末端电压会明显跌落容易导致充电桩本身因电压异常停机台区线损也会上升如果未来换成快充桩60kW直流快充一台的冲击就相当于8-9台交流慢充问题会更剧烈。只有把一部分充电负荷从高峰时段挪开才能从根上缓解。1.2 价格信号就是最柔性的调度指令电网调度不能直接夺走用户的充电桩控制权除非做V2G聚合和合同约定最现实的手段是价格信号。用户在充电App上看到电价曲线自然会选择更便宜的时段充电这就是需求响应。多时段动态电价的意义在于把高峰贵、低谷便宜的差异做到足够大让用户有动力改变充电习惯同时把电网侧的负荷压力通过价格传递到用户侧形成双向互动。这里要澄清一个概念有序充电不是少充电而是在合适的时段充电。每辆车最终都要充到用户期望的电量优化的只是时间段内的功率分配。所以评价一个策略好坏既要看用户花了多少钱也要看电网侧的负荷曲线是否被削平了。这也是后面模型里把费用和峰值负荷一起放进目标函数的原因。1.3 这份实现适合谁用说白了这篇文章对应三类人电气工程、控制工程方向的学生课题涉及电动汽车充电策略、需求响应、微电网优化调度需要一个能复现、能改参数的Matlab基线充电桩运营商或园区/小区配电网规划人员想评估分时电价或动态电价能转移多少负荷、每辆车平均能省多少钱刚接触YALMIP和优化建模的Matlab用户想看一个完整项目里如何定义决策变量、写约束、调求解器、做后处理。我下面写的所有代码都是Monolithic地放在一个工程文件里跑通的尽量少依赖第三方数据包装好YALMIP即可求解器用Matlab自带的linprog就能解决连续线性规划问题。2. 多时段动态电价别把动态想复杂了2.1 从分时电价到动态电价的演进逻辑分时电价TOU大家很熟悉就是峰、平、谷三段价格固定。国内很多地区工商业已经推行了分时电价尖峰时段和低谷时段价差可以拉到3-4倍。但分时电价的缺点也明显它的时段边界是提前定好的如果某天实际用电情况偏离了预料比如极端天气导致傍晚负荷暴涨固定时段的价格并不能精确反映当时的供需紧张程度。多时段动态电价更细致一些把一天切成更多时段常见是24个时段甚至每15分钟一个实时电价价格会有一个基础曲线再根据次日负荷预测做修正。也就是说动态通常指日前生成的时变电价不是股票K线那种随机跳动。在学术论文里动态电价可以来自市场出清模型在工程仿真里最常见的是基础电价负荷预测修正的组合我的代码就用这个思路。2.2 生成一条24小时的电价曲线我先把基础分时电价定义好用Matlab写成列向量顺序对应一天24个小时%% 基础分时电价元/kWh按小时索引1~24 T 24; price_base zeros(T, 1); price_base(1:8) 0.30; % 00:00-08:00 谷段 price_base(9:15) 0.65; % 08:00-15:00 平段 price_base(16:21) 1.00; % 15:00-21:00 峰段 price_base(22:24) 0.65; % 21:00-24:00 平段这个曲线已经能表达晚上贵、凌晨便宜的信号但还谈不上动态。我在它之上加一个负荷预测修正项%% 模拟次日负荷预测标幺化曲线量纲不影响结果 L_pred [0.45 0.40 0.38 0.37 0.36 0.38 0.42 0.48 ... 0.55 0.60 0.65 0.68 0.72 0.75 0.78 0.85 ... 0.95 1.00 0.98 0.90 0.80 0.70 0.60 0.50]; L_norm (L_pred - min(L_pred)) / (max(L_pred) - min(L_pred) eps); alpha 0.5; % 动态修正强度系数 price_dyn price_base .* (1 alpha * L_norm);这里的修正逻辑是预测负荷最高的时段指数组里下标17-19附近电价会在基础价格上上浮50%左右负荷最低的凌晨时段电价基本贴着基础价走。alpha越大价格波动越剧烈用户的充电费用差异也越明显。2.3 为什么多时段比峰谷两段更有意义如果只用峰、谷两段电价模型只有两个价格水平优化的自由度其实很低——车要么在峰段充要么在谷段充没有中间选择。多时段的好处在于它能表征更细的供需关系比如晚高峰之后还有一段平峰电价相对温和凌晨2点到5点负荷最低这是最便宜的深谷时段。这样优化算法就能在满足用户约束的前提下把充电功率尽量压到最便宜的1-3个时段而不是简单地在整段谷时平均分配。从数学角度看电价向量的分辨率越高目标函数里的权重越细得到的充电功率时间分布就越能贴合实际负荷曲线。这也是动态电价相比静态分时的核心优势。当然电价时段越多对输入数据的要求也越高实盘中还需要考虑用户响应行为和隐私问题但这些在Matlab仿真阶段都不用操心先把曲线搭好。3. 优化模型的数学骨架目标、变量与约束3.1 决策变量怎么选我见过不少初学者把决策变量设成每辆车的充电开始时间和充电结束时间然后发现约束很难写充电功率也定死了。更自然、更灵活的做法是以每辆车在每个时段的充电功率作为决策变量。设共有N辆车一天划分为T24个时段定义P(i, t)第i辆车在第t时段的充电功率单位kWSOC(i, t)第i辆车在第t时段结束时的荷电状态单位%这里用0到1表示。P是N×24的连续变量矩阵SOC同样。这样一个车的充电行为是连续可调的它可以在多个时段分配功率而不是简单地在某个时刻开或关。实际充电桩虽然很多时候只有开/关两态但工程调度中普遍把功率离散化到0、3.5、7kW等档位那是混合整数问题为了先跑通基线先用连续变量后文会聊离散化升级。3.2 目标函数费用最小与削峰填谷怎么平衡第一个目标很直观用户总充电费用最小。F1 Σ(i1..N) Σ(t1..T) price_dyn(t) × P(i,t) × ΔtΔt1小时所以P(i,t)×Δt就是第i辆车第t时段的充电电量kWh。但只最小化费用有个问题如果某辆车必须在很短时间内充满优化可能会在某个低电价时段内集中大功率充电形成新的局部尖峰。所以我加入第二个目标最小化时段总负荷峰值。总负荷是P_total(t) P_base(t) Σ(i1..N) P(i,t)其中P_base(t)是小区基础负荷。为了保持线性我不直接用方差它是二次的而是引入一个辅助变量Z代表峰值负荷加约束P_total(t) ≤ Z然后在目标里加一个惩罚项F2 λ × Z。这样最终目标min (F1 λ × Z)λ 是权重系数。λ0时只关心用户费用λ很大时算法会优先压平负荷峰值。实际调参时我会先跑一版λ0看看费用基准再逐步加大λ观察费用上升和负荷削平之间的性价比。这个方法在工程里特别常用因为它把削峰转成了线性约束不需要用quadprog求解稳定性好得多。3.3 约束条件SOC递推、充电需求、功率限制、变压器容量约束是整个模型里最需要小心的地方我按四个维度写一是SOC递推关系SOC(i,t1) SOC(i,t) η × P(i,t) × Δt / Cap_iη是充电效率取0.92Cap_i是第i辆车的电池容量单位kWh。注意这个递推是从t到t1它把相邻时段的SOC牢牢绑定是描述电池能量累积的核心方程。二是充电需求约束每辆车在离开时段T_dep(i)时SOC必须达到目标值比如90%SOC(i, T_dep(i)) ≥ SOC_target初始SOC由参数给定这样充多少电由初始和目标的差隐含决定不需要单独计算总能量需求。三是充电功率和设备限制0 ≤ P(i,t) ≤ P_max(i) × Avail(i,t)Avail(i,t)是0/1逻辑矩阵表示第i辆车在第t时段是否连接充电桩。这样能自然地处理白天上班不在家只有晚上才能充电这类时间窗口约束非常实用。四是SOC上下限SOC_min ≤ SOC(i,t) ≤ SOC_max取SOC_min0.1SOC_max0.95。锂电池过放和过充都有安全风险这个约束不能省。五是变压器容量约束P_base(t) Σ(i1..N) P(i,t) ≤ Cap_trans × cosφ这里Cap_trans是变压器视在容量乘功率因数折算为有功功率上限。如果某天负荷预测特别高这个约束可能会把充电功率压得非常低导致部分车辆充不满这时模型会报不可行——这个问题我留在第六节专门讲。3.4 模型性质这是一个线性规划上面所有目标函数项和约束都是线性的Z辅助变量也不影响线性性所以这本质是一个连续线性规划LP问题。决策变量个数N×T大约480个约束大概一千条左右用Matlab自带的linprog或者YALMIP内置的求解器几秒钟内就能跑完完全没必要上CPLEX或Gurobi。但如果把充电功率离散成多档位模型变成混合整数线性规划MILP求解难度会明显上升到那时才需要考虑更换Gurobi等商业求解器。我在实际项目中的经验是先用连续变量跑通逻辑确认约束正确、结果合理再根据业务需要加离散档位。否则一堆约束和数据问题混在MILP里排查起来非常痛苦。4. Matlab代码实现YALMIP建模到求解一段段拆开讲4.1 环境准备与工具箱选择代码依赖只有两个Matlab R2020b及以上版本都可以YALMIP工具箱去GitHub下载安装把文件夹加入路径即可求解器方面用内置的linprog就够YALMIP会自动起一个默认线性的求解器。如果要从YALMIP调用更专业的求解器可以装Gurobi或CPLEX学术用户通常能申请免费许可但在当前规模下没必要。我这里用一个统一的optimize()函数YALMIP会自动选择合适的底层求解器。4.2 参数设置为了让结果可复现我把参数汇总成一张表代码里也按这个顺序来参数数值说明车辆数N20小区充电桩数量时段数T24每小时一个时段电池容量Cap60 kWh按主流长续航车型最大充电功率P_max7 kW交流慢充充电效率η0.92传导损耗SOC初始0.2~0.5随机模拟不同剩余电量SOC目标0.9用户期望电量SOC上下限0.1~0.95安全窗口变压器容量400 kVA小区配电容量功率因数cosφ0.95有功换算基础负荷P_base自定义曲线见代码%% 参数设置 N 20; T 24; Cap_batt 60; % kWh P_max 7; % kW eta 0.92; SOC_min 0.1; SOC_max 0.95; SOC_target 0.9; Cap_trans 400 * 0.95; % 有功上限 kW Delta_t 1; % h rng(2024); SOC_init 0.2 0.3 * rand(N, 1); % 20%~50% % 基础负荷曲线24小时低压侧 P_base [45 40 38 37 36 38 42 48 55 60 65 68 ... 72 75 78 85 95 100 98 90 80 70 60 50]; % 车辆可用时段矩阵 Avail(i,t)1 表示可充电 Avail zeros(N, T); for i 1:N if mod(i, 3) 0 % 部分车辆白天也在家 Avail(i, :) 1; else % 多数车辆仅夜间可用前8小时 后6小时 Avail(i, 1:8) 1; Avail(i, 19:24) 1; end end % 每辆车要求在某时刻前达到目标SOC T_dep 8 * ones(N, 1);这里有一点要说明T_dep8意味着所有夜间充电车辆必须在早上8点之前充满对应的索引是第8个时段07:00-08:00。这是从0点开始算的一天窗口模型符合当晚评估次日凌晨充电计划的场景。如果你的车辆有不同离开时间把T_dep改成向量即可。4.3 YALMIP核心建模代码接下来是重头戏。我用sdpvar定义决策变量逐条添加约束最后调用optimize求解%% 定义变量 P sdpvar(N, T, full); % 充电功率 N×T SOC sdpvar(N, T, full); % 荷电状态 N×T Z sdpvar(1, 1); % 峰值辅助变量 %% 构建约束 Constraints []; % SOC递推与变量边界 for i 1:N % 初始状态 Constraints [Constraints, SOC(i, 1) SOC_init(i) eta * P(i,1) * Delta_t / Cap_batt]; % 递推 for t 2:T Constraints [Constraints, SOC(i, t) SOC(i, t-1) eta * P(i,t) * Delta_t / Cap_batt]; end % 离开前达到目标 Constraints [Constraints, SOC(i, T_dep(i)) SOC_target]; % 功率上下限 可用时段 for t 1:T Constraints [Constraints, 0 P(i,t) P_max * Avail(i,t)]; end % SOC安全边界 Constraints [Constraints, SOC_min SOC(i,:) SOC_max]; end % 变压器容量约束与峰值辅助约束 P_total sum(P, 1); % T×1 每时段总充电功率 for t 1:T Constraints [Constraints, P_base(t) P_total(t) Cap_trans]; Constraints [Constraints, P_base(t) P_total(t) Z]; end %% 目标费用 0.5*峰值惩罚 F1 sum(price_dyn .* P_total) * Delta_t; F2 0.5 * Z; F_obj F1 F2; %% 求解 optimize(Constraints, F_obj); P_opt value(P); SOC_opt value(SOC); total_load P_base sum(P_opt, 1);需要留意的是我把SOC处于初始时段的递推单独写了一行避免索引越界在循环里使用sdpvar的full属性来声明矩阵变量比逐个定义简洁得多。sum(P,1)得到24×1的总充电功率注意Matlab中行向量与列向量的转置关系。4.4 这版代码的求解效率如何我实际跑下来N20、T24的规模YALMIP自动转成线性规划用linprog求解单次耗时大约0.3-0.5秒非常快。这对滚动优化的场景很友好比如每15分钟重新求解一次一天96个周期总耗时也就半分钟左右。如果需要做蒙特卡洛抽样或者灵敏度分析把上面的建模和求解封装成一个函数外层循环跑几百次都在可接受范围内。5. 仿真结果怎么看三条线一起读5.1 画好四张图跑完代码光看数值没什么感觉一定要画图。我通常画四张第一张负荷曲线对比图横轴24小时画出无序充电总负荷把所有车都集中在18:00-21:00充和有序充电总负荷再叠加基础负荷和变压器容量线一眼就能看出峰值被削下来。第二张充电功率热力图横轴24小时、纵轴20辆车颜色表示充电功率大小。这张图很直观地展示算法把功率塞到了凌晨的低价时段。第三张每辆车的SOC曲线确认所有车在08:00前达到0.9并且没有任何时段越界。第四张费用对比柱状图无序充电下每辆车平均充电费用对比有序充电下的费用。画图代码不算核心我用最基础的plot和bar就能完成% 无序充电结果假设每辆车到达即充满需求电量 P_unorder zeros(N, T); for i 1:N need (SOC_target - SOC_init(i)) * Cap_batt / eta; P_unorder(i, 18:21) min(P_max, need / 4); % 简单均分到18-21点 end total_load_unorder P_base sum(P_unorder, 1); figure; plot(1:T, total_load, b-o, LineWidth, 1.5); hold on; plot(1:T, total_load_unorder, r--s, LineWidth, 1.5); plot(1:T, P_base, k:, LineWidth, 1.2); yline(Cap_trans, g--, 变压器上限); xlabel(时段/h); ylabel(负荷/kW); legend(有序充电总负荷, 无序充电总负荷, 基础负荷, 变压器上限, Location, best); grid on;画图的代码故意写得朴素因为重点是让读者能直接改数据看趋势。5.2 一次典型运行的结果解读在我设定的参数下一个非常典型的运行结果如下表指标无序充电有序充电变化峰值总负荷约310 kW约245 kW下降约65 kW20辆车总充电费用约260元约140元降幅约46%平均每辆SOC达标情况全部达标全部达标无损失充电时间分布18:00-21:00集中于01:00-05:00平移到深谷费用下降的主要原因是算法把约80%的充电电量挪到了凌晨0.3元/度的谷段同时在平段补足剩余需求峰段几乎不充电。这里注意挪负荷不是无限挪因为每辆车有离开时间约束而且变压器容量约束也限制了不能把所有车都堆到同一个凌晨低谷。5.3 权重系数λ和电价修正系数α的灵敏度两个参数值得做灵敏度分析。一个是目标函数里的峰值惩罚权重λλ太小费用最优但峰值可能贴近限制λ太大峰值被压得很低但用户费用会上升因为充电时段的选择范围被压缩了可能被迫在较贵的时段充电以满足时间约束。建议扫描λ从0到2画一条费用-峰值的帕累托曲线找肘部位置作为工程取值。另一个是动态修正系数αα越大电价波动越大用户选择低价时段的动力越强但峰谷价差过大会让系统对不同车辆的充电费用差异拉得很开普通人会产生不公平感。实际工程中α取0.3-0.6比较常见我代码里默认0.5。6. 调试踩坑记录不可行约束、求解警告与参数整定6.1 一上来就infeasible problem怎么办这是YALMIP新手最容易撞的墙。模型逻辑没问题但求解器直接报Problem is infeasible。我梳理一个排查链路第一步看约束是不是过紧。最典型的是某辆车初始SOC太高比如初始60%目标90%电池60kWh需要补18kWh能量按7kW和η0.92约需2.8小时但如果这辆车只有2小时可用窗口就无解。这时要么把SOC_target调低要么把可用时段放宽。第二步看变压器容量约束是否和充电需求存在本质冲突。如果基础负荷峰值已经接近Cap_trans那么充电功率余量很小所有车都要在深夜充电但车辆可用时间窗口又限制了深夜充电时长同样会不可行。这时候要引入切负荷的概念允许部分车辆达不到0.9或者提高Cap_trans到450kVA。第三步用YALMIP的check(Constraints)查看哪条约束的残差最大。这个命令非常有用它会返回每条约束的松弛量残差异常的约束就是问题源头。我在调试时还习惯先跑一个无变压器容量约束的版本如果这个版本可行说明问题大概率出在容量约束与充电窗口的匹配上如果这个版本也不可行那就往SOC初始值、目标值方向查。6.2 YALMIP报solver not found或no solver available出现这个提示通常是YALMIP没有正确识别可用求解器。检查Matlab有没有装Optimization Toolboxlinprog需要它在在YALMIP里运行yalmiptest看哪些求解器可用如果不想装YALMIP可以直接用linprog手工拼矩阵但那样约束代码会非常难维护我不推荐。另外要注意YALMIP在遇到optimize时如果检测到变量里有整数型会优先找MILP求解器如果你把某个变量定义成binvar或intvar但机器上没有Gurobi/CPLEX它可能会fallback失败。这就是为什么我说初学者先把所有变量保持连续。6.3 变量维度和索引越界的经典错误我见过很多次问题出在sdpvar初始化和转置上P sdpvar(N, T)默认全变量矩阵是N行T列要提取第t时段总功率用sum(P,1)得到1×T行向量再转置成列向量和P_base对齐在约束中写P(i, :)表示第i行向量和Avail(i,:)逐元素比较没问题SOC递推时务必把初值单独写不要试图用P(i,1)去更新SOC(i,0)因为索引从1开始。这类错误通常表现为维度不匹配或索引超出数组范围报错信息很直白但要提醒的是YALMIP里有些维度错误不会在建模时报而是等到optimize阶段才暴露为奇怪的内部错误。我的调试习惯是每添加一小块约束就size()打印一下变量维度而不是一口气写完整个模型再查。6.4 合理的参数参考与经验值最后给一组我总结的参考值不是绝对真理但能让你少走弯路充电效率η取0.88-0.95不要取1否则结果偏乐观SOC目标不要设成0.95这类极限值电池BMS通常不建议充满到极限0.9是用户可接受且符合安全习惯的默认值变压器容量约束建议留5%-10%裕量因为实际功率因数和三相平衡不像理想模型那么完美λ的扫描范围从0.1到1.5通常就能看到明显趋势不必拉到很大如果后面要加离散档位0/3.5/7kW把目标函数的线性性质保持住否则求解时间会从0.5秒飙到几分钟那时就要认真考虑求解器升级。我个人实际跑项目的习惯是先写一个不带变压器约束、不带惩罚项的费用最优版本确认SOC递推和功率边界没问题再加变压器约束确认是否有不可行最后加入Z辅助变量做削峰。每加一层就跑一遍问题永远能快速定位到最新加入的那一层里。这种渐进式建模的方式比一口气写完整个优化模型要稳妥得多尤其适合刚接触YALMIP的读者。后面如果你想升级到V2G双向充电、多目标Pareto前沿分析、或者用滚动时域做实时调度都是在当前LP骨架上加变量、加约束而已核心的调试思路完全保持一致。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →