☰
可再生能源发电与电动汽车协同调度:从论文公式到可运行代码的复现手记
2026/10/10 19:06:44 网站建设 项目流程

可再生能源发电与电动汽车协同调度:从论文公式到Matlab可运行代码的完整复现手记

搞过论文复现的朋友都知道,最难的不是看懂公式,而是把纸上的一堆集合、上下标、约束条件变成能跑出结果的代码。尤其是电力系统优化调度这种方向,模型往往一个比一个复杂,数据一堆,求解器一调就是大半天,跑出来的结果还要跟论文原文对得上。我花了两周时间完整复现了一篇硕士论文里的“可再生能源发电与电动汽车协同调度策略”,用的就是Matlab。今天把这套思路和踩过的坑完整记录下来,项目涉及风电、光伏、电动汽车集群充放电、微电网经济调度,核心是让可再生能源出力和电动汽车充电负荷在时间上“打配合”,实现削峰填谷和运行成本最小化。

这个方向属于典型的能源互联网交叉课题,适合电力系统、新能源、智能网联汽车方向的硕博生,也适合做微电网优化调度、V2G策略、需求响应的同行参考。如果你手里也有类似题目,但对怎么把论文中的数学模型转成可复现的代码感到头疼,这篇文章应该能帮你省下不少时间。先说结论:论文复现最核心的能力不是写代码,而是把论文里的“隐语”翻译成程序逻辑,这一步做对了,后面跑通只是时间问题。

1. 问题拆解:搞清楚协同调度到底“协同”了什么

1.1 协同调度的本质:让发电侧和用电侧双向互动

传统电力调度是“源随荷动”,也就是发电跟着用电走。但可再生能源(风电、光伏)有个天然毛病——出力不可控,太阳不出来、风不吹,发电就不稳定。这时候如果还死板地让电网去平衡,要么弃风弃光浪费资源,要么就得调用昂贵的备用火电机组。电动汽车不一样,它既是负荷又是“移动储能”,在停着不用的时候,电池里存着电,完全可以响应系统需求调整充电时段,甚至在电价高的时候反向送电(V2G)。

所以这篇论文要解决的协同问题可以概括成一句话:在满足用户充电需求的前提下,通过灵活调整电动汽车的充放电时段和功率,去平抑可再生能源出力的波动,最大化消纳清洁能源,同时降低系统总运行成本。这里面的核心矛盾是“发电的时间特性”和“用电的时间特性”不匹配,而电动汽车恰好是这两者之间的调节器。

1.2 系统场景设定:单微网还是多微网?

论文标题里没有明说,但从常见研究范式和参数设定来看,基本都建立在单微电网或区域配电网的框架下。我做复现时的基础场景设定如下:

  • 系统内包含:风电机组、光伏阵列、常规负荷、电动汽车集群、储能装置(可选)、以及上级电网交互。
  • 调度周期通常取24小时,时间步长取1小时,部分精细化研究会取15分钟步长。
  • 电动汽车用户提前上报出行计划(到达时间、离开时间、期望电量),调度中心据此生成各时段充放电计划。

我建议你在复现任何同类论文时,第一步做的事情不是写代码,而是把论文里的系统拓扑图转换成一张表格,把“谁和谁相连、谁在哪个节点、能量往哪流”列清楚。这个动作能帮你避免后面建模时漏掉约束条件。

1.3 从论文到代码的思路转换

论文里通常会写“建立以系统总运行成本最小为目标函数的优化调度模型”,但落到代码里,你需要先回答三个问题:

  • 决策变量是什么?(各时段电动汽车充放电功率、储能充放电功率、与上级电网交互功率)
  • 目标函数怎么表达?(所有成本项的线性组合,通常包括购电费用、弃风弃光惩罚、电池损耗折算等)
  • 约束条件有哪些?(功率平衡、充放电功率上下限、电池SOC范围、用户充电需求等)

如果这三个问题答不清楚,代码写多少删多少都是正常的。我复现时把这三条写成注释放在代码文件头部,后面每一步修改都对照着来,基本没有再乱过。

2. 数学模型:把目标函数和约束条件逐条翻译成程序语言

2.1 目标函数:不止是成本最小那么简单

论文里最常见的目标函数形式是:

[ \min \sum_{t=1}^{T} \left[ c_{grid,t}P_{grid,t} + c_{EV}P_{EV,t} + \lambda_{curtail}(P_{curtail,t}) \right] ]

