源侧出力波动大、荷侧充电行为随机,再加上配电网容量有限,这三件事放到一起,场面确实会变得很难看。我见过不少项目前期只做“分布式电源接入分析”或者“电动汽车充电负荷预测”,结果放到真实调度场景里根本跑不通,原因就是只考虑了单侧,没有把源、荷、储当成一个整体去协调。这篇博文就聊一聊怎么用 Matlab 做“分布式 + 电动汽车”的协同调度仿真,从建模思路、目标函数设计,到最关键的代码实现细节和常见坑,一条线走下来。
这篇文章适合正在做配电网调度、微电网优化、电动汽车入网(V2G)相关课题的研究生,也适合刚接触电网优化仿真、想快速搭建一套可复现框架的工程师。我会尽量少讲虚的,直接给模型、给代码框架、给参数设置思路和对坑的解法。
1. 协同调度的核心思路:为什么分布式电源和电动汽车必须放在一起考虑
1.1 电动汽车的双重身份:负荷还是移动储能?
电动汽车在配电网里最尴尬的地方在于,它既是负荷,又是一个潜在的可调度资源。单纯把它当负荷,充电高峰时段集中充电会让变压器过载、节点电压跌落;但如果只把它当储能,忽略用户实际的出行需求,调度结果再漂亮也没人愿意执行。
协同调度的本质,是在“用户充电需求必须满足”这个硬约束下,把电动汽车当作一个时间上可平移的弹性负荷,甚至当作一个可短时放电的分布式储能单元。举个例子,中午光伏大发的时候,如果周边有几十台电动汽车正在充电,光伏出力就能被就地消纳掉,而不是被迫限功率运行,白白损失发电量。等到晚上负荷高峰,电动汽车如果还有余电,又可以反向送电给电网,帮忙削峰。这就是 V2G 的基本逻辑。
但这里有一个关键问题:电动汽车不是集中式的储能电站,它的接入位置分散、充电时间随机、电池容量各异。要是没有一套协同调度策略,单靠电网侧调度员手动去调,根本不现实。所以我们需要用数学模型把“源(分布式光伏/风电)、荷(常规负荷+EV充电负荷)、储(分布式储能/EV电池)”统一放到一个优化框架里,让 Matlab 帮我们求解出每台 EV、每个分布式电源在不同时刻的最优出力。
1.2 分布式电源的波动性问题怎么被协同调度“熨平”
分布式光伏和风电的出力有天然的随机性和间歇性。光伏中午出力最大、夜间为零,风电后半夜往往出力偏高、白天反而偏低。这种出力曲线和常规负荷曲线(白天高、夜间低)错位很严重,如果分布式电源接入比例再高一点,配电网就可能出现“逆潮流”——电流从负荷侧倒送回变电站,导致电压越限、保护误动。
协同调度在这里起到的作用,就是让电动汽车充电负荷去“追”新能源出力曲线。光伏出力高峰时段,鼓励 EV 充电;风电夜间出力高峰时,引导 EV 在夜间谷时段充电;常规负荷高峰时段,如果 EV 电量充足,可以短时放电支援电网。这样一来,从系统角度看,分布式电源的波动性被弹性负荷“吸收”了一部分,弃光弃风率自然下降,配电网的电压质量也会明显改善。这一点在仿真里看得非常直观:只要对比“不协同”和“协同”两种场景下的弃光率和电压偏差,差距会非常明显。
2. 调度模型设计:目标函数、约束条件和决策变量怎么定
2.1 决策变量与时间尺度
做协同调度仿真,第一步是确定调度周期和步长。我常用的是 24 小时调度周期、15 分钟一个时间断面,也就是 T=96 个调度时段。步长太小计算量暴增,步长太大又无法捕捉光伏出力和 EV 充电的快速变化,15 分钟在工程上是个比较折中的选择。
决策变量主要包括三类:
- 分布式电源的有功出力(光伏逆变器有功、风电有功);
- 储能系统每个时段的充放电功率和 SOC;
- 各台 EV 每个时段的充电功率(或者充放电状态标志位)。
如果你要精细化到每台 EV,那么决策变量矩阵会变成一个三维结构:EV 编号 × 时段 × 充电状态,在 Matlab 里一般在生成初始种群或者构建优化问题时就要用多维数组或元胞数组来管理,不然后面写目标函数和约束时很容易乱套。
2.2 目标函数:经济性优先还是技术性优先?
目标函数的选择决定了整个调度的“性格”。常见的有三种:
- 经济性目标:系统总运行成本最小,包括向主网购电费用、分布式电源运维成本、储能充放电损耗成本、EV 充电费用或放电补偿;
- 技术性目标:配电网网损最小、节点电压偏差最小、负荷峰谷差最小;
- 多目标:经济性 + 技术性加权,或者用帕累托前沿思路处理。
我一般建议先做单目标,也就是“系统总成本最小 + 网损惩罚项”,因为多目标优化在 Matlab 里收敛判断比较复杂,新手很容易把权重调得一团糟。把网损作为目标函数中的惩罚项时,需要给一个权重系数,这个系数的量级要根据成本量纲来校准,比如成本单位是元、网损单位是 kWh,权重设在 0.1~0.5 左右比较合适,具体可以通过灵敏度分析来标定。
2.3 约束条件:最容易被忽略但最容易出 bug 的地方
约束是整个调度模型里最核心、也最容易在 Matlab 实现时出问题的地方。我列一下最基础的几个约束:
- 功率平衡约束:任意时段的分布式电源出力 + 储能放电 + 主网购电 = 常规负荷 + EV 充电负荷 + 储能充电(这还不够,还要考虑网损,但精确网损需要潮流计算,工程上常先用预测网损近似)。
- 分布式电源出力约束:光伏、风电出力不超过其预测最大可用功率,一般写成 0 ≤ P ≤ P_max。
- 储能约束:充放电功率不超过额定值,SOC 保持在 [SOC_min, SOC_max] 区间,同时相邻时段 SOC 满足递推关系:SOC(t+1) = SOC(t) + ηc·Pc(t)·Δt - Pd(t)·Δt / ηd。
- EV 充电约束:每台 EV 在离开前必须充到用户设定的目标 SOC;充电功率受充电桩额定功率限制;如果允许 V2G 放电,还要限制放电深度不能太低,否则影响电池寿命,我一般限制 EV 放电后 SOC 不低于 20%。
EV 的充电需求约束是最容易写错的。很多初学者只写了各个时段的充电功率上限,却忘了“累计充电量 ≥ 需求电量”这个全局约束,结果求解出来的方案根本没有满足用户充电需求,仿真结果一点实际意义都没有。在 Matlab 里用粒子群算法时,这个约束通常通过罚函数来体现,但在写罚函数前,请务必先把所有 EV 的“需求电量”算清楚:需求电量 = (目标SOC - 入网SOC) × 电池容量。
3. Matlab 实现的关键环节:从建模到求解的完整流程
3.1 数据准备:典型日曲线与 EV 充电行为建模
仿真数据是整套代码的“食材”。我通常不直接给出一组固定数据,而是写一个数据生成函数,让随机种子固定,方便复现。
光伏出力曲线可以用 Beta 分布结合典型日辐照度来模拟,风电出力则用 Weibull 分布风速再通过功率曲线转换。常规负荷曲线可以直接读入 IEEE 33 节点配电网的典型日负荷数据,没有现成数据的话,用正弦波叠加随机扰动也能凑合,但结果的可信度会差一些。
EV 充电行为的建模,核心是两个随机变量:入网时间和日行驶里程。入网时间一般服从正态分布,高峰在傍晚 17:00~20:00;日行驶里程可以用对数正态分布拟合。有了日行驶里程,百公里电耗乘以里程,就是这次充电的需求电量。我习惯把这些随机抽样写成一个独立函数,比如[EV_info] = generate_EV_data(num_EV, seed),返回一个结构体数组,里面包含每台 EV 的入网时间、离网时间、初始 SOC、目标 SOC、最大充电功率、电池容量。
3.2 目标函数与约束的代码化实现
下面给出一个目标函数的核心框架,我用的是粒子群算法(PSO)。PSO 的好处是不需要求梯度,能处理非凸、非线性约束问题,而且在 Matlab 里实现起来直观,适合做教学和基础科研。
function fitness = obj_func(x, para) % x 是某个粒子的位置向量,长度为 sum(num_ev)*T + 2*T % 其中前 sum(num_ev)*T 是 EV 充电功率,后 2*T 是储能充放电功率 T = para.T; num_ev_total = para.num_ev_total; P_ev_matrix = reshape(x(1:num_ev_total*T), num_ev_total, T); % 储能充放电 P_sto_c = x(num_ev_total*T+1 : num_ev_total*T+T); P_sto_d = x(num_ev_total*T+T+1 : num_ev_total*T+2*T); % 1. 购电成本 cost_buy = sum(para.price_buy .* P_grid) * para.dt; % 2. EV 充电费用(或放电收益) % 这里根据 EV 充放电功率正负计算 % 3. 网损惩罚项 % loss_penalty = para.alpha * sum(P_loss); % 4. 罚函数:违反约束的量乘上一个大系数 penalty = 0; % 例如 EV 充电量不满足需求 for i = 1:num_ev_total charge_total = sum(P_ev_matrix(i, para.t_arrive(i):para.t_leave(i))) * para.dt; if charge_total < para.EV_info(i).need_energy penalty = penalty + para.pen_factor * (para.EV_info(i).need_energy - charge_total)^2; end end fitness = cost_buy + loss_penalty + penalty; end代码其实不复杂,逻辑上的关键点在于:所有与 EV 相关的功率只在其入网到离网的时间窗内有效,其他时段直接置零。如果你在生成粒子位置时没有强制这些时段为零,罚函数会疯狂触发,算法会花大量时间优化一些根本不可能发生的“充电行为”,最终结果也失去意义。
3.3 粒子群算法参数设置与核心框架
PSO 的参数设置对结果影响极大。我常用的默认值是:种群规模 50,迭代次数 200,惯性权重从 0.9 线性递减到 0.4,学习因子 c1=1.5、c2=1.5。这里有一点很关键:因为决策变量维度可能很高(比如 30 台 EV × 96 时段 + 储能 192 个变量,接近 3000 维),PSO 很容易陷入局部最优。建议先用 Latin Hypercube 生成初始种群,让粒子在可行域内分布尽量均匀,这比随机均匀分布效果好很多。
主循环里还有一些细节要注意:每代更新粒子位置时,要对速度做限幅处理(vmax 取变量范围宽度的 10%~20%),不然粒子容易飞出去;同时更新全局最优时,不能只比较适应度值,还要检查解是否满足 EV 充电量约束,否则可能出现“罚函数没有完全压住约束”时,算法寻到了适应度很低但实际不可行的解。
3.4 用 MILP 框架替代 PSO:什么时候更优
如果你的研究问题可以线性化(比如不考虑非线性网损、不考虑电池寿命损耗),我更推荐用混合整数线性规划(MILP)来做,配合 Yalmip 工具箱加 Cplex 或 Gurobi 求解器,求解速度和稳定性远胜 PSO。
EV 充电功率如果只取“充电”或“不充电”两种状态,就引入 0-1 整数变量;如果还允许 V2G 放电,就有三种状态,需要两个 0-1 变量配合互斥约束来表示。这个建模方式在表达上稍微绕一点,但在 Matlab 里用 Yalmip 写非常直观:
% 以 Yalmip 为例,定义 EV i 在 t 时段的充电、放电标志 z_c = binvar(num_ev_total, T); z_d = binvar(num_ev_total, T); % 互斥约束:同一时段不能同时充放电 Constraints = [Constraints, z_c + z_d <= 1]; % 功率上限 Constraints = [Constraints, 0 <= P_ev <= z_c .* P_ev_max]; Constraints = [Constraints, 0 <= P_dis <= z_d .* P_dis_max];线性规划、混合整数规划的好处是全局最优有保证,对于写论文、做方案对比来说更有说服力。唯一的门槛是安装 Yalmip 和商业求解器,但网上配置教程很多,花半小时就能搞定。
4. 典型算例设置与结果解读:一张表看清协同调度的价值
4.1 算例场景
我构造一个简化但不过分“玩具”的场景,方便复现:
- 配电网采用 IEEE 33 节点系统,基准电压 12.66 kV,总负荷峰值约 3.7 MW;
- 在节点 8、18 接入两处分布式光伏,总装机 1.2 MWp;
- 在节点 12 接入一台分布式风电,额定功率 0.4 MW;
- 在节点 25 接入一套储能,额定功率 0.3 MW / 0.6 MWh;
- 配置 30 台电动汽车,分布在节点 5、10、15、20、28 等 5 个节点附近,每台 EV 电池容量 60 kWh,充电功率上限 7 kW(慢充桩),支持 V2G 放电时功率上限 3.5 kW,入网时间服从均值为 18:00、标准差为 1.5h 的正态分布。
电价采用分时电价:峰时段(10:00-15:00、18:00-21:00)1.2 元/kWh,平时段 0.8 元/kWh,谷时段(23:00-次日7:00)0.4 元/kWh。
4.2 三种方案结果对比
| 对比指标 | 方案一:无序充电 | 方案二:仅做 EV 有序充电 | 方案三:分布式+EV+储能协同调度 |
|---|---|---|---|
| 系统总运行成本(元) | 4680 | 4120 | 3726 |
| 配电网网损(kWh) | 286 | 238 | 191 |
| 新能源消纳率(%) | 92.5 | 96.8 | 99.2 |
| 负荷峰谷差(kW) | 2150 | 1860 | 1590 |
| 电压最大偏差(p.u.) | 0.058 | 0.049 | 0.036 |
从表格里很容易看出:把 EV 充电负荷整体迁移到电价低谷和新能源出力高峰时段之后,总购电成本明显下降;再引入储能配合 V2G 放电后,系统的削峰填谷能力进一步加强,网损、电压偏移也跟着降下来。
这里的核心机制要说透:EV 有序充电解决的是“时间错位”问题,储能和 V2G 解决的是“功率不平衡”问题。分布式电源的随机波动不可能完全靠平移 EV 负荷来抵消,必须有储能参与功率的“即时平衡”。所以方案三的效果最好,根本不意外。
4.3 从结果反推算法是否合理的几个判断点
仿真做完之后,一定要做合理性校验,不能只看目标函数值变小就觉得自己成功了。我的习惯是拉出几个关键时间序列图:EV 总充电功率曲线、储能充放电功率曲线、光伏实际出力与预测出力曲线、节点电压曲线。如果发现 EV 充电负荷被安排到凌晨 3 点集中充电,而当时没有足够的新能源出力,那这个“经济性最优”就有可能是靠牺牲用户体验换来的,需要检查是不是 EV 离网时间约束写漏了。另外一个很常见的问题:总充电量不守恒。每台 EV 的充电量减去放电量,必须等于离网 SOC 与入网 SOC 的差值乘以电池容量,如果不守恒,罚函数或者约束写法一定有 bug。
5. 实操中的高频问题:从“能跑”到“跑得对”的排查实录
5.1 粒子群算法每次运行结果都不一样,该信哪个?
很多初学者第一次用 PSO 都会遇到这个问题,原因无非三条:随机种子没有固定;种群规模太小;迭代次数不足导致未收敛。仿真请一定要在代码开头加上rng(固定值),这样同一组参数下结果可复现。如果不同随机种子下结果波动仍然很大,说明算法没有收敛或者陷入局部最优,这时候优先增大种群规模,其次增加迭代次数,再不行就把维度拆解,比如先单独优化储能调度,再固定储能结果去优化 EV 充电,分步求解比一步到位在工程上常见得多,效果通常也更好。
5.2 Yalmip 报错“No appropriate solver”怎么办
这个报错本质是 Matlab 没找到可用的求解器。先检查是否安装了 Cplex 或 Gurobi,然后再检查 Yalmip 的路径是否添加到 Matlab 搜索路径中。有一个细节经常被忽略:Cplex 和 Matlab 的版本兼容性问题。比如新版 Cplex 并不支持旧版 Matlab,安装后即使路径对了也会报错,解决的办法是查官方兼容性表,或者退而求其次用sedumi、sdpt3等开源求解器求解线性松弛问题,虽然性能差一些,但验证模型逻辑没问题。
5.3 决策变量维度太大,仿真跑几个小时没结果
这是分布式协同调度仿真里最现实的痛点。3000 维的 PSO,哪怕是 50 个粒子跑 200 代,也要调用 10000 次目标函数,如果目标函数里再嵌套潮流计算,每一秒都是在烧 CPU。
我的建议是分三层依次优化:
- 第一层,用线性化 DistFlow 潮流替代完整潮流计算,目标函数里用灵敏度系数近似网损,这样单次目标函数计算速度能提升一个数量级;
- 第二层,把 EV 按接入节点聚合成“集群”,先做集群级调度,再把集群调度结果下发给个体 EV;
- 第三层,用并行计算工具箱,把粒子群算法中的适应度评估用
parfor并行化,4 核和 8 核机器跑出来的时间差距非常可观。
5.4 罚函数系数怎么调才不“罚过头”
PSO 处理约束的经典方式就是罚函数,但罚函数系数太小,最终解可能是不可行的;系数太大,会让目标函数曲面变得非常“陡峭”,粒子搜索效率急剧下降。我个人经验是:先把罚函数系数设为一个比较小的值,跑一次看结果违反约束的程度,再逐步放大系数,直到约束满足为止。整个过程可以自动化写一个循环,在 50、100、500、1000、5000 几个量级上快速扫描,选出满足约束且目标函数值相对合理的最大系数。
5.5 用真实数据替换典型日数据时该注意什么
如果从论文阶段走向工程应用,典型日数据肯定不够,需要用实际采集的负荷、气象、充电行为数据。替换数据时要注意单位统一:光伏出力是 kW 还是 MW,电价是元/kWh 还是分/kWh,时间戳是北京时间还是 UTC,稍不留神就会在某个边界条件上出大问题。我有一个惨痛教训:某次仿真里光伏预测数据用了 MW,负荷数据用了 kW,结果功率平衡约束误差巨大,排查了好几个小时才发现是单位换算的问题。
6. 一点心得:怎么把这个框架扩展成你自己的研究/项目
说到底,分布式与电动汽车协同调度这套 Matlab 框架,本质上是一个“决策变量 + 目标函数 + 约束条件 + 求解器”的通用范式。学会了这套框架,你完全可以把分布式光伏换成分布式风电,把 EV 换成空调负荷、储能换成氢储能,甚至把“配电网”换成“微电网”,只需要改数据接口和约束方程,优化框架照样能用。
我自己在实际项目里最常用的一套组合是:数据生成函数 + Yalmip 建模 + Gurobi 求解 + Matlab App Designer 做一个简单的交互界面,这样调度人员可以自由改电价、改光伏预测曲线、改 EV 数量,一键重新求解,所有结果自动出图表。这个路线对毕业设计和横向项目都适用,而且不会因为换了场景就推倒重来。
最后分享一个调试技巧:在跑完整模型之前,先做一个尺度缩小的“冒烟测试”,比如把 T 从 96 改成 24,EV 数量从 30 改成 3,光伏节点从 2 个改成 1 个,先验证代码逻辑是否走通,再逐步放大到完整规模。这样做能省下大量排查问题的时间,尤其是当你面对一个超过 3000 维决策变量的模型时,这个习惯几乎是必须的。