增程式电动汽车这几年热度挺高,但真正把它做好的关键,不在发动机本身,而在“什么时候发电、发多少电”这套能量管理策略。我最近在做一个增程式车型的能量管理预研项目,把原来的固定阈值策略换成了基于工况的自适应ECMS(等效消耗最小化策略),整条策略都在Matlab环境下完成建模与仿真验证。这篇文章就把这个方案的完整思路、数学原理、代码实现和踩坑记录都摊开来讲,适合正在做混动/增程能量管理、或者想从规则策略往优化策略转型的工程师参考。
先说结论:固定SOC阈值策略不是不能用,但它在工况波动大的真实路况下会明显偏离最优;固定等效因子的ECMS比规则策略好一截,但遇到长上坡、持续高速、严重拥堵这些场景又会被“带偏”;真正稳的是让等效因子跟随工况和SOC状态自适应调整。下面从头梳理。
1. 先搞清楚增程式能量管理到底在管什么
1.1 从整车构型理解控制自由度
增程式电动汽车的拓扑是典型的串联混动:发动机带动发电机发电,电能汇入电池包和驱动电机之间的直流母线,发动机与车轮之间没有机械连接。这个构型最大的特点是——发动机的运行点彻底自由了。不需要像并联混动那样考虑发动机转速和车速的耦合,整车上唯一的控制自由量就是增程器的发电功率,或者等效地写成电池功率。
整车功率平衡式非常直观:
P_demand = P_gen + P_batt其中P_demand是驱动电机当前需要从母线取用的功率,P_gen是增程器发出的电功率,P_batt是电池的充放电功率(这里约定放电为正、充电为负)。
所以能量管理策略真正要回答的只有一个问题:**在每一个控制周期,我应该让增程器发多少电?**发少了,电池持续放电,SOC往下掉,最终保不住电量;发多了,多余的电充进电池,又要经历“油→电→化学能→电”的多级转换,整体效率不一定划算。这就是增程式整车最核心的控制矛盾。
1.2 为什么固定阈值规则策略不够用
早期增程式车型用得最多的是规则策略,典型逻辑是:SOC高于28%时增程器完全停机,低于24%时启动,按固定功率(比如30kW)发电,直到SOC回到28%再停机。这套逻辑简单可靠,我在项目初期也先用它做了一遍参照仿真,结果很能说明问题。
低速城市工况下,驱动功率需求平均只有8kW左右,增程器一启动就是30kW,多出来的20多kW全部充进电池。于是出现“发动机在高效点发电、电池被反复充放”的局面,循环下来燃油没有少烧,电池的充放电里程倒是多了一倍。高速工况又反过来,驱动功率经常超过30kW,增程器满负荷运行还不够,SOC快速下滑,到了低SOC区间电池放电能力受限,整车动力性都受影响。
规则策略的毛病在于:它把连续的控制量硬切成了几个离散状态,完全不看当前和未来一段时间的功率需求。而ECMS这类瞬时优化方法,思路是把“未来一段时间内该不该用电”折算成当前一个代价函数里的惩罚项,实时求最优发电功率,天然比阈值切换更灵活。
2. ECMS的理论核心:把电能折算成“油”
2.1 等效因子的物理意义
ECMS全称Equivalent Consumption Minimization Strategy,核心思想非常直白:电池里的电不是从天上掉下来的,最终还是要靠发动机烧油补回来。所以策略在评价“当前时刻花掉多少代价”时,不能只看发动机的瞬时油量,还要把电池的放电/充电折算成“虚拟燃油消耗”。
瞬时代价函数写成:
J = m_dot_fuel(P_gen) + s * P_batt / H_LHV其中m_dot_fuel是增程器当前发电功率对应的燃油消耗率,单位kg/s;P_batt是电池功率,单位为W;H_LHV是燃油低热值,约46MJ/kg;s就是等效因子,一个无量纲系数。这个式子把电池功率除以燃油低热值,等于把“电功率”换算成“假设用电功率来自燃油时对应的燃料质量流量”,再用s去加权。
s的物理意义可以这样理解:**它代表当前策略对用电的“吝啬程度”。**s偏大时,放电代价被放大,策略倾向于让增程器多发电、给电池充电;s偏小时,用电显得“便宜”,策略倾向于让增程器少发电、尽量用电池顶着。这个参数直接决定了整车的电量维持倾向,是ECMS的灵魂。
2.2 从全局最优到瞬时最优的近似
如果从全局角度看,增程式能量管理是个最优控制问题,目标是一整段工况内的总油耗最小。理论上可以用动态规划(DP)离线求解,但DP需要提前知道完整工况,实车没法直接使用。ECMS做了一次关键近似:把全局积分目标拆成每个时刻的瞬时最小化,把未来工况的影响压缩进等效因子s这个标量里。
这个近似成立的前提是:在足够长的时间尺度上,电池SOC回到目标值附近,电池电量本身不产生“净收益”。也就是说,等效因子实际上承担了“全局协态变量”的角色——动态规划里那个拉格朗日乘子,在ECMS里就是s。这也是为什么ECMS的被广泛使用:它用一个标量,把一个高维全局优化问题简化成了一维瞬时寻优,实时性极好。
2.3 固定等效因子的阿喀琉斯之踵
如果把s固定成某个值,比如s=2.0,在单一工况下可以调得不错,但工况一变就会露馅。
我做了个简单试验:固定s=2.0,跑高速工况和城市工况。高速工况下驱动功率大,电池放电快,SOC一路往下掉,但固定s并没有及时惩罚放电,等到SOC跌破保电区间,电池功率受限,整车只能被迫提高发电功率,反而把发动机拖到高油耗区。换成低速城市工况,s=2.0又显得太“吝啬”,增程器频繁启动给电池充电,发动机启停次数和油耗双双上升。
问题不在于ECMS框架,而在于等效因子s应当随工况和SOC状态实时调整。这就是自适应ECMS(A-ECMS)出现的动机。
3. 自适应设计:工况怎么“识别”,等效因子怎么“调”
3.1 从车速序列里提取什么特征
要做工况自适应,第一步就是把“当前是什么工况”这件事量化。不需要什么高深算法,用滑动窗口统计车速序列的简单特征就够用。我在项目里用的窗口长度是60秒、步长1秒,提取了四个特征:
- 平均车速v_mean,反映整体运行速度水平;
- 加速度均方根a_rms,反映加减速激烈程度;
- 停车时间占比p_stop,反映拥堵程度;
- 平均驱动功率需求P_dem_mean,反映负荷强度。
这四个特征都有明确的物理含义。城市拥堵工况v_mean低、p_stop高;高速工况v_mean高、a_rms低;混合郊区工况介于两者之间。用它们做分类,既能区分工况,又不会引入太多计算量。
3.2 离线聚类+在线查表:兼顾精度与实时性
工况识别不需要在车上跑机器学习模型。我采用的做法是:先在Matlab里用三组标准驾驶循环(城市、郊区、高速)提取大量样本特征,做一遍k-means聚类,得到三个类别中心;上车之后只需要算当前窗口的特征向量,再求它与三个中心点的欧氏距离,最近的那个就是当前工况类别。整个过程就是几十次乘加运算,单片机上都能轻松跑。
聚类结果大致会形成下表这样的分布(数值是我这个项目的标定结果,不同车型要重新标):
| 工况类别 | 平均车速范围 | 停车时间占比 | 等效因子基准值s_base |
|---|---|---|---|
| 城市拥堵 | <25 km/h | >15% | 1.6~1.8 |
| 混合郊区 | 25~70 km/h | 5%~15% | 1.9~2.2 |
| 高速巡航 | >70 km/h | <5% | 2.3~2.6 |
为什么高速工况的s_base要更高?因为高速下驱动功率持续大,电池放电风险高,策略需要更倾向于保住电量;而城市工况速度低、功率需求波动大,让电池多承担一些负荷反而能避开发动机低效区,所以s_base低一些。
3.3 等效因子修正链路:前馈+反馈双通道
单独靠工况查表还不够,SOC状态必须也参与调节。我最后实现的等效因子更新式是:
s(k) = sat( s_base(k) + kp*e_soc(k) + ki*integral_e_soc(k), s_min, s_max ) e_soc(k) = SOC_ref - SOC(k)前馈通道s_base来自工况识别,解决“当前工况下该用什么样的电量策略倾向”的问题;反馈通道是SOC误差的比例积分调节,解决“实际SOC偏离目标时要把倾向拉回来”的问题。两者相加后限制在s_min和s_max之间,防止等效因子波动过大导致发电功率剧烈振荡。
双通道比单纯SOC反馈更聪明的地方在于:工况突变时前馈通道能立刻调整s,不用等SOC偏差积累起来再被动反应。比如车辆从城市驶入高速,s_base马上从1.7跳到2.4,增程器发电功率会提前提升,SOC就不会出现大滑坡。
另外,为了防止工况误判引起s频繁抖动,我还对s做了低通滤波,时间常数取5秒左右。这个细节很重要,后面在第六章还会专门说。
4. Matlab实现细节:模型、代价函数与求解器
4.1 仿真模型的必要简化
策略开发阶段,仿真模型不需要把每个部件的电磁瞬态都建出来,但也不能简化到失真。我的Matlab模型按“准静态+效率Map”的方式搭建,模块划分如下:
| 模块 | 作用 | 关键参数 |
|---|---|---|
| 驾驶循环 | 提供车速-时间序列 | 标准工况/自定义工况 |
| 纵向动力学 | 由车速反推驱动功率需求 | 整车质量、风阻、滚阻 |
| 增程器模型 | 由发电功率查燃油消耗率 | BSFC最优曲线 |
| 电池模型 | 由电池功率更新SOC | 开路电压、内阻、容量 |
| A-ECMS控制器 | 每步计算最优发电功率 | 等效因子、SOC目标 |
| 统计模块 | 累计油耗、启停次数等 | — |
整车纵向动力学用这个公式反推功率需求:
P_demand = (m*g*f + 0.5*rho*Cd*A*v^2 + m*delta*a) * v / eta_drive其中m取1800kg,f取0.012,Cd取0.28,A取2.5m²,eta_drive取0.92。这些参数的选取直接影响仿真结果绝对值,但不会影响策略优劣的相对比较。
增程器模型按“最优发电曲线”简化:发动机和发电机带刚性连接,不同发电功率下总能通过调节转速和节气门找到最低BSFC点。将这个最低点拟合成一维函数:
m_dot_fuel = f(P_gen)这个项目里增程器发电功率范围是0~45kW,最低燃油消耗率大约230g/kWh,出现在25~30kW区间。曲线在低功率段油耗率偏高,高功率段也偏高,呈典型凹函数形状。
4.2 电池模型与SOC更新的耦合
电池我用的是Rint等效电路模型,即开路电压U_oc和串联内阻R_int都随SOC变化。这里有一个容易搞反的细节:策略层用端功率P_batt作为代价函数输入,而SOC更新必须用欧姆损耗之后的实际电流积分。
给定端功率P_batt,先解一元二次方程求电流:
U_oc * I - I^2 * R_int = P_batt解得:
I = (U_oc - sqrt(U_oc^2 - 4*R_int*P_batt)) / (2*R_int)然后SOC差分更新:
SOC(k+1) = SOC(k) - I * dt / (Q_Ah * 3600)注意符号约定:P_batt为正表示放电,电流为正,SOC下降。充电时P_batt为负,电流为负,SOC上升。工程上最好把正负号约定写进代码注释,不然很容易在联调时出低级错误。
4.3 代价函数与网格枚举求解
每个控制周期,A-ECMS控制器做这样一件事:在当前功率需求P_demand和当前SOC下,枚举所有可行的发电功率u,计算每个u对应的瞬时代价J(u),选出代价最小的u作为增程器目标功率。
代价函数:
J(u) = m_dot_fuel(u) + s * (P_demand - u) / H_LHV因为P_batt = P_demand - u,所以发电功率越大,电池放电越少,第二项越小;但第一项燃油消耗可能增大。这个一维寻优问题完全没必要用复杂优化器,直接网格枚举最稳、最可控。我用的网格是0~45kW、步长0.5kW,每步91个点,Matlab M代码单步耗时也就1毫秒多。
4.4 核心代码逐段拆解
先看控制器主体代码:
function [P_gen, s_k] = AECMS_controller(P_demand, SOC, s_prev, s_integral, p) % p 是参数结构体 % 1. 工况识别:根据最近60秒车速窗口求特征向量,查最近聚类中心 feat = get_window_feature(p.v_buffer); [~, class_id] = min(sum((p.cluster_centers - feat).^2, 2)); s_base = p.s_base_table(class_id); % 2. SOC反馈修正 soc_err = p.SOC_ref - SOC; s_feedback = p.kp * soc_err + p.ki * s_integral; s_raw = s_base + s_feedback; s_k = s_prev + (1/p.tau_s) * (s_raw - s_prev); % 一阶低通 s_k = max(p.s_min, min(p.s_max, s_k)); % 3. 网格枚举寻优 u_grid = 0:p.delta_u:p.P_gen_max; P_batt = P_demand - u_grid; P_batt = max(p.P_batt_min, min(p.P_batt_max, P_batt)); m_dot_fuel = interp1(p.eng_power_table, p.eng_fuel_table, u_grid, 'linear', 0); J = m_dot_fuel + s_k * (P_batt / p.H_LHV); % P_batt>0表示放电,产生正等效油耗 [~, idx] = min(J); P_gen = u_grid(idx); end这段代码逻辑不复杂,但有三个容易踩的坑。
第一个是P_batt越限后要重新参与代价计算。我把夹紧后的P_batt用于计算J,但P_gen仍取对应的u_grid(idx)。这一步保证了寻优结果一定在电池功率允许范围内。
第二个是interp1的外推值处理。发电功率超出插值表范围时,我强制赋0是不对的,更稳妥的做法是把燃油消耗率表建到发电机最大功率之外,外推时保持线性延伸,这样代价函数在边界附近才有正确的梯度。
第三个是低通滤波的写法。直接用s_k = s_raw会引入工况切换的阶跃,导致发电功率跳变;加上一阶惯性之后,s的变化率和工况识别策略的切换频率就脱钩了。
SOC积分项在控制器外部的循环里更新:
s_integral = s_integral + soc_err * dt; s_integral = max(p.integral_min, min(p.integral_max, s_integral));积分项限幅很重要。否则SOC长时间偏低时积分项会持续增大,把s顶到上限,导致策略在SOC回中后仍然过度充电,形成明显的过冲。
整个仿真主循环就是按1秒步长推进:
for k = 1:length(v_cycle) P_demand = longitudinal_model(v_cycle(k), a_cycle(k), p.vehicle); [P_gen, s_k] = AECMS_controller(P_demand, SOC(k), s(k-1), s_integral, p); P_batt = P_demand - P_gen; SOC(k+1) = battery_step(SOC(k), P_batt, dt, p.battery); fuel_total(k+1) = fuel_total(k) + interp1(p.eng_power_table, p.eng_fuel_table, P_gen, 'linear', 0) * dt; end5. 仿真验证:三组工况下的实测结果对比
5.1 验证工况与对比策略
为了模拟真实用户一天的驾驶,我把标准驾驶循环的三类片段拼接成一个完整工况:前20分钟城市拥堵,中间25分钟混合郊区,最后15分钟高速巡航。总时长约1小时,平均车速约38km/h。所有策略都在同一初始SOC=60%、目标SOC=50%的设置下跑完,保证对比公平。
对比对象有三个:
- 规则策略:SOC低于24%启动增程器、30kW固定发电,高于28%停机;
- 固定ECMS:s固定为2.0;
- 自适应ECMS:本文的方案。
5.2 SOC轨迹与油耗对比
仿真结果整理成下表:
| 策略 | 百公里油耗 | SOC终值 | 发动机启停次数 | 平均油耗率(g/kWh) |
|---|---|---|---|---|
| 规则策略 | 7.8L/100km | 49.2% | 9 | 248 |
| 固定ECMS | 7.1L/100km | 48.1% | 5 | 236 |
| 自适应ECMS | 6.4L/100km | 50.6% | 3 | 230 |
规则策略油耗最高的原因很清晰:固定30kW发电功率在城市阶段大量给电池充电,这些电在后续拥堵段又被反复充放,效率损失全算进油耗。固定ECMS用瞬时寻优,大部分时间能把发电功率压到当前最优值附近,油耗已经不算差,但在高速段SOC掉到44%后才开始被动加大发电,发动机被顶到高油耗区,整体油耗率偏高。
自适应ECMS的优势体现在两个点:一是进入高速段前,工况识别提前把s_base从2.0抬到2.4,发电功率比固定ECMS提早提升,SOC没有发生大滑坡;二是SOC反馈通道保证了全程SOC始终贴着50%目标线,没有出现“前段放任放电、后段被迫强充”的失控局面。
5.3 发动机运行点分布
看发动机运行点分布更有意思。规则策略的发电功率是一个固定值30kW附近,看起来是落在高效区的,但加上启停次数多、每次启动后的瞬态油耗,实际税后效率并不高。固定ECMS的发电功率宽泛地分布在15~35kW,高速大负荷段逼近上限。自适应ECMS的发电功率分布明显向25kW附近集中,这正是BSFC最优曲线的最低点。
这就引出另一个容易被忽视的结论:ECMS并不一定追求发动机“永远在最高效点工作”。它追求的是“燃油消耗+电池等效油耗”总代价最小。当电池SOC低或工况负荷大时,哪怕瞬时BSFC偏高,多发电去保住SOC也是值得的。自适应ECMS把这个权衡关系做得比固定参数更聪明。
6. 工程落地中的几个关键细节与坑
6.1 SOC窗口与发动机起停的最小时间约束
ECMS算出的最优发电功率可能是很小的值,比如在拥堵蠕行时算出3kW。如果让发动机为了3kW启动一次,反而亏得厉害。工程实现一定要给增程器加起停滞回和最小运行时间约束。我实现了一个状态机:发动机停机状态下,目标发电功率低于8kW不启动;启动后即使目标功率低于8kW,也至少运行20秒再停机。这个限制在Matlab仿真里看着不起眼,实车标定时不加上会导致发动机频繁点动,油耗和NVH都会崩。
6.2 坡度和载荷变化会干扰工况识别
工况特征里的平均功率需求P_dem_mean有一个天然缺陷:它把“坡道导致的持续大功率”和“高速导致的大功率”混为一谈。长上坡时P_dem_mean偏高,工况识别很容易把城市路误判成高速工况,s_base被抬高,策略就会倾向多发电,而多余的电在坡顶之后又会面临SOC偏高问题。
处理的办法有两个方向:简单一点的办法是把纵向加速度和坡度估计量作为特征之一;更稳妥的办法是只让工况聚类用车速类特征(v_mean和p_stop),把功率需求放到SOC反馈通道里。功率需求高但车速不高时,SOC往下走,反馈通道自然会增大s。这个思路在车上有坡度传感器的情况下更实用。
6.3 等效因子表和SOC反馈系数的标定顺序
自适应ECMS需要标定的参数包括:s_base表、kp、ki、s_min/s_max、低通时间常数tau_s。我的经验是先粗标s_base表,让三条典型工况下SOC能基本维持;再加SOC反馈系数,把SOC终值误差调小;最后调tau_s,抑制工况切换引起的s抖动。顺序不能反,因为kp和s_base都影响SOC维持能力,混在一起调会陷入反复试错的循环。
标定数据也可以从全局优化来:把典型工况用动态规划离线求解,反解出每个时刻的最优等效因子,再聚合成s_base表。这样标定出来的表格有理论支撑,比纯试错可靠得多。
6.4 代码部署前的固定点问题
Matlab仿真用的是double类型,上嵌入式控制器时一般是单精度或定标定点。代价函数里s乘以P_batt/H_LHV,H_LHV数值很大,中间运算容易截断误差。我的建议是在控制器里把所有物理量预归一化:发电功率用百分比表示,SOC误差用百分比,s直接存成定点数。等效因子范围1.4~2.8,乘以一个标定系数后转成16位整数,精度完全够用。
最后说一点个人体会。自适应ECMS这套方案,在仿真阶段跑出来的收益相当可观,但真正上车前还要面对发动机响应延迟、电池老化导致的容量漂移、空调等附件功率扰动这类“模型里没写干净”的问题。我的处理方式是把它当成一个基础框架,后续把坡度前馈、电池温度修正、启停状态机一并叠加上去。做能量管理的人都要记住:算法再漂亮,也要控制好边界条件,让策略在任何边角场景下都不犯错,这才是工程落地的真正门槛。