其中第一项是向电网购电的成本,第二项是电动汽车充放电的运行成本,第三项是弃风弃光的惩罚费用。在Matlab里实现时,我的做法是把目标函数写成带有决策变量的线性表达式,然后用linprog或YALMIP直接求解。如果你用的是YALMIP,目标函数可以写得很接近数学公式:

objective = sum(repmat(c_grid, T, 1) .* P_grid) ... + sum(repmat(c_ev, T, 1) .* P_ev) ... + sum(lambda_curtail .* P_curtail);

注意:很多论文把电动汽车的电池损耗成本也放进目标函数,通常用一个成本系数乘以充放电功率的绝对值来表示。但绝对值在优化问题里是非线性的,处理起来要格外小心,后面我会详细讲怎么处理。

我个人在复现时建议优先采用线性目标函数,因为线性规划的求解速度快、全局收敛性有保证,这对跑通整条链路来说非常重要。如果你想复现的论文用了非线性目标函数,也要先尝试把它线性化,实在不行再考虑调用遗传算法或粒子群这类启发式算法。

2.2 可再生能源出力建模:不要拿到数据就塞进代码

风电和光伏的出力曲线多数论文会直接给出一组典型日数据,也有不少论文用威布尔分布或Beta分布来模拟随机出力。这里我踩过一个坑:拿到论文里的风光出力曲线后,我直接就把数据当成确定值代入模型,结果跑了半天,系统一直报功率不平衡的警告。

原因后来查明了。原来论文里给出的是“预测出力”曲线,而实际建模时需要考虑预测误差,通常的做法是在预测值上叠加一个正态分布随机扰动。正确的复现思路是:

  • 基线场景用预测出力数据(论文表格或图里读出来的数值)。
  • 不确定性场景用蒙特卡洛抽样生成多条出力场景,再对所有场景求期望成本。
  • 如果论文侧重确定性调度,那就不加扰动,保持与原文一致。

你复现时一定要先搞清楚论文用的是确定性方法还是随机优化方法,否则后续对结果会怀疑人生。

2.3 电动汽车集群建模:最难但最核心的一块

电动汽车协同调度的难点在于电动汽车数量很多、行为各异。如果把每辆车都作为独立个体建模,变量数量大得惊人,求解速度会直线下降。论文里普遍采用的做法是“集群等效”或者“蒙特卡洛抽样+聚类”,把大量车辆按到达时间、离开时间、初始SOC等特征聚成几个代表性群体。

我复现时用了5类电动车群,每类的关键参数如下:

电动车群编号车辆数到达时段离开时段初始SOC区间目标SOC
类型1(工作日通勤)20008:00-09:0017:00-18:000.3-0.50.9
类型2(晚间充电为主)30018:00-20:0007:00-08:000.2-0.40.95
类型3(随机到达)150全天随机随机0.3-0.60.8
类型4(日间快充)10010:00-14:0014:00-16:000.1-0.30.85
类型5(长停车辆)25007:00-08:0022:00-23:000.4-0.51.0

每一类电动车群建立如下约束:

  • 各时段充放电功率必须在充电设施允许的范围内。
  • 电池SOC必须保持在上下限之间,比如0.2到1.0。
  • 调度结束时SOC必须达到用户期望值(目标SOC)。
  • 同一辆车不能同时充放电(这个约束在处理集群等效模型时需要额外小心,通常通过限制充电功率和放电功率不同时为正来实现)。

在Matlab里,SOC更新方程可以这样表达:

SOC(:, t+1) = SOC(:, t) + eta_ch * P_ch(:, t) / Cap - P_dis(:, t) / (eta_dis * Cap);

其中eta_ch是充电效率,eta_dis是放电效率,Cap是电池容量。

2.4 约束条件:功率平衡是最不能出错的一条

功率平衡约束是所有电力系统优化模型的灵魂,形式很简单:

[ P_{grid,t} + P_{wind,t} + P_{pv,t} + P_{dis,t} = P_{load,t} + P_{ch,t} + P_{curtail,t} ]

翻译成代码就是每一时刻所有功率的代数和不等于零就会崩。我在复现初期最常犯的错误是忽略了变量取值范围的问题——功率平衡等式一直满足,但充放电功率把设备上限顶破了,这就不是模型逻辑错了,而是少写了边界约束。所以写约束时一定要把以下几条全部配齐:

  • 每个时段与上级电网交互功率的上限;
  • 充电桩的充放电功率上限;
  • 电池SOC上下限约束(这个几乎每个模型都要写);
  • 弃风弃光量的非负约束和上限约束;
  • 电动汽车用户充电完成度约束。

