☰
DQN深度强化学习在串联混动能量管理中的实战经验
2026/10/17 11:41:32 网站建设 项目流程

上次调串混样车的能量管理策略时,我还在靠一堆标定表来回切换发动机发电功率,直到试了基于深度强化学习的功率分配方案,用DQN来控制电池与发动机发电机组的出力比例,整套控制器的设计思路才彻底打开。这篇文章不是教科书式讲解,而是我自己在仿真、台架和实测中真正踩过的坑、试过的参数、总结出来的经验,适合正在做混合动力整车控制、增程式车型能量管理或者刚接触强化学习想找落地场景的工程师参考。

我尽量少写公式推导,多讲"为什么这么设计""问题到底出在哪",以及那些论文里不会写的工程细节。

1. 从标定表求助到自己:串混能量管理的核心矛盾

1.1 串联混动本质上在控什么

串联式混合动力(包括增程式)的结构不复杂:发动机带动发电机发电,发电电能接到母线上,和电池一起给驱动电机供电。驱动电机通过减速器驱动车轮。发动机和车轮之间没有机械连接,所以发动机转速、扭矩和车速是解耦的。

这样整车控制就变成一个很纯粹的功率分配问题:某一时刻,驱动电机需求功率为 P_req,电池端功率为 P_bat,发动机发电机组输出功率为 P_gen,三者的关系是:

P_gen + P_bat = P_req

P_bat > 0 表示电池放电,P_bat < 0 表示电池充电。能量管理策略要做的,就是决定每一时刻 P_gen 发多少、电池充放电功率怎么分配。

很多人第一反应是:这不就是查个表的事吗?发动机功率大一点、电池少放电一点,SOC就保住了;发动机功率小一点、电池多放电,燃油就省了。问题在于,"省油"和"保SOC"本身是矛盾的。要在不同工况下做全局最优的分配,牵扯到发动机工作效率、电池充放电效率、发动机启停平顺性、整车动力性等一堆约束,规则表很难全照顾到。

1.2 规则策略、离线优化与在线学习的根本差别

常见的工程方案有四类,我简单梳理一下各自的处境:

  • 恒温器式控制:SOC低于下限时启动发动机,按固定挡位发电;SOC高于上限就停机。像家用空调开开停停。优点是简单、可靠、好标定;缺点是发动机工作点固定,低速时常在低效率区运行,而且启停次数多,驾驶体验也受影响。
  • 功率跟随式控制:P_gen 直接跟随 P_req,发动机实时响应。好处是电池充放电压力小,坏处是发动机工作点随风速和路况剧烈摆动,很难一直停留在最高效率区间,燃油经济性并不好。
  • 基于动态规划的最优控制:如果提前知道未来一段时间的全部工况,可以用动态规划离线算出一条全局最优的功率分配轨迹。但真实驾驶永远没有"已知未来"这回事,所以DP只能作为基准参考,没法上车实时用。
  • 等效燃油消耗最小策略(ECMS):把电池放电/充电折算成等效油耗,在每一时刻选等效油耗最小的功率点。它是一个在线可用的近似最优策略,问题在于等效因子怎么在线调节、怎么保证SOC终值,标定起来也不省心。

这些方案本质上都在用"人工经验+离线数据"去逼近一个动态优化问题。而深度强化学习换了个思路:它不去算"未来某一整段工况的最优解",而是在大量工况交互中学习"每个状态下应该怎么选动作",相当于把规则标定从"人写表"变成"算法自己从数据里拟合策略",这也是我最终转向DQN的核心原因。

2. DQN凭什么能接住这个活:从Q值到功率决策的完整机制

2.1 Q-Learning和DQN到底在干什么

深度强化学习的经典算法DQN,全称Deep Q-Network,是Q-Learning和深度神经网络的结合。Q-Learning的核心是一个Q函数:在状态s下采取动作a,之后一直按最优策略走,能获得的期望累积奖励(折扣后)是多少,记作Q(s,a)。

如果有了准确的Q函数,决策就非常简单:每个状态下选Q值最大的动作。问题在于真实系统里状态空间是连续的(车速、SOC、需求功率都是连续变量),没法把每个精确状态对应的Q值都查表存下来。DQN的做法是用一个多层神经网络去近似Q(s,a),输入是状态,输出是每个动作对应的Q值,然后通过不断试错、采样、更新来逼近真实Q函数。

这样做有个很自然的优势:车辆能量管理的控制周期是毫秒到秒级,神经网络计算一个前向推理的延迟远低于控制周期,所以在线实时选动作没问题。

2.2 能量管理写成马尔可夫决策过程

把能量管理问题映射成强化学习的标准框架,我们需要明确四个东西:状态、动作、奖励、状态转移。

