搞电力系统优化的人,这两年对V2G应该都不陌生。所谓V2G,就是让电动汽车不只是从电网“取电”,还能在用电高峰把动力电池里的电“反哺”回电网。这套“低谷充电、高峰放电”的逻辑,听着很简单,真正落地却有一堆麻烦事。我这个项目做的就是其中最核心的“实时调度”环节——用Matlab写了一套完整的调度策略,模拟几十辆电动汽车在不同时段的充放电行为,在满足用户出行需求的前提下,帮运营商少花钱、帮电网削峰填谷。
做这个项目之前我踩了不少坑,最典型的是:很多人一上来就写优化算法,结果模型复杂到跑不动,或者干脆忽略了电池寿命损耗,导致调度结果“理论很美、实际不能看”。这篇就把我的整体思路、数学建模、Matlab代码结构以及排查问题的方法全部摊开讲清楚。代码主体用Yalmip建模、接入求解器求解,适合做科研仿真,也适合给实际微网系统做策略原型。
1. 项目概览:V2G实时调度到底在做什么
1.1 V2G技术的基本逻辑
V2G技术本质上做的是“能源双向互动”。电动汽车不再被视为单纯的用电负荷,而是当成一块移动的分布式储能。电网需要削峰时,车辆放电;电网有富余风电、光伏时,车辆吸收电能。
我常说,一辆私家电动汽车的动力电池,容量普遍在40到80千瓦时之间,这比一台家用储能电池的容量还大。如果一个小区停车场里有50辆电动汽车,聚合起来就是好几兆瓦时的“虚拟电厂”,响应速度还比传统火电快得多。
但这个逻辑落地有一个致命难点:车是随时移动的,用户随时可能开走。不能像固定储能电站那样,按部就班地安排充放电计划。所以必须做“实时调度”——根据当前时刻车辆接入状态、剩余电量、用户设置的离开时间,以及电网当下的负荷和电价信号,动态决定每辆车接下来该充还是该放。
1.2 实时调度与传统调度的区别
传统调度,也就是“离线计划调度”,是在一天开始前,假设所有车辆的接入时段、出行需求、电价曲线全部已知,集中求解出一份全天充放电计划表。这种方式在仿真里很好用,但在现实中会遇到以下问题:
- 车辆实际到达、离开时间,和预测值偏差很大;
- 用户临时取消或新增充电需求;
- 实时电价波动,与日前预测曲线不一致;
- 某辆车接入后才发现电池健康状态不佳,无法执行大功率放电。
实时调度则采用“滚动优化”的思路,把全天的调度问题,切成一个个时间窗口,每隔一段固定时间重新优化一次,每次都根据最新的状态信息做决策。这样既保留了优化策略的全局视野,又能应对突发变化。我在这个项目里把滚动周期设成15分钟一次,这也是很多实际微网项目常用的节奏。
1.3 项目目标与应用场景
我做这个项目时,给自己定了三个具体目标:
- 用户侧不越界:每辆车的SOC在用户离开时不能低于用户设定的最小电量,这是硬约束。
- 运营成本最省:在满足用户需求的前提下,利用峰谷电价差做充放电套利,降低整个停车场的净购电成本。
- 电网不过载:停车场变压器容量有限,所有车辆的充放电总功率,必须控制在变压器可承受范围内。
应用场景我设定在某公司园区的员工停车场,工作日早进晚出,车辆规律性强,且园区屋顶有光伏。这个场景很适合做V2G演示——白天光伏出力大时给车充电,傍晚用电高峰时让车放电回馈园区电网,夜里谷电时段再补充电量。
2. 调度策略的数学建模:目标函数与约束条件
2.1 目标函数设计:经济性优先还是用户舒适度优先?
调度策略的核心,首先是目标函数。我在这个项目里选的是“经济成本最小化”,也就是让整个车联网系统在运行周期内的净购电成本最低。
目标函数分三块:
第一块是购电成本,也就是车辆从电网买电的钱,但这笔钱会因为放电收入被抵消一部分。这里的核心是峰谷电价套利:电价高的时候尽量少买电甚至卖电,电价低的时候尽量多买电补充SOC。
第二块是电池损耗成本。很多人做调度时把这个忽略掉,我强烈不建议这么做。V2G场景下,车辆频繁充放电,电池循环寿命衰减非常明显。如果忽略损耗成本,优化算法会倾向于让所有车辆频繁深度放电,看起来节省了电费,实际上电池衰减的代价远超省下的钱,用户也不可能接受。
第三块是用户舒适度惩罚项。具体做法是:如果某辆车在某时刻虽然还有余量可以放电,但放电后SOC会低于用户期望值,那么给一个很大的惩罚系数,让优化算法尽量避免这种情况。
最终目标函数写成这样(离散化到每个时段):
总成本 = 所有时段的市电购电费用 - 所有时段的放电售电收入 + 电池损耗折算成本 + 用户满意度惩罚项
其中电池损耗部分,我参考了常见的“雨流计数法”简化版本:在仿真步长内,用“放电深度×充放电循环次数系数”近似估算等效损耗成本。虽然不如电池电化学模型精确,但作为调度层面的量化指标完全够用。
2.2 电池模型与充放电约束
电池模型的复杂度,直接影响求解速度和实用性。我没有做复杂的电化学等效电路模型,而是采用工程上常用的“能量积分模型”,只跟踪SOC变化:
SOC变化量 = 充放电功率乘以对应效率,再累积到电池总容量上
充放电过程有四个约束:
SOC上下限约束:我设置为0.2到0.9。这里下限设置为0.2,是为了避免过放对锂电池造成不可逆损伤;上限设为0.9,是因为V2G场景下经常需要留出放电空间,而且接近满充状态下电池的极化内阻增大,充放电效率都会下降。
充放电功率约束:每辆车的最大充电功率和最大放电功率,我按车型分别设置。普通家用车充电功率取7千瓦,放电功率同样取7千瓦;部分支持直流快充的车型,最大功率取20千瓦,但考虑到电池寿命,放电功率一般不会给那么高。
充放电互斥约束:同一时刻一辆车要么充电要么放电,不能同时进行。这个约束在数学上属于“互补约束”,用Yalmip建模时,我会额外引入一个二进制变量来限制状态。
电量平衡约束:每个时段结束时,车辆的SOC要等于上一时段SOC加上充放电带来的变化量。
2.3 电网功率平衡与变压器容量约束
单辆车的行为约束好建模,难点在于“聚合约束”——整个停车场接入电网的总功率,不能超过变压器的容量上限。
这个约束我是这样写的:
变压器总功率限制 = 所有车辆充电功率之和 - 所有车辆放电功率之和,再加上园区其他基础负荷,这个总和必须小于变压器额定容量。
之所以要加上园区基础负荷,是因为变压器是为整个园区服务的,不能只看车辆。我在仿真中给变压器额定容量设的是400千瓦,基础负荷峰值为150千瓦,预留的车辆充放电空间大约有250千瓦。这个参数如果不提前算好,很容易出现优化结果“看着漂亮,现场变压器直接跳闸”的尴尬情况。
另外,V2G放电时还需要考虑线路潮流方向。严格来说应该做潮流计算,验证反送电不会导致线路末端电压越限。我这个项目简化处理了——因为只针对一个停车场,馈线很短,电压问题不明显,所以在模型里暂未加入潮流约束。如果场景换成大范围配电网,这一块就绝对不能省。
3. 实时调度算法选型与核心思路
3.1 为什么不用“开环计划”而要用“实时闭环”
我早期一版代码用的就是开环思路:早上根据预测数据算好全天的充放电计划,然后一整天就机械执行。结果仿真没过关,原因很简单:
- 光伏预测误差大:下午一场云遮住太阳,光伏出力暴跌,但计划里仍然安排了大量车辆在下午充电;
- 车辆接入时间不确定:某辆车原定8点接入,结果9点半才到,既定时间段里的充电计划直接落空;
- 用户临时开走:原本计划在傍晚放电的车辆,下午4点提前开走,导致傍晚时段系统缺少了计划中的放电容量。
这不是算法不够好,而是“开环”结构决定了它不具备纠错能力。后来我把决策框架改成滚动优化,也就是模型预测控制(MPC)的思路:每个控制周期开始时,重新读取最新状态,重新预测未来一段时间的电价、光伏出力和车辆接入情况,然后只执行当前周期这15分钟的决策结果。
3.2 基于滚动优化的模型预测控制框架
MPC框架在V2G调度里非常合适,原因有三:
第一,MPC天然支持约束处理。电池SOC上下限、变压器容量、充放电功率这些硬约束,都能直接放进优化问题里。
第二,MPC对模型预测误差有反馈校正能力。每次滚动都会用最新的实测数据更新初始状态,所以相当于不断“纠偏”。
第三,MPC的在线计算量可控。预测时域我设置为24个周期,即未来6小时,采样周期15分钟。这样优化变量数量有限,在Matlab里调用求解器,几秒钟就能解出来。
整个MPC循环在代码里是这样实现的:
- 初始化车辆数据、电网数据;
- 进入循环:读取当前时刻所有车辆状态,更新预测曲线;
- 构建当前时刻起的优化模型,调用求解器求解;
- 取出第一个控制周期的决策结果,下发给车辆执行;
- 推进到下一个时刻,重复循环。
这里要特别提醒:MPC的“当前时刻状态更新”一定要做扎实。很多初学者在循环里用上一轮的预测状态替代实测状态,滚动优化就失去了校正意义。
3.3 简化处理:规则基准与启发式修正
MPC模型不是万能的,它对预测模型精度有依赖。而且,把全部决策都交给数学优化求解器,一旦遇到求解失败的情况,整个系统就没法运行了。
因此我在主策略之外,加了一条“规则基准”作为兜底:如果优化求解失败或者计算超时,系统自动切换到一个启发式规则库:
- 当前电价处于高峰时段:优先让SOC充足且用户允许放电的车辆放电;
- 当前电价处于低谷时段:优先给SOC较低且停留时间足够的车辆充电;
- 光伏出力充裕时段:优先消纳光伏,减少市电购电;
- 变压负载率超过90%:立即降低所有充电功率,暂停放电。
这个规则库逻辑虽然简单,但可以保证系统在任何情况下都能输出一套安全可执行的方案。实际项目中,求解器在大多数情况下都能正常求解,规则库很少被触发,但它在系统鲁棒性上发挥了重要作用。
4. Matlab代码实现:工程化落地全过程
4.1 代码总体结构与函数划分
我习惯把代码拆成这样几个脚本和函数文件:
main_real_time_schedule.m % 主程序:滚动优化循环 load_system_data.m % 读取基础数据 get_vehicle_state.m % 获取车辆当前状态 build_optimization_model.m % 构造MPC优化模型 solve_schedule.m % 调用求解器求解 execute_first_step.m % 执行第一个控制动作 update_simulation.m % 更新仿真状态 plot_results.m % 绘制结果图这样做的好处是:每个函数的职责单一,后续调整约束或目标函数时,不用大改主程序。比如想改成其他车型的参数,只需要改数据文件,不需要动主逻辑。
4.2 核心数据结构的定义
在Matlab里,我用的核心数据结构是“车辆状态结构体数组”。一辆车的关键字段包括:
- 接入状态:当前时刻是否接入充电桩
- 当前SOC:0到1之间
- 目标SOC:用户离开时需要的最低SOC
- 最大充电功率、最大放电功率
- 接入时间、预计离开时间
- 电池容量
- 是否允许V2G放电:用户可以选择关闭
另外还有一个“系统状态结构体”,保存当前时刻、电价、基础负荷、光伏出力等全局信息。
这个数据结构设计起来不难,但要注意:所有车辆的SOC更新,必须放在同一个时间步里同步完成,不能一边更新车辆A的SOC、一边用车辆A的新状态去计算车辆B的约束。并发时序错乱,会导致优化结果莫名其妙的异常。
4.3 关键模块实现:预测模块、决策模块、执行模块
预测模块的作用,是生成未来一段时间内系统的输入预测序列。光伏出力我用的是典型日曲线乘以随机扰动系数;电价用的是实际市场峰谷分时电价,分成峰、平、谷三个时段。
有意思的是,我在预测模块里加了一个“车辆到达预测”:根据历史到达规律,用泊松分布模拟新车辆接入。这个模型很简单,但能有效模拟“实时不确定性”。每个MPC周期重新生成一次预测序列,模拟“实时更新”的效果。
决策模块是整个项目的核心。我用Yalmip构建如下优化问题:
% 决策变量 P_ch = sdpvar(n_vehicle, horizon, 'full'); % 充电功率 P_dis = sdpvar(n_vehicle, horizon, 'full'); % 放电功率 Soc = sdpvar(n_vehicle, horizon+1, 'full'); % SOC状态 目标函数 = 购电成本 - 放电收益 + 电池损耗 + 满意度惩罚; 约束条件: - SOC递推方程 - SOC上下限 - 充放电功率限制 - 充放电互斥(引入二进制变量) - 变压器总功率约束调用求解器时,我常用的是Gurobi或Cplex。如果没有商业求解器,也可以用默认的Sedumi或者SDPT3,但求解速度会慢很多。这里强调一句:这个问题的难度在于存在二进制变量,属于混合整数规划。模型规模一旦扩大,求解时间会上升很快,所以预测时域不能设得太长。
执行模块做的事情很简单:从求解结果中,取出第一个时刻的充放电功率值,更新车辆SOC,然后进入下一个时刻。
4.4 参数整定与配置文件设计
我把所有可调参数集中在一个参数文件里,方便反复调试:
- 采样周期:默认15分钟
- 预测时域:默认24个周期(6小时)
- SOC上下限:0.2到0.9
- 电池损耗系数:0.05元/千瓦时
- 用户满意度惩罚系数:10元/单位缺口SOC
- 最大充放电功率:7千瓦/20千瓦两档
参数整定是门手艺活。我踩过的一个坑是“电池损耗系数设太小”——优化算法觉得放电几乎没有代价,于是让车辆频繁深度放电,到傍晚用户开车时,SOC已经低到接近下限。后来我把损耗系数调高,同时增加用户满意度惩罚项,优化结果才变得合理。
5. 仿真案例与结果分析
5.1 场景设置与输入数据准备
仿真场景设在一个公司园区停车场,工作时间早上8点到晚上8点,总共有30辆电动汽车接入。光伏装机容量为100千瓦,变压器容量400千瓦,园区基础负荷峰值150千瓦。
电价设置按常见的峰谷分时:峰段10:00-15:00、18:00-21:00,电价1.2元/千瓦时;谷段23:00-次日7:00,电价0.4元/千瓦时;平段其余时间,电价0.7元/千瓦时。
车辆参数有两种类型混用:20辆车为7千瓦交流慢充,电池容量40千瓦时;10辆车为20千瓦直流快充,电池容量60千瓦时。每辆车的初始SOC设定为0.3到0.8的随机值,目标是离开时至少达到0.8。
5.2 运行结果与优化效果对比
我做了两组对比实验:一组是“不调度”模式,即车辆接入后马上以最大功率充电,充满就停;另一组是“V2G实时调度”模式。
从结果看,最关键的经济性指标差异明显:不调度模式下,全天净购电成本约2680元;V2G实时调度模式下,净购电成本约2035元,节省了约24%。这主要来自两个部分:一是把充电行为从高峰时段平移到了低谷时段,利用峰谷价差降低了购电成本;二是部分车辆在傍晚高峰时段放电给电网,获得了一笔放电收益。
电网侧的削峰效果也很明显。不调度模式下,停车场负荷叠加基础负荷,在傍晚时段突破320千瓦,最高接近变压器容量上限;V2G调度模式下,车辆被安排在18:00-20:00时段放电,停车场净负荷反而下降到220千瓦以下。
5.3 调度策略的鲁棒性分析
实时性到底有多重要,我用“预测误差注入”的方式做了验证。我在光伏预测中加入了随机误差,让预测值和真实值偏差达到20%以上。
结果对比很有意思:
- 开环计划调度:出现多次变压器过载,原因是实际光伏出力远低于预测,导致系统从市电购电功率超标;
- 滚动优化调度:总成本上升了8%,但仍然在所有约束范围内,没有一次越限。
这就是滚动优化的魅力。它并不完美,不能消除所有预测误差的影响,但它能持续修正决策,把误差控制在可接受范围内。
6. 踩坑实录:常见问题与排查技巧
6.1 求解器报错或数值不稳定怎么办
使用Yalmip构建模型时,一个非常常见的问题是变量维度不匹配,尤其是SOC递推方程里出现“矩阵维度不一致”的报错。这时需要用size函数逐段检查矩阵维度,确认预测时域维度和车辆数量维度没有转置颠倒。
另一个典型问题是二进制变量导致的求解过慢。如果模型规模较大,我建议先简化预测时域,或者减少二进制变量的数量。例如,充电和放电互斥约束可以不用二进制变量,而通过设定“放电时充电功率必须为0”这种条件,简化建模。
6.2 电池循环次数约束导致的“假收敛”
我之前曾经在模型里加入“单辆车一天内充放电切换次数不超过两次”这种约束。在想得很美的逻辑下,这确实能减少电池损耗,然而在MPC滚动框架里,这个约束每次滚动窗口都会“重置”计数,导致优化算法在每轮决策中利用这个重置做文章——频繁切换充放电状态,但每个窗口内切换次数都不超限。
这个问题的本质是:MPC的滚动窗口天然会丢失跨边界的约束记忆。解决办法有两个:一是在每次滚动时把过去时段的累计切换次数作为初始状态传进去;二是干脆放弃硬约束,改用目标函数里的惩罚项来抑制频繁切换。我最后选了第二种,加上一个“切换惩罚系数”,效果好得多。
6.3 模型预测控制的实时性问题
在实际应用中,计算耗时是绕不开的问题。我的求解器在单步求解耗时大约在0.5到2秒之间,对于15分钟的控制周期来说,实时性完全够用。但如果把场景扩大到几百辆车,同时预测时域拉长,求解耗时可能会超过20秒。
优化速度有几个方向可以尝试,我一般按这样的顺序排查:
- 求解器参数:尝试调整MIPGap容忍度;
- 模型规模:减少预测时域;
- 简化策略:把相似车辆聚合成“车队”处理,先算出聚合功率,再按比例分配。
6.4 数据缺失与通信延迟处理
仿真环境里数据总是完美传输,现实中不可能。车辆通信中断、充电桩状态上传失败,都会导致调度器缺数据。我在代码里做了数据容错机制:
- 如果某辆车的状态数据超时未更新,默认按“不可用车辆”处理,不允许放电只允许充电;
- 如果光伏数据缺失,用上一时刻的光伏出力值替代,并加大滚动更新的权重;
- 如果电价信号缺失,沿用最近一次有效电价。
这套容错处理让系统在数据不完整的情况下,仍然能输出一套安全的调度方案,不至于崩溃。
7. 一点个人体会
做这个项目给我最大的感受是:V2G调度策略的真正难点,不在于优化算法有多花哨,而在于要把“用户需求”“电池寿命”“电网安全”这三个目标同时平衡好。纯数学模型很容易让车辆变成电网的“工具电池”,忽略用户的真实感受,结果用户根本不支持这种调度模式。反过来,只照顾用户体验,又难以实现经济收益。
我个人在实际操作中还发现一个容易被忽略的点:充电桩的通信协议和数据采集粒度,对调度策略的落地影响极大。很多实际充电桩的实时功率指令下发周期不是秒级的,而是分钟级的,且功率调节精度有限。所以调度策略在仿真阶段就应该考虑到执行分辨率,而不是假设功率可以连续任意调节。如果代码里生成的功率指令是6.37千瓦这种小数,到了现场执行机构可能就会出现较大偏差。
最后再分享一个小技巧:如果你是在做一个新的微网场景,不要一开始就追求大而全的模型,先把核心的MPC框架跑通,再逐步加入电池老化、需求响应、光伏不确定性这些复杂因素。我第一版代码只用了5辆车、30个时段,调通了整个逻辑之后才扩展到30辆车。这种“先小后大、逐步加约束”的做法,能让你排查问题的时候思路清晰,不至于被一堆耦合因素绕晕。