用YALMIP表达约束集合时,推荐所有约束统一放进一个cell数组:

Constraints = []; Constraints = [Constraints, P_grid_min <= P_grid <= P_grid_max]; Constraints = [Constraints, 0 <= P_ch <= P_ch_max]; Constraints = [Constraints, 0 <= P_dis <= P_dis_max]; Constraints = [Constraints, P_ch .* P_dis == 0]; Constraints = [Constraints, SOC_min <= SOC <= SOC_max]; Constraints = [Constraints, SOC(:, end) >= SOC_target];

注意第三行的互斥约束(充放电不同时进行)在非凸问题里会让求解器崩溃。线性化方法是用一个二进制变量乘上各自的最大值,比如P_ch <= binary * P_ch_max,P_dis <= (1-binary) * P_dis_max。这样问题变成了混合整数线性规划(MILP),YALMIP会调用intlinprog求解,速度也很快。

3. 实操过程:Matlab代码的完整实现路线

3.1 工具选型:为什么我推荐YALMIP而不是纯手写linprog

直接调linprog写标准形式会让人崩溃,因为电力系统优化模型动辄几十上百个变量和约束,每一个约束都要手动拼成矩阵A、b、Aeq、beq,错一个下标就全线崩盘。YALMIP的优势在于它能把数学表达式的逻辑直接映射成代码,你不需要手动拼矩阵,只要把约束和目标函数写清楚,它会自动帮你整理成求解器需要的标准形式。

如果你不想装YALMIP,用Matlab自带的Optimization Toolbox也可以,但代码可读性会差很多,不利于后期改参数、加约束。我强烈建议你装YALMIP并搭配至少一个商用求解器,比如Gurobi或有学术许可的CPLEX,实在没有就用Matlab自带的intlinprog,也能处理中小规模问题。

3.2 数据准备:论文里的数据要自己会“造”

说到数据,这是论文复现里最让人头疼的地方。很多论文不会把所有原始数据都写清楚,特别是电动汽车的参数、充电桩功率、分时电价表,你得从图表中去反推,实在反推不出来就只能按常理自己设定。我复现时把参数分成两类:

  • 硬性参数(论文明确给出的):风光出力曲线、负荷曲线、分时电价、购电上限。
  • 软性参数(论文没写全、但必须合理的):电动汽车数量、电池容量、充放电效率、SOC上下限、目标SOC。

软性参数的处理原则是尽量贴近常见工程经验值,比如电池容量取40kWh,充电效率取0.95,放电效率取0.9,充电桩最大功率取7kW,V2G放电功率取3.5kW。数据准备好了,下一步是把它封装成结构体,方便代码各模块共用。

% 基础参数 para.T = 24; % 调度时段数 para.dt = 1; % 时间步长(小时) para.Cap = 40; % 电池容量(kWh) para.eta_ch = 0.95; % 充电效率 para.eta_dis = 0.9; % 放电效率 para.P_ch_max = 7; % 最大充电功率 para.P_dis_max = 3.5; % 最大放电功率 para.SOC_min = 0.2; para.SOC_max = 1.0;

分时电价我采用峰谷平三段式设定,因为在大多数论文里这既能体现削峰填谷的优化空间,又不会因为过渡段太多让结果难以解读。峰时段10:00-15:00和18:00-21:00,平时段07:00-10:00、15:00-18:00、21:00-23:00,谷时段23:00-07:00。这个设置直接决定了电动汽车会不会在谷时段集中充电,也决定了V2G反向送电是否有经济性。

3.3 优化模型求解:从写代码到出结果的全流程

我把求解流程拆成了5个步骤,这里每个步骤都已经跑通验证过,你可以直接参考:

第一步:定义决策变量。用YALMIP定义矩阵变量,行是时段,列是电动车群。充电功率、放电功率、与电网交互功率、弃风弃光功率都定义成sdpvar。

第二步:写目标函数。目标函数里的所有成本项都按论文公式对应。注意分时电价是一个T维列向量,充电功率是T×N矩阵,做矩阵乘法前先用repmat把电价向量复制成N列,再逐个对应。

第三步:写约束条件。把前面2.4节里提到的所有约束一项不漏地写上。尤其注意SOC递推方程是等式约束,必须有初始SOC作为起点。