我建议按下面这个思路来对齐:

MDP要素在能量管理中的对应
状态 sSOC、车速、需求功率、发动机是否运行、电池温度等
动作 a发电机组的功率挡位(或发动机-发电机工作点)
奖励 r油耗负值、SOC偏差惩罚、发动机启停惩罚等
状态转移整车动力学模型 + 电池/发动机/发电机模型 + 驾驶工况

这里最容易忽略的是"马尔可夫性"这个概念。强化学习理论假设当前状态已经包含了决策所需的全部信息,下一步只依赖当前状态和动作。实际工程里,如果你只把SOC和车速放进去,但SOC的变化速率很重要、电池温升对功率影响很大,那模型可能学不到合适策略。我的做法是尽量把影响功率分配的关键量都放进状态,后面我会具体展开。

2.3 DQN的训练流程和一个能跑的伪代码

DQN的训练逻辑可以压缩成三步:采集、回放、更新。先用ε-greedy策略在环境中探索,把四元组(state, action, reward, next_state)存进经验回放缓冲区;训练时随机抽样一个小批量(mini-batch),用目标网络计算TD目标值;再用当前网络预测值和目标的均方误差做梯度下降。每隔一定步数,把当前网络参数同步给目标网络。

我按实际项目中的写法整理了一个简化的伪代码,读者可以直接对照理解:

# DQN 训练主循环(伪代码) for episode in range(train_episodes): state = env.reset() while not done: if random.random() < eps: action = random.randrange(action_num) # 探索 else: action = q_net(state).argmax().item() # 利用 next_state, reward, done = env.step(action) replay_buffer.store((state, action, reward, next_state, done)) state = next_state if len(replay_buffer) >= batch_size: batch = replay_buffer.sample(batch_size) # TD目标:r + gamma * max_a' Q_target(s', a') q_target = (reward + gamma * q_target_net(next_state).max(dim=1) * (1 - done)) q_pred = q_net(state).gather(1, action) loss = F.mse_loss(q_pred, q_target) optimizer.zero_grad() loss.backward() optimizer.step() if step % target_update_period == 0: q_target_net.load_state_dict(q_net.state_dict())

很多人问我,能量管理为什么不用SAC、DDPG这些连续动作算法?我的回答是:功率分配本质上可以离散化,而且DQN在动作空间不大时收敛更稳定、工程实现更简单。连续动作算法要额外调的东西太多了,刚开始不稳定,先用DQN跑通整个链路,后面再换更先进的算法也不迟。

3. 状态、动作与奖励函数怎么设计:能量管理成败的分水岭

3.1 状态空间必须包含哪几项

状态空间的选择直接决定训练好不好学。我第一版只放了SOC和需求功率,训练出来的策略在工况随机性稍强时会变得很怪。后来把状态空间稳定在这么几个量:

  • SOC:电池荷电状态,归一化到0~1之间。它代表了电池的"库存",是能量管理中最核心的状态。
  • 车速:会影响当前驱动功率需求,也能帮助判断未来一段时间的负载趋势。
  • 需求功率P_req:来自驾驶员踏板解释和附件功率,是当前功率分配的最直接依据。
  • 发动机运行状态标志:发动机当前是启动还是停机。加这个很重要,因为"是否启动发动机"是个离散模式,不放进状态里,策略很难学会平滑启停。
  • 电池温度:低温下电池充放电能力下降,不加温度状态,训练出的策略在冷机工况可能直接失效。

在送入神经网络之前,所有连续变量都要做归一化。比如SOC除以100,车速除以最高车速,需求功率除以最大功率。这个细节很多人忽略,结果训练时loss长期不降——MLP对输入尺度很敏感,量级差距过大,初始梯度方向就被大数值特征带偏了。

3.2 动作空间的离散粒度怎么选

动作空间的设计要结合发动机-发电机组的工作特性。发动机的效率通常用BSFC(制动比油耗)图表示,横轴转速、纵轴扭矩,等油耗线呈现出一个碗状区域,最低油耗点附近是"经济区"。每个功率点下,都存在一个对应的最优转速和扭矩组合,连成一条"最优工作线"。

因为发动机和车轮解耦,我们完全可以控制发电机组沿着最优工作线运行。这样一来,控制输出就可以从"转速+扭矩"二维降维成一个一维量:发电功率挡位。比如从0kW到60kW,按2kW间隔离散成31个挡位(包括停机挡0)。

我的经验是:挡位数量控制在30到50之间比较合适。太少,控制粗糙,电池会频繁调整充放电流;太多,DQN的动作空间过大,训练样本量要求翻倍,收敛速度明显变慢。如果你非要精度更高的连续控制,那就得往SAC、TD3那边走了,但工程复杂度是另一档。

