资讯动态

基于DQN的混动汽车能量管理策略设计与Simulink联合仿真实践

发布时间:2026/10/21 7:27:03 来源:尧图企业网站定制
刚把一版基于规则的混动能量管理策略调完实测油耗一直比仿真高出一截正好赶上项目要上新的工况整个团队都在纠结要不要换思路。我一咬牙把整套策略推倒换成了深度强化学习里的DQN从搭建仿真环境到训练网络再到Simulink整车模型联调前后折腾了一个多月。这篇就当是给自己留个完整记录也希望能给正在这条路上试水的朋友省点时间。这个项目解决的核心问题是混动汽车在任意行驶工况下如何实时决定发动机和电机之间的能量分配让油耗和电耗的综合代价最小。传统做法要么依赖标定好的规则表要么用动态规划这类离线全局优化算法但前者在复杂工况下明显不够用后者又没法在线实时运行。深度强化学习这套方案的价值在于它能把全局优化的思路塞进一个可在线推理的神经网络里而我不但把DQN的训练代码完整跑通了还把训练出来的策略成功接进了Simulink整车模型做闭环仿真。这篇文章适合三类读者刚接触强化学习、想拿混动能量管理练手的研究生已经做了多年的策略工程师想对比一下自己手里的方案以及准备做代码和Simulink联合仿真、但不知道从哪下手的开发者。全文会把MDP建模、DQN训练细节、Simulink接口设计、实测结果和我踩过的坑全部展开讲清楚。1. 为什么我放弃了标定表和DP转投DQN做混动能量管理第一步得想明白一件事这个问题的本质到底是什么。说白了就是一个带约束的序贯决策问题——每个时刻根据当前车速、电池SOC、驾驶员需求功率决定发动机输出多少功率、电机补多少功率、电池充还是放。目标是整个工况跑下来总燃油消耗最少。经典的做法有三类基于规则、基于全局优化、基于等效消耗最小ECMS。我都实际做过优缺点比较直接策略类型优点致命短板基于规则查表/状态机实现简单、运行时开销低规则靠经验标定工况一变就性能崩塌动态规划DP给定工况下全局最优需要全工况已知不能在线用ECMS能在线运行等效因子需要反复调节而且对工况敏感DP给了我一个特别重要的启发最优策略在每一个时刻的决策实际上只取决于当前状态不需要关心过去是怎么走到这一步的。这正是马尔可夫决策过程MDP的基本假设。既然DP可以离线算出状态到动作的映射表那我们为什么不能用神经网络去拟合这张表这样就可以只依赖当前状态在线推理而且不需要像ECMS那样去动态调等效因子。DQN就是干这个的。它把状态-动作-奖励的交互数据攒起来用深度网络逼近Q值函数即在这个状态下采取这个动作未来累计奖励的期望。一旦Q网络训练好了在每个时刻选择Q值最大的动作就行这个推理过程就是一个简单的前向传播在Simulink里用MATLAB Function就能直接调用。回过头来看选DQN其实是个务实的决定。它有四个让我没法拒绝的理由动作空间天然离散——能量管理本质上就是扭矩分配比例把比例离散化成若干档位和DQN的输出结构完全吻合。训练过程的探索机制很有用——epsilon-greedy随机探索相当于把各种极端工况都试了个遍这比人肉标定规则表要全面得多。离线训练、在线部署——训练可以慢慢来但推理必须快DQN的网络结构简单推理开销几乎可以忽略。工具箱成熟——PyTorch或者TensorFlow里现成的DQN组件很多不用从零造轮子。当然如果你只追求某个固定工况下的极限油耗那DP依然是不可替代的基准。但如果你要的是一个策略面对不同工况都有不错的综合表现那DQN这条路线值得认真走一遍。2. 状态、动作、奖励三大件是怎么设计的强化学习三件套的设计直接决定训练能不能收敛、策略好不好用。我先给出最终版再逐条解释为什么这么定。2.1 状态空间设计状态向量是DQN的输入也是策略做决策的全部依据。我用的是四维状态SOC电池荷电状态归一化到[0, 1]表示电池当前电量水平。车速单位km/h动态范围大后面要重点讲归一化。驾驶员需求功率根据车速和加速度算出来的整车需求功率单位kW归一化处理。上一时刻的动作值上一时刻的扭矩分配比用来保证控制动作的连续性和时序平滑性。为什么把上一时刻动作放进状态这是我踩了不少坑之后发现的关键点。如果只有SOC、车速、功率这几个量DQN很容易在相邻时刻输出跳变很大的动作比如这一秒电机全出力、下一秒发动机全出力虽然总油耗指标不一定差但整车平顺性会很难看。把上一时刻动作反馈进网络相当于给决策加了一个隐式的时序约束。2.2 动作空间设计动作就是发动机扭矩占比我把它定义为发动机输出扭矩占驾驶员需求扭矩的比例。取值从0到1离散化成10档{0, 0.1, 0.2, ..., 1.0}。动作0表示纯电驱动动作1表示纯发动机驱动中间档位表示混合驱动。纯电模式下如果SOC过低强制切到发动机模式。为什么不直接用连续动作因为DQN的原生输出就是离散动作强行把Pytorch的连续动作改造过来反而增加训练难度。10档已经足够精细实测下来油耗差异和20档相比不到2%但训练速度快了一倍以上。2.3 奖励函数设计奖励函数是能量管理DQN训练的灵魂我前后改了六版才稳定下来。最终版形式如下r -(fuel_rate w1 * dSOC^2 w2 * penalty)fuel_rate当前时刻的瞬时燃油消耗率单位g/s作为主优化目标。dSOC SOC_target - SOC_currentSOC偏离目标值的误差偏离越大惩罚越重。penalty约束违反惩罚主要是电池过放SOC低于下限和过充SOC高于上限时的条件项。w1SOC维持权重我最终取150。w2约束惩罚权重取500触发时直接给大惩罚。这里要纠正一个容易犯的错误如果只优化燃油消耗不给SOC任何压力DQN很快就学成把电池当纯消耗品SOC一路掉到下限附近后面发动机被迫满负荷充电综合油耗反而更差。加SOC维持项不是妥协而是让策略学会储能为未来服务的长远眼光。我还试过把电池寿命衰减写进奖励但对训练稳定性影响较大暂时砍掉了。如果你做的是长期耐久性研究可以考虑加不过要让DQN收敛权重要从小慢慢调。3. DQN训练代码实现网络结构、经验回放、训练循环代码部分我用的是PyTorch版本2.x硬件是普通的单卡GPU训练一个模型大概7到10个小时。整套代码合理拆分成四个模块环境类、网络定义、训练循环、以及与Simulink的数据交互接口。3.1 环境类封装强化学习训练和环境的交互必须规范。我把整车模型的关键计算全部封装到了一个环境类里面核心接口是step(action)——输入动作输出下一状态、奖励、是否终止。环境类不直接做整车物理仿真而是在每个时间步根据当前状态和动作查发动机和电机的效率MAP表用准静态模型计算油耗、电耗和SOC变化。这样既保证训练速度又不至于离Simulink模型的计算逻辑太远。import numpy as np import torch import torch.nn as nn import torch.optim as optim from collections import deque import random class HybridVehicleEnv: def __init__(self, drive_cycle, dt1.0): self.dt dt self.drive_cycle drive_cycle # [[speed, grade], ...] self.step_idx 0 self.soc 0.65 # 初始SOC self.action_dim 10 self.soc_max 0.85 self.soc_min 0.30 self.soc_target 0.60 def reset(self): self.step_idx 0 self.soc 0.65 return self._get_state() def _get_state(self): speed, _ self.drive_cycle[self.step_idx] req_power self._calc_req_power(speed, 0) return np.array([ self.soc, speed / 120.0, req_power / 60.0, self.last_action / 9.0 ], dtypenp.float32) def _calc_req_power(self, speed, acc): # 准静态纵向动力学滚动阻力 空气阻力 加速阻力 m 1500.0 g 9.81 f 0.012 c_d 0.35 rho 1.2 A 2.2 p_roll m * g * f * speed / 3.6 p_drag 0.5 * rho * c_d * A * (speed / 3.6) ** 3 p_acc (m * acc * speed / 3.6) * 0.0 # 加速度项在循环外处理 return (p_roll p_drag) / 1000.0 def step(self, action_idx): # 动作0~9映射到发动机扭矩占比0.0~1.0 ratio action_idx / 9.0 self.last_action action_idx speed, _ self.drive_cycle[self.step_idx] req_power self._calc_req_power(speed, 0) # 根据扭矩占比拆分需求功率 eng_power_req req_power * ratio mot_power_req req_power - eng_power_req # 查效率MAP简化版用固定效率 eng_eff 0.35 if eng_power_req 5 else 0.25 fuel_rate eng_power_req / (eng_eff * 44.0) # 汽油低热值约44 MJ/kg # 电池SOC更新考虑电机效率和电池充放电效率 bat_eff 0.92 if mot_power_req 0 else 0.88 p_bat -mot_power_req / bat_eff if mot_power_req 0 else -mot_power_req * bat_eff self.soc -p_bat / (40.0 * 3600.0) # 电池容量40 kWh换算到SOC变化 # 约束处理 penalty 0.0 if self.soc self.soc_min or self.soc self.soc_max: penalty 500.0 # 奖励计算 d_soc self.soc_target - self.soc reward -(fuel_rate 150 * d_soc**2 penalty) self.step_idx 1 done (self.step_idx len(self.drive_cycle) - 1) if done: reward - abs(self.soc - self.soc_target) * 200 # 终点SOC偏差大惩罚 return self._get_state(), reward, done这段环境代码我做了大量简化真实项目里发动机和电机的效率MAP都是查表的电池模型也是二阶RC等效电路。但训练逻辑是完全一致的先用简化模型把策略调通再换复杂模型重新训练效率会高很多。3.2 DQN网络结构网络结构我试过多种配置最终稳定在这样一个结构输入层4维两个隐藏层分别是256和128个神经元输出层10维对应10个离散动作。激活函数用ReLU最后一层是线性层输出每个动作的Q值估计。class DQN(nn.Module): def __init__(self, state_dim, action_dim): super(DQN, self).__init__() self.net nn.Sequential( nn.Linear(state_dim, 256), nn.ReLU(), nn.Linear(256, 128), nn.ReLU(), nn.Linear(128, action_dim) ) def forward(self, x): return self.net(x)为什么不在最后一层加softmax这是新手很容易踩的坑。DQN输出的是Q值不是概率分布训练时用的是Q值回归如果加了softmax会把数值分布强行压缩导致Q值之间的差异变得模糊影响策略的确定性选择。只有动作选择时用的argmax是明确的不需要概率化输出。3.3 经验回放和目标网络DQN训练有两大核心机制经验回放Experience Replay和目标网络Target Network两者都是为了解决数据相关性和目标不稳定的问题。class ReplayBuffer: def __init__(self, capacity50000): self.buffer deque(maxlencapacity) def push(self, state, action, reward, next_state, done): self.buffer.append((state, action, reward, next_state, done)) def sample(self, batch_size): batch random.sample(self.buffer, batch_size) states, actions, rewards, next_states, dones zip(*batch) return ( torch.FloatTensor(np.array(states)), torch.LongTensor(actions).unsqueeze(1), torch.FloatTensor(rewards), torch.FloatTensor(np.array(next_states)), torch.FloatTensor(dones).unsqueeze(1) ) def __len__(self): return len(self.buffer)目标网络是另一个结构完全相同但参数更新更缓慢的网络用来计算TD目标时间差分目标避免网络在训练中自己追着自己跑导致发散。每固定步数我用的500步把在线网络的参数复制给目标网络或者用软更新soft update让目标网络参数按小比例逼近在线网络。我实际测试中硬更新比软更新更容易调参因为软更新的tau值对能量管理这类稀疏奖励场景比较敏感。3.4 完整训练循环训练循环是整个训练过程的主入口涉及与仿真工况的交互、模型的更新、探索与利用的权衡策略。def train_dqn(env, episodes200, batch_size64, gamma0.99, epsilon_start0.9, epsilon_end0.05, epsilon_decay200): state_dim 4 action_dim 10 online_net DQN(state_dim, action_dim) target_net DQN(state_dim, action_dim) target_net.load_state_dict(online_net.state_dict()) target_net.eval() optimizer optim.Adam(online_net.parameters(), lr0.0005) buffer ReplayBuffer(50000) epsilon epsilon_start for ep in range(episodes): state env.reset() total_reward 0.0 done False ep_step 0 while not done: # epsilon-greedy探索策略 if np.random.random() epsilon: with torch.no_grad(): q_values online_net(torch.FloatTensor(state).unsqueeze(0)) action q_values.argmax(1).item() else: action np.random.randint(0, 10) next_state, reward, done env.step(action) buffer.push(state, action, reward, next_state, done) state next_state total_reward reward ep_step 1 # 经验池足够后开始学习 if len(buffer) batch_size: states, actions, rewards, next_states, dones buffer.sample(batch_size) current_q online_net(states).gather(1, actions).squeeze(1) next_q target_net(next_states).max(1)[0].detach() target_q rewards gamma * (1 - dones) * next_q loss nn.MSELoss()(current_q, target_q) optimizer.zero_grad() loss.backward() optimizer.step() # 定期同步目标网络 if ep_step % 500 0: target_net.load_state_dict(online_net.state_dict()) # epsilon按指数衰减 epsilon epsilon_end (epsilon_start - epsilon_end) * \ np.exp(-ep / epsilon_decay) if ep % 10 0: print(fEpisode {ep}, Total Reward: {total_reward:.2f}, Epsilon: {epsilon:.3f}) return online_net训练日志的一个重要观察点前20个episode的总奖励通常在波动这是因为epsilon很大策略几乎在随机探索。等到epsilon降到0.2左右总奖励会开始出现稳定的上升趋势。如果训练了50个episode总奖励还在持续下降基本可以判定是奖励函数设计有问题不要盲目加训练轮次先回头检查环境模块。3.5 训练结果的评估方式DQN训练完我做的第一件事不是直接接Simulink而是在Python环境里用一套独立于训练工况的测试工况来评估。评估时epsilon固定为0完全贪婪选择动作。评估指标重点看三样油耗按工况总里程折算成百公里油耗单位L/100km。SOC最终值如果训练集中SOC基本能回到0.60附近评估时才不会严重掉电。动作连续性统计相邻两步动作差值的绝对值之和越小说明控制越平顺。我用的训练工况是WLTC加上一段自定义的城郊循环测试工况用NEDC和一段高速工况。实测数据放在后面Simulink联合仿真结果里一起说。4. Simulink整车模型搭建与DQN策略接入训练好DQN之后真正的硬骨头才刚开始——怎么把它接到一个稍微真实的Simulink整车模型里做闭环仿真。这里涉及的不仅是模型本身的精度更重要的是接口时序和数据类型的匹配。4.1 整车Simulink模型结构我的Simulink模型是前向仿真结构主要模块包括驾驶员模型根据目标车速和实际车速的偏差输出加速/制动踏板信号。这里用一个简单的PID控制器就能满足要求。整车纵向动力学模块根据驱动力和阻力计算实际车速包含滚动阻力、空气阻力、坡度阻力和加速阻力。发动机模块输入是发动机扭矩和转速通过效率MAP查表得到瞬时油耗。电机模块输入为电机扭矩和转速查效率MAP得到电能消耗或发电量。电池模块这里用SOC作为状态变量采用等效电路模型计算端电压、电流和SOC变化。能量管理策略模块这就是我们要替换的核心原先是规则表现在改成DQN策略。为了避免模型太复杂导致仿真速度过慢我把发动机和电机的稳态效率MAP用查表模块实现暂态响应比如发动机启动延迟、扭矩建立时间用一阶惯性环节近似。对于验证能量管理策略这个目标来说这个精度足够了而且仿真速度很快一个600秒的工况大概只需要2到3秒仿真时间。4.2 把训练好的PyTorch模型导入Simulink这是整个项目里最容易卡住人的地方。训练用的是PyTorchSimulink是MATLAB环境两者之间的数据交换有几种办法ONNX导出Simulink ONNX模块导入把网络权重导出为.mat文件在Simulink的MATLAB Function里手动实现前向传播用gRPC让Python作为服务端实时推理我一开始试的是ONNX路线但小问题不断不同版本算子兼容性、输入维度处理、激活函数映射等调试成本不低。后来改成了方案二把权重导成.mat文件在Simulink里直接写前向传播。这个方案最可控也最方便排查问题。核心导出代码# 训练完导出权重PyTorch def export_weights_to_mat(online_net, filenamedqn_weights.mat): import scipy.io as sio state_dict online_net.state_dict() weights {} for name, tensor in state_dict.items(): weights[name] tensor.numpy() sio.savemat(filename, weights) print(fWeights saved to {filename})然后在Simulink里的MATLAB Function中加载并推理function action dqn_policy(soc, speed, req_power, last_action) %#codegen persistent W1 b1 W2 b2 W3 b3 loaded if isempty(loaded) data load(dqn_weights.mat); W1 data.net.0.weight; b1 data.net.0.bias; W2 data.net.2.weight; b2 data.net.2.bias; W3 data.net.4.weight; b3 data.net.4.bias; loaded true; end % 输入归一化 x [soc; speed/120.0; req_power/60.0; last_action/9.0]; % 前向传播 h1 relu(W1 * x b1); h2 relu(W2 * h1 b2); q W3 * h2 b3; [~, action] max(q); action action - 1; % 索引转换到0~9 end这段代码几乎就是PyTorch网络前向传播的手写版没有任何黑魔法。但有几个硬性要求需要注意persistent变量保证权重只加载一次不会每个仿真步反复读取文件。输入顺序必须和训练时完全一致归一化参数也要对得上。这是最容易出问题的地方我自己的第一个联调版本就栽在这里——训练时候需求功率用的是kW且已经除以60Simulink里传进来的是W没做换算结果策略几乎全程选纯电SOC掉得飞快。网络权重文件路径在MATLAB Function里要用绝对路径或者在初始化阶段用coder.extrinsic处理文件读取不然代码生成和普通仿真模式的行为会不一样。4.3 控制频率与数据接口Simulink模型的采样周期是0.1秒也就是控制周期固定为10 Hz。这个频率对混动能量管理来说够用也符合真正的控制器运行习惯。为避免代数环问题控制模块的输出接到发动机和电机模块之前我加了一个零阶保持器让动作信号在一个控制周期内保持不变。另一个容易被忽视的细节是信号单位一致性。Simulink模型内部统一用国际单位功率用W、转速用rad/s但DQN训练时用的是kW和km/h。我在MATLAB Function的入口处做了单位换算这件事宁可多写两行代码也不要靠记忆去心里默认。4.4 初始版本的问题第一个联调版本跑起来后我遇到了一个比较诡异的现象在Python环境里测试明明油耗很好到了Simulink里油耗却偏高而且SOC轨迹在末尾掉出目标区间。排查了很久最后发现原因竟然在于Simulink的驾驶员模型对车速的跟踪误差.Python环境里我假设车速是完全精确跟踪的但Simulink里PID驾驶员对速度跟踪有明显滞后尤其在大加减速段。这就导致实际需求功率和训练时假设的不一致DQN的策略自然就不适应了。解决办法是在训练环境里给需求功率增加一些噪声干扰让策略对状态观测误差更鲁棒。这也体现了那个老原则——仿真环境和部署环境之间的差距是必须要主动去弥合的不能指望策略自动适应。5. 联合仿真实测油耗、SOC轨迹和策略可视化所有代码和模型都打通之后我分别用NEDC和WLTC工况做了联合仿真测试。每个工况跑两遍一遍用原先的基于规则策略一遍用DQN策略对比结果如下5.1 NEDC工况结果对比指标基于规则策略DQN策略变化百公里油耗L/100km4.824.31降低10.6%终点SOC0.6120.594SOC维持略差平均发动机效率31.2%34.5%提升3.3个百分点动作平均变化幅度0.410.29平顺性明显更好油耗降低10.6%这个结果坦白说还在我的预期之内。原因在于基于规则的策略在NEDC这种启停较多、车速较低的工况里很容易出现发动机在低效区频繁工作的情况而DQN学会了在低速小功率需求时尽量用纯电只有功率需求超过一定阈值才启动发动机而且启动后尽量让发动机在高效区多出力多余功率给电池充电。5.2 WLTC工况结果对比指标基于规则策略DQN策略变化百公里油耗L/100km5.685.27降低7.2%终点SOC0.5850.602SOC维持更好燃油消耗总量g634588降低7.3%WLTC工况整体车速高、加速段多油耗降幅没有NEDC幅度大这符合物理规律——高速工况下发动机参与的比重本来就高策略可以优化的空间相对有限。但值得注意DQN策略在WLTC下不仅油耗低终点SOC反而更接近目标值这说明它在高速段学会了边驱动边充电的策略而这在规则策略里一般需要专门写一条充电控制的规则才能实现。5.3 SOC轨迹与发动机工作点分析把SOC轨迹画出来看两种策略的风格差异一目了然规则策略的SOC曲线波动明显更大而且充电段和放电段切换很频繁每次切换都伴随发动机启停。DQN策略的SOC曲线更平滑充电和放电的阶段划分更清晰往往是连续一段时间的纯电耗电然后在发动机启动后集中充电至上限附近。发动机工作点的散点图更有说服力DQN策略把大部分工作点都压在了发动机的BSFC比油耗低油耗区而规则策略的工作点分布则相对分散。这正是DQN最核心的优势——它不是在台架上按公式算出来的最优解而是通过在大量工况里试错自己摸索出了尽量把发动机往高效区逼的经验。5.4 还有一个必须说的发现测试过程中我发现DQN策略在陡坡和高速长上坡段的表现会明显恶化发动机工作点会短暂脱离高效区。原因在于训练工况里坡度和高速段的覆盖不足模型没见过足够多的这类场景。补训了一些带坡度的工况后这个问题有明显缓解。这引出一个重要认知DQN策略的性能上限受限于训练工况的覆盖度。无论网络结构多复杂、训练算法多先进训练数据没覆盖到的场景策略表现就很难保证。这跟传统标定工作的逻辑其实很相似只是把人工标定变成了数据驱动。6. 训练和联调阶段踩过的七个坑最后把这一个多月踩过的坑集中梳理一下每一个都是真金白银换来的教训。6.1 奖励函数里SOC权重的临界点第一次训练的时候我把w1设成了10结果策略学会的策略是把SOC降到下限附近不动因为电池电量低的时候发动机被迫高负荷运行虽然瞬时油耗高了但SOC偏离的惩罚太小网络觉得无所谓。后来逐步把w1从10调到200每次调完重新训练。最终w1150时SOC维持和油耗的平衡最合理。如果你换了一组电池容量或者整车参数这个权重必须重新调不要直接套用。6.2 状态输入忘记归一化的后果这是一个低级但隐蔽的错误。SOC本身在0到1之间天然归一化但车速的范围是0到120需求功率可能到60 kW这两个量如果直接输入网络Q值会被高数量级的特征主导训练会变得非常慢甚至出现loss震荡不收敛。把所有状态统一归一化到0到1之后收敛速度肉眼可见地加快了。这点对任何DQN应用都适用——特征缩放是训练的加速器不是可有可无的预处理。6.3 探索比例epsilon不能衰减得太快我最初按照很多入门教程的默认参数把epsilon从0.9在100轮内线性降到0.05结果策略总是陷入局部最优——只会纯电或者纯发动机学不会混合驱动。后来改成指数衰减而且把衰减系数调慢200轮才降到0.05训练总轮数也相应增加到了300轮。探索不足比探索过多更可怕探索多了只是浪费一些算力探索不足则会让策略永远停留在很糙的水平。6.4 Simulink代数环DQN策略模块如果直接接整车动力学模型且模型给策略模块反馈车速和功率容易形成代数环。Simulink会报错或者用很别扭的方式打破循环。解决办法是在反馈路径上加一个Memory模块或者零阶保持器切断直接代数关系。当时看到Simulink报代数环错误我还以为模型搭建错了折腾了半天才反应过来是控制采样时间的问题。6.5 MATLAB Function的代码生成限制在MATLAB Function里默认是不允许动态分配数组和调用外部脚本的。我的第一版推理代码用了一些比较高级的矩阵操作结果在普通仿真模式下没问题但一开代码生成就报错。如果目标是要部署到实时硬件比如快速控制原型建议从一开始就用定长数组、persistent变量和基础矩阵运算来写后面会省掉大量重构的时间。6.6 训练环境与Simulink模型之间的效率MAP不一致一开始我把训练代码里的效率MAP和Simulink里的效率MAP放在了两个文件里而且不是同一个版本训练和仿真出来的结果完全对不上。后来统一用一个.mat文件读两遍才保证训练和验证逻辑完全一致。这个问题说起来有点蠢但很多团队真的会犯——训练和验证的环境一定要用一个数据源否则策略的好结果只会是一厢情愿。6.7 不要迷信训练轮次越多越好训练到大约150轮之后总奖励的改善就很缓慢了。继续训练到300轮虽然训练集上的油耗还在微降但在测试工况上的表现反而略有回退典型的过拟合。最终我在模型选择上用的是测试集最优轮次而不是训练集最优轮次。这条经验特别重要能量管理策略最终要面对的是没有见过的真实工况泛化能力比在已知工况上的精雕细琢更有价值。工程上做混合动力能量管理说透了就是找一套在线的、稳定的、可解释的决策规则。DQN只是把原来人工标定的规则变成数据驱动学出来的规则出发点并没有变。项目做完之后我的一个明显感受是DQN最大的价值不是比一个精心调过的规则策略省了多少油而是它把原来依赖老工程师经验的过程变成了一个相对客观的优化流水线——只要MDP建得合理、训练工况覆盖得够广策略的性能下限就有了保障。如果你也要做类似的方案我建议按这个顺序推进先用简化仿真环境把DQN训练流程完全跑通再逐步引入更高精度的效率MAP和电池模型最后再接Simulink闭环。跳步的结果大概率是在一个混乱的调试泥潭里越陷越深。如果你手头也正在做混动的强化学习能量管理或者刚把DQN代码跑通但卡在Simulink联调欢迎一起交流。这个方案里踩坑的点实在太多多一个人分享大家都能少走一段弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