第四步:调用求解器。用optimize(Constraints, objective, options)一行命令求解。设置求解器为intlinprog,或指定用Gurobi。求解完成后检查输出的problem标志是否为0,非零说明求解失败或结果不可信。

第五步:结果后处理。把优化出来的变量导出,画成功率平衡图、电动汽车充放电计划图、SOC变化曲线图。

% 在这里给出一个完整可运行的极小示例(不含数据加载部分) ops = sdpsettings('solver', 'intlinprog', 'verbose', 1); result = optimize(Constraints, objective, ops); if result.problem == 0 P_grid_opt = value(P_grid); P_ch_opt = value(P_ch); P_dis_opt = value(P_dis); SOC_opt = value(SOC); disp('求解成功'); else disp(['求解失败,错误代码:', num2str(result.problem)]); end

3.4 结果可视化:图要画到能直接放进论文里的水准

论文复现最后一步是画图。别小看这一步,图的漂亮程度直接决定了你复现成果的说服力。我用Matlab绘图时坚持三条原则:

  • 功率平衡图用堆叠面积图,能清楚看出每个时段各类电源的出力构成和负荷曲线之间的gap是怎么被填上的。
  • 电动汽车充放电计划用柱状图,正负分开,可以看出充电和放电窗口。
  • SOC曲线用折线图,多类电动车群用不同颜色,同一张图里既比大小又看趋势。

绘图代码里我经常用area、bar、plot三个函数轮番上阵。一张高质量的配图,配上对比实验表格(协同调度前 vs 协同调度后),基本就是完整的复现结果。记得出图时用exportgraphics导出为PDF或EPS格式,分辨率高,投稿和写报告都拿得出手。

4. 常见问题排查:我在复现过程中踩过的七个坑

4.1 求解器报错“Infeasible problem”怎么办

这是最让人崩溃的错误,你把模型撸了半天,结果告诉你没有可行解。我排查这个问题的顺序是:

  • 检查功率平衡约束等式两端单位是否一致,常见错误是把kW和kWh混用了。
  • 检查SOC递推方程是否满足能量守恒,充电量减去放电量再折算损耗后,是否能满足最终SOC要求。
  • 检查电动汽车的到达时间和离开时间窗口是否合理,比如一辆车早上8点到达、下午5点离开,结果你让它在凌晨3点充电,那必不可能。
  • 先用一个缩小版本的模型(比如把电动车群从5类缩减成1类,把T从24缩减成12)测试,逐步扩大,锁定是哪个约束造成不可行。

在论文复现类项目里,“不可行”大概率不是求解器的问题,而是模型本身有矛盾。我见过很多人反复调求解器参数,其实根本方向就错了。

4.2 结果不合理:优化完成本反而更高

有一种情况仿真也能跑通,但结果明显不合理,比如优化后的购电费用比不优化还高。这时先看是不是目标函数里的符号写错了,或者分时电价与功率方向对应错了。

比如目标函数里写cost = grid_price * P_grid,但P_grid的正负方向约定如果和功率平衡约束里的方向不一致,就会出现“该系统向电网售电反而算作购电成本”的乌龙。我在代码注释里对每个变量的正负方向都做了明确标注,比如P_grid>0表示从电网购电,P_grid<0表示向电网售电。这个约定必须贯穿所有公式和代码。

4.3 YALMIP安装和求解器连接问题

YALMIP的安装本身很简单,只要把目录添加进Matlab路径就行。但求解器连接经常出幺蛾子,尤其是Gurobi这种商用求解器。你需要在YALMIP的官方文档里找到对应的求解器版本兼容表,严格按表来选求解器版本。我一开始Gurobi装了最新版,结果YALMIP识别不了,折腾了一天才发现版本不兼容。建议没拿到学术许可证的话,就用Matlab自带的intlinprog,对小规模案例完全够用。

4.4 计算时间爆炸:优化模型跑一小时不出结果

如果模型规模不大,求解时间却很长,多半是把问题写成了非线性。MILP求解速度非常快,通常几秒到几十秒。一旦出现非线性约束或者非凸项,求解时间会指数级增长。检查下目标函数或约束里是否有绝对值、乘积项、分段函数,有的话全部转换为线性表达。绝对值可以通过引入辅助变量和一组不等式来处理,乘积项如果发生在连续变量和二进制变量之间,也可以线性化。永远记住:能写成MILP就不要写成分NLP。

4.5 论文结果对比不上:差一点就是差很多