还有一个容易踩的坑:动作空间里不要包含那些明显不合理的组合。比如需求功率只有20kW,你却允许发电功率60kW的挡位,模型可能在探索阶段学到疯狂充电的奇怪行为。这虽然能被奖励函数纠正过来,但白白多花很多探索时间。我的做法是把动作挡位按需求功率动态裁剪,让每次可选动作都集中在合理范围内。

3.3 奖励函数的每一项都要有名字

奖励函数是能量管理强化学习里影响最大、也最容易写歪的部分。我用的奖励函数形式是:

R = - ( α1 * m_fuel + α2 * |SOC - SOC_ref| + α3 * engine_start )

m_fuel 是当前步的瞬时油耗(g/s),SOC_ref 是目标SOC(一般设为50%或60%),engine_start 是发动机从停机变启动的瞬间标志,用来抑制频繁启停。

这里有几个设计意图:

  • 油耗项直接代表经济性,负号让算法去减小它。
  • SOC偏差用一个绝对值惩罚,保证算法不会只顾省油而把电池电量放空。SOC保持在参考值附近,代表电池能量"收支平衡"。
  • 启停惩罚是容易被漏掉的。如果只有前两项,模型往往学会频繁启动发动机发一小会儿电、然后又停机,出现几十秒内反复启停的抖动现象。加上瞬时切换惩罚后,算法会自动权衡"多发电一段时间"和"再启动一次"哪个更划算。

3.4 权重调参的一个实用顺序

α1、α2、α3怎么定?很多新手一上来就凭感觉写死,训出来的策略要么SOC严重偏低,要么发动机一直不关闭。我建议按这个顺序去调:

  1. 先把 α1 固定为1,其他系数全设0,训练一次。观察模型行为,通常它会学会完全不用发动机、一直放电,SOC会掉到下限。
  2. 加入 α2 的SOC惩罚,从0.1开始逐步增大,直到SOC轨迹能在工况结束时不跌出可接受范围。注意别把 α2 加得太大,否则模型会为了保SOC而频繁大功率充电,这时候油耗反而升高,发动机也经常在高负荷区运行。
  3. 最后加 α3 启停惩罚,从0.5开始试,观察启停次数曲线。如果启停次数还很密集,就继续调大,但要注意过大之后,模型可能干脆不停机,长时间怠速发电。

核心思路是"一次只调一个变量":先把油耗项跑通,再修正SOC约束,最后消抖。我自己调完的一组典型权重是 α1=1.0、α2=5.0、α3=2.0,但这不是什么标准答案,换一个车型、换一个工况分布,权重就得重新微调。

4. 训练基础设施与实测对比:从模型到工况验证

4.1 仿真环境:整车模型与发动机油耗模型

在把DQN放上车之前,仿真环境的可信度决定了策略的天花板。我的仿真环境包含以下几个部分:

  • 整车纵向动力学模型:根据目标车速和车辆参数,反推驱动轮端的驱动力,再结合车速得到驱动功率。这部分决定 P_req 的来源。
  • 电机和逆变器效率模型:用一张效率MAP(转速-扭矩-效率)查表计算电机端输入功率。
  • 电池模型:采用一阶等效电路模型,OCV(开路电压)和内阻都随SOC变化,内阻还要考虑温度修正。SOC变化量按电流积分计算。
  • 发动机模型:BSFC MAP查表,输入转速和扭矩,输出燃油消耗率。
  • 发电机组模型:发电机效率MAP,加上发动机输出机械功率到发电功率之间的转换效率。

这个模型精度不用追求特别高,但趋势要准。尤其是BSFC的低油耗区位置,如果和真实发动机偏差大,学出来的策略就会倾向于把发动机往错误的工作点带。我的建议是:仿真环境里最好留一个可配置的"模型误差注入"开关,后续做域迁移测试时能派上大用场。

4.2 训练超参数与工况组合设计

训练超参数我直接给一组能跑的配置:

参数数值说明
折扣因子 γ0.99让策略兼顾未来一段时间的油耗和SOC
学习率1e-3使用Adam优化器,后期可衰减
经验回放容量50000需要足够大到覆盖各种工况片段
批大小64常见稳妥区间
ε-greedy初值1.0前20000步线性衰减到0.05
目标网络更新周期200步硬更新,简单有效

工况组合是我觉得比超参数更值得花时间的环节。如果只用WLTC单工况训练,测试到UDDS时效果可能打折扣。我的做法是把多个标准循环工况(比如WLTC、UDDS、CLTC)按随机顺序拼接,再叠加一些坡度变化和随机加减速片段,组成更长的训练工况流。这样DQN见到的状态覆盖度大很多,泛化能力明显提升。

4.3 和基准策略的实测对比

我在仿真阶段做了三组对比:恒温器策略、功率跟随策略、DQN策略。测试工况用的是训练中没有出现过的另一组驾驶循环。为保证公平,三组策略的初始SOC相同,工况完全一致,评估指标包括百公里等效油耗、SOC终值偏移和发动机启停次数。

下面是我仿真中得到的一组示例性结果,不具备普适性,但能反映趋势:

策略百公里油耗SOC终止偏差启停次数备注
恒温器式基准基本保持较多工作点偏离经济区
功率跟随式略高较好多发动机工作点波动大
DQN明显降低稳定少工作点集中在高效区附近

从训练可视化里看,DQN策略下发动机工作点在BSFC图上的分布明显向低油耗区聚拢,启停切换也更果断。这个结果和很多已发表研究的结论方向一致:DQN在串混能量管理上能学出接近"发动机工作在最优线附近、电池充当缓冲"的启停逻辑。

5. 仿真到落地的五道坎:安全约束、泛化性与硬件部署

5.1 安全约束不能指望奖励函数兜底

仿真里一切理想,上车就变脸。我遇到的第一道坎就是约束问题。奖励函数里的SOC惩罚虽然能让策略在统计意义上维持SOC,但训练数据里出现的概率不等于实车会遇到的概率。比如极端长上坡、连续急加速,有可能把SOC带到训练时很少出现的低值区间,策略这时给出的动作可能并不安全。

工程上不能靠"希望策略学过"来保证安全。我的做法是在策略输出后面加一个独立的安全过滤层:如果当前状态预估会引发SOC越限,就强制切换到充电挡位;如果发电功率超过电池充电功率上限,就做功率钳制。简单说,规则脚本做兜底,强化学习做优化,不能混为一谈。

5.2 状态信号噪声与仿真到真实的落差

仿真里状态值都是干净的真值,实车上却是CAN总线信号,有噪声、有延迟、还可能丢帧。车速信号抖一下、需求功率跳变一下,如果策略对输入很敏感,动作就会跟着抖动。我实测中DQN本身对小幅噪声还好,因为训练时工况本身就有随机性,学出来的策略天然带有平滑倾向,但还是要做好输入滤波。

建议对输入状态做一阶低通滤波,时间常数取决策周期的1到2倍即可。另一个问题是训练环境和实车环境的偏差:电池内阻老化、发动机油耗MAP偏移、发电机效率变化,都会让策略表现下降。这个问题没有一劳永逸的解法,常见思路是定期用实车数据做微调训练,或者在策略网络后面接一个基于规则的修正层。

5.3 DQN的过估计问题

DQN有一个众所周知的问题:用同一个网络既选动作又算Q值,Q值会产生系统性高估(overestimation),导致策略次优。在能量管理这种连续决策任务里,过估计会让策略在选择挡位时倾向于"看起来Q值最高但实际收益没那么高"的动作。

解决成本最低的方式是换用Double DQN:动作选择用当前Q网络,Q值评估用目标网络。代码改动很小,几乎不增加训练时间,但能显著改善策略质量。如果想进一步提高样本效率和稳定性,还可以加优先经验回放(Prioritized Experience Replay)和Dueling网络结构,但这些都是增量优化,先把Double DQN加上,性价比最高。

5.4 部署算力与网络轻量化

很多工程师听到强化学习就觉得上车跑不动。实际上,一个输入只有5到8维、输出30到50个动作Q值的全连接网络,大概就是3层MLP、每层64到128个神经元,参数量在几万级别。这种规模在常见的域控制器甚至部分高性能MCU上都能跑,单次前向推理的时间远小于1毫秒,对控制周期来说完全够用。

部署时真正要花力气的不是网络推理,而是软件架构:强化学习模型输出一个动作索引,需要翻译成发动机转速和扭矩请求,再走原有VCU的扭矩仲裁、故障诊断流程。我的经验是,把策略模块做成一个独立的"能量管理建议器",输出给原有控制层,由控制层做最后的安全校验,这样既保留了强化学习的优化能力,又不破坏原来成熟的整车安全逻辑。

最后说一个我踩得最深的坑:第一版奖励函数里我只放了油耗项,训练到中途发现SOC一路掉到下限,而发动机策略完全"躺平"不启动。后来加了SOC偏差惩罚,又开始出现发动机几十秒反复启停的抖动。直到引入启停惩罚、又把SOC偏差做成死区(3%以内不惩罚),整个训练曲线才稳定下来。如果你也想在这块试水,我建议先从写好一个规则策略做基线开始,再让DQN去对比超越,这样每一步的改进都能量化,排查问题也不会像无头苍蝇。

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

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

立即咨询