复现完成后,拿你的结果和论文里的图对比,发现曲线形状差不多,但具体数值对不上。这种情况我经历了很多次,最后总结出三条可能原因:

  • 数据颗粒度不同,论文可能是15分钟一个点,你按1小时跑,结果自然不同。
  • 边界条件设定不同,比如论文可能假设电动汽车必须100%满足出行需求,你设的是95%。
  • 目标函数中某个成本系数取了不同数量级,比如电池损耗成本系数论文里可能没有明确给出。

遇到这种情况,不要强行把参数凑到和论文一模一样,重点在于验证论文方法在你重建的模型上能不能产生预期的调度效果。只要效应趋势一致(比如协同调度显著降低了负荷峰谷差、提高了风电利用率),复现就算成功了。

4.6 多场景蒙特卡洛计算太慢:先跑单场景还是先跑多场景

如果你复现的论文里有不确定性分析,大概率会涉及蒙特卡洛模拟。最开始不要直接跑上千个场景,先用一个场景验证确定性模型没问题,再写循环叠加多场景。每个场景独立求解然后把目标值平均,这个过程用Matlab的parfor并行循环能大幅节约时间,我实测下来4核并行能缩短约50%的耗时。跑之前记得先delete(gcp('nocreate'))清理已有并行池,避免内存爆掉。

4.7 代码报错却不知道错在哪行:调试技巧

Matlab调试设置成出错时进入调试模式是个好习惯,但更重要的是你会不会看YALMIP变量类型的报错。YALMIP里sdpvar变量和数值矩阵做比较时,会遇到“无法将sdpvar转换为逻辑值”的报错。这种情况往往出在你试图用if语句判断约束条件成立与否时。记住:约束条件用等式和不等式直接加进Constraints集合,不要先判断再写入。

调试时多用size()检查变量维度,我最常犯的错误就是repmat时维度对不上,矩阵乘法报维度错误。给每个变量起名时带上维度注释,比如P_ch_5types_24h一眼就知道是24×5的矩阵,后续排查维度问题快得多。

5. 扩展方向与个人几点体会

5.1 从确定性调度走向鲁棒优化

复现完确定性协同调度,如果你想自己往前走一步,推荐把模型从确定性改成鲁棒优化。做法很简单:把风光出力的预测值替换成一个不确定区间,目标函数变成最坏场景下的成本最小化。你的变量定义和约束框架基本不动,只需要新增不确定变量集和鲁棒对等约束。Matlab里YALMIP对鲁棒优化有内建支持,定义不确定变量后直接写uncertain命令,求解器会自动处理。

这一步的好处是模型更贴近工程实际,而且答辩或写论文时讲“我不仅复现了基础模型,还做了鲁棒性扩展”,加分效果相当明显。

5.2 把电动汽车当成“移动储能”参与调频辅助服务

论文里的协同调度主要还是经济调度层面,但实际工程中新能源占比升高后,电网最缺的是调频能力。电动汽车响应速度快、分布式分布广,是非常理想的调频资源。你可以尝试把本文的调度模型扩展成“日前经济调度+日内实时调整”的两阶段结构,日前决定充放电计划,日内根据频率偏差信号做毫秒级响应。这个方向上Matlab里的Simulink可以做,但计算量上台阶了,适合做毕业设计的第二阶段。

5.3 关于复现工作本身,几点个人体会

论文复现这件事,说难很难,说简单也简单,关键看你愿不愿意沉下心去把每一个公式翻译成代码。我复现这篇论文最大的收获不是跑通一个模型,而是培养了一种“模型-算法-代码”的三层映射直觉。以后再拿到任何优化类论文,我会本能地先在脑内拆解决策变量、目标函数和约束条件,而不是上来就google代码。这种能力只能靠一次次调试和排查慢慢攒出来。

另一个体会是,数据不规范、参数不全才是论文复现真正的坑,绝大多数时间花在数据整理和参数合理性验证上,纯写代码的时间反而不长。所以如果准备做一个论文复现,先花半天时间把数据的单位、范围、含义全部确认清楚,再动手写模型。这个习惯能帮你避免大量无意义的重构。

最后说一句,Matlab的生态对于这种中规模优化问题仍然是非常合适的。YALMIP加Gurobi的组合,配合Matlab强大的绘图和后处理能力,一套完整流程下来从零到出结果,我用了一周左右,期间还包含各种摸鱼和排查问题的时间。如果你也在做同类型题目,可以先按本文的框架把模型搭起来,然后再根据你自己的论文需求去调整场景细节。跑通的那一刻,你会觉得之前所有卡壳都是值得的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询