ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

信号交叉口下燃料电池混动汽车生态驾驶的双层凸优化控制

信号交叉口下燃料电池混动汽车生态驾驶的双层凸优化控制 互联燃料电池混合动力汽车FCHEV在信号交叉口下的生态驾驶控制我盯了挺久。这类工作几乎都是同一个套路利用V2I通信拿到红绿灯相位和倒计时信息在满足通行时间、车速、动力系统约束的前提下规划出一条省氢的驾驶轨迹同时把燃料电池和动力电池之间的功率分配一起算出来。这两年SCI一区上用双层凸优化做这件事的文章肉眼可见地多了起来原因也很实在双层结构把“车怎么开”和“能量怎么分”两个时间尺度完全不同的子问题拆开了又因为每个子问题都是凸的能快速得到全局最优解。这篇文章把我复现和二次开发这个方向时的建模思路、优化套路、Matlab代码组织方式和踩过的坑整理出来适合正在做能量管理、生态驾驶、车路协同控制方向的研究生和工程师参考。1. 这个课题到底在解决什么问题1.1 四个关键词拆开看先把这个标题拆成四个部分每部分对应一类技术点避免一上来就糊成一个“大而全”的黑盒。“互联”指的是车联网环境具体到这个场景就是车与基础设施V2I通信。车辆在接近交叉口时能拿到信号灯的SPaT信息即当前相位、剩余时间、下一周期时长这些信息是生态驾驶优化的前提条件。没有这个信息车辆就只能靠摄像头识别红绿灯识别到红灯再减速往往已经浪费了大量动能。“燃料电池混合动力汽车”是研究对象。这车不是单一燃料电池驱动而是燃料电池加动力电池组成的混合动力系统。燃料电池负责基础功率电池负责峰值功率和再生制动回收两者之间有一个功率分配问题。这个分配问题就是下层优化要解决的。“信号交叉口”是场景。城市路况里交叉口是能耗重灾区频繁停车起步、怠速等待、急加速冲过绿灯都会显著拉高氢耗和电耗。信号灯约束让速度规划不再是简单的两点间最优控制问题而是带有时序窗口约束的优化问题。“生态驾驶”是目标层。Eco-driving不是一个具体算法而是一整套驾驶策略在知道信号灯时序的前提下让车辆在绿灯窗口内不停车通过或者红灯无法避免时提前减速滑行尽量减少制动损耗和怠速时间。它的本质是“用信息换能量”。1.2 为什么这个组合能发到一区从学术角度看这个题目能发一区不是因为它用了多复杂的数学工具而是它踩准了三个问题的交叉点。第一是乘员安全和能耗的矛盾。规划车速不能牺牲舒适性和安全性加速度必须平滑停车线不能闯。这个约束组合让问题具有一定难度。第二是时间尺度上的耦合。速度轨迹的变化是秒级到十秒级动力电池SOC的变化是分钟级燃料电池输出功率的动态响应又是毫秒到秒级。直接在同一个优化框架里同时处理这些不同尺度的变量会让问题非凸或者规模爆炸。双层结构恰好把不同尺度分隔开上层管秒级的车速下层管分钟级的能量分配。第三是可证明的最优性。很多生态驾驶文章用启发式、动态规划或者强化学习来做结果能不能全局最优很难说。凸优化则不同只要建模正确解出来的就是全局最优这对学术论文来说是非常强的卖点。审稿人看到“凸优化保证全局最优”这句话天然就会对结果多一分信任。2. 建模从车辆动力学到信号灯约束2.1 车辆纵向动力学和燃料电池混动系统用什么模型建模是整个工作的地基模型选不好后面所有优化都是空中楼阁。我做这个项目时采用的是工程上最常用的纵向动力学离散模型。把一段从当前位置到交叉口的距离分成N个时间步每步时间间隔固定为dt通常取0.1秒到0.5秒。状态变量是位置s、速度v、加速度a离散动力学写成s_{k1} s_k v_k * dt 0.5 * a_k * dt^2 v_{k1} v_k a_k * dt这两个等式在优化里是标准线性约束直接写进约束矩阵即可。速度有上下限加速度也有上下限加速度上下限实际上隐含了舒适性约束和路面附着约束。需求功率由纵向动力学推算F_res m * g * f_r 0.5 * rho * C_d * A * v^2 P_req (m * a F_res) * v / eta_drivem是整车质量f_r是滚动阻力系数C_d是风阻系数A是迎风面积eta_drive是传动效率。这里的F_res是速度的二次函数P_req里就出现了v的三次项后面优化时要注意处理凸性问题。燃料电池混合动力系统的模型不能太复杂也不能太简单。我用的是PEMFC加锂电池的构型功率平衡关系写成P_req P_fc P_batP_fc是燃料电池输出功率P_bat是电池功率电池放电时为正充电时为负。电池SOC的动态用简化模型SOC_{k1} SOC_k - P_bat[k] * dt / (3600 * Q_bat)这个模型假设电池端电压近似恒定忽略了内阻损耗的细节但对功率分配优化来说已经够用。燃料电池的瞬时氢耗用二次函数拟合m_H2 c0 c1 * P_fc c2 * P_fc^2这里的关键是拟合系数必须保证二次项系数为正即氢耗函数是凸的。高保真的燃料电池效率MAP表往往是凹的或非凸的直接进优化框架会破坏凸性所以二次拟合成凸函数是必要妥协。2.2 信号交叉口的红绿灯信息怎么进优化框架信号灯约束是这个问题的灵魂也是建模时最容易出bug的地方。核心约束是车辆在红灯期间不允许越过停车线。如果用t_pass表示车辆通过交叉口停车线的时刻那么t_pass必须落在绿灯窗口集合里。绿灯窗口由SPaT信息决定。假设信号周期固定已知绿灯起始时间和时长可以得到一系列窗口区间 [t_g1_start, t_g1_end], [t_g2_start, t_g2_end], ...如果直接用这个约束t_pass ∈ 任意一个绿灯窗口这本质上是一个“或”逻辑不是凸约束。标准凸优化框架处理不了。我的处理方式有三种按适用场景选。第一种是枚举法对每个候选绿灯窗口单独求解一次优化得到若干可行速度轨迹从中选氢耗最小的。这个方法最简单也最稳妥缺点是计算量随候选窗口数量线性增加。第二种是引入二进制变量把“或”约束转成混合整数二次规划MIQP。每一段绿灯窗口对应一个二进制变量表示是否在该窗口通过约束变成t_pass t_g_start - M * (1 - z_i) t_pass t_g_end M * (1 - z_i) sum(z_i) 1这里M是大数。MIQP的求解速度比纯QP慢而且Gurobi处理MIQP的耗时在小规模问题上还能接受但一旦时间步数N变大商业求解器也会吃力。第三种是滚动时域策略在每个控制周期只考虑最近的一个或两个绿灯窗口不一次性规划整个通行过程。这个策略接近实车控制器后续做实时部署时我倾向于这个方案。2.3 目标函数和约束怎么写得“凸”凸性是这个方法的上限决定因素。模型写得好不好直接体现在目标函数和约束能否被写成凸形式。我踩过的数次坑基本都是这里出了问题。上层速度规划的目标函数最直观的写法是最小化总通行时间和能耗的加权和J_upper alpha * T_total beta * 能耗近似项这里T_total是总通行时间能耗近似项我最开始尝试直接用P_req对时间的累加。但P_req里含有v的三次方项v是决策变量v^3在变量非负时虽然是凸的但和加速度a相乘后变得非常复杂联合求解时往往不是凸的。解决方式是把能耗项拆开处理。风阻项0.5 * rho * C_d * A * v^3在v非负时其实是凸的但为了保持整体的凸二次规划形式我经常将v^3用一个分段线性函数近似或者把v^3替换成v * v^2并引入辅助变量做松弛。另一个更省事的方式是在目标函数里不显式写那个复杂的瞬时功率项而是用v的二次项和a的二次项来近似能耗。结果就是J_upper alpha * T_total beta * sum(v.^2) gamma * sum(a.^2)其中sum(v.^2)鼓励平稳行驶避免过高车速导致风阻快速增加sum(a.^2)是舒适性惩罚同时也能减少不必要的加减速。这种写法的好处是整个目标函数是一个关于v和a的凸二次型约束也全是线性的标准二次规划求解器几秒钟内就能解完。下层能量管理的目标函数相对简单J_lower sum(m_H2(P_fc[k])) lambda * (SOC_N - SOC_ref)^2S_COM终端SOC约束用惩罚项代替硬约束lambda取一个较大值既能保证SOC最终回到参考值附近又不会把优化问题变得过紧。约束方面是功率平衡、燃料电池功率上下限、爬坡功率限制和SOC上下限全部线性。3. 双层凸优化算法上层管速度下层管能量3.1 为什么必须拆成两层有人可能会问能不能把速度规划和能量管理合并成一个大优化问题直接求解理论上可以实际操作起来很难。原因在于速度规划需要的决策变量是位置、速度、加速度时间尺度以秒计而能量管理需要的是燃料电池功率序列、电池功率序列、SOC轨迹时间尺度长得多。两个子问题的变量数量加起来会非常可观联合求解时目标函数的耦合项容易导致问题的Hessian矩阵不再正定凸性荡然无存。拆成两层之后的优势非常明显上层问题的变量规模只有3N个左右下层只有2N个左右两个问题都是标准QP求解稳定高效。而且从工程角度看“先规划轨迹再分配能量”本身就是车辆控制器常用的分层架构上层对应整车控制器下层对应能量管理控制器物理含义清晰。代价是两层之间的模型精度会有一定的近似损失。上层不知道下层的氢耗函数有多精细下层认为上层给出的速度就是最终的执行轨迹两者之间的误差需要通过迭代反馈来弥补。3.2 上层速度规划问题怎么建上层问题的输入是当前位置、当前速度、信号灯SPaT信息、道路限速。输出是一整条离散速度轨迹和时间序列。正式开始建模之前先判断一个问题车辆能在当前绿灯窗口内通过吗如果从当前位置以最大允许速度行驶仍无法在绿灯结束前到达停车线那当前绿灯窗口不可行直接锁定下一个绿灯窗口作为目标如果即使以最小速度巡航也能在绿灯结束前到达那可以选择一个较平缓的速度轨迹。实际场景中更常见的是中间情况踩油门可以冲过去松油门可以用下一个绿灯窗口通过。这时候优化就要权衡早到还是晚到。比如说当前绿灯还有15秒结束距离停车线200米最大速度15米每秒最小速度5米每秒。以15米每秒可以在13.3秒通过以5米每秒需要40秒只能等下个绿灯。上层的QP会在这两种可行方案之间根据alpha和beta的权重自动选择综合代价更小的策略。上层QP的决策变量序列为s、v、a每个都是长度为N的向量。约束包括运动学等式约束、速度区间约束、加速度上下界约束、停车线位置约束和信号灯窗口约束。目标函数按前面说的凸二次型构建。我习惯在目标函数里额外加一项小正则项比如1e-4倍的v对参考速度差值的平方目的是让优化问题数值上更稳定避免出现解的唯一性问题导致求解器反复试探。3.3 下层能量管理问题怎么建下层问题的输入是上层算出的速度轨迹通过车辆动力学模型算出每一时刻的需求功率P_req序列。输出是燃料电池功率P_fc和电池功率P_bat的序列。下层问题本质上是一个带约束的功率分配问题。目标是让整趟行程的氢耗最小同时维持SOC在安全区间。约束包括功率平衡等式P_fc P_bat P_req燃料电池功率上下限电池功率上下限燃料电池的爬坡约束以及SOC的动态方程和上下限。这里有一个细节容易被忽略燃料电池的爬坡约束。燃料电池输出功率不能突变否则会影响电堆寿命。这个约束是线性的形式如下-delta_H2 P_fc[k1] - P_fc[k] delta_H2delta_H2的典型值在1到5千瓦每秒之间取决于燃料电池的型号。加上这个约束之后下层QP的解会更接近实际而不是出现燃料电池功率在相邻时刻之间大幅跳动的理想化结果。SOC的初始值我想强调一下。很多教程把SOC初值设成0.8终端不加约束跑出来各种奇怪的解。我一般都设SOC_0 0.5再在目标函数里加终端SOC惩罚项让优化器在氢耗和SOC保持之间取折中。这样得到的功率分配结果更符合实际运行逻辑因为电池不能只用来压榨氢耗而不考虑自身电量维持。3.4 两层之间的迭代与信息传递双层问题写完之后如何把两个子问题串起来是算法设计的核心。第一种方法是简单的顺序求解即先解上层得到速度轨迹再解下层得到功率分配一次性结束。这个方法快但精度差因为上层用的能耗模型是近似模型和下层精细模型之间的偏差没有反馈。第二种方法是不动点迭代。流程如下给一个初始速度轨迹猜测通常按匀速巡航算。根据当前速度轨迹解下层问题得到氢耗序列和总氢耗。将下层氢耗对速度的敏感性即KKT乘子或数值梯度反馈给上层。上层在目标函数中增加一个拉格朗日修正项重新求解速度轨迹。重复步骤2到5直到两次迭代之间的总氢耗变化小于阈值。我在实际代码中遇到过迭代来回震荡的情况后来加了阻尼更新v_new (1 - rho) * v_old rho * v_solvedrho取0.3到0.6之间显著改善了收敛性。实测下来这个简单的不动点迭代通常能在5到15次迭代内收敛每次迭代求解两个QP的时间加起来不到1秒整体计算时间可以接受。如果追求更严格的数学性质可以把下层的KKT条件显式写入上层的约束得到一个带互补约束的数学规划问题MPEC但这类问题本身非凸求解难度更大不推荐在工程实现中碰。4. Matlab代码实现从建模到仿真复现4.1 求解器和优化工具箱怎么选Matlab环境下的优化求解器选择直接影响开发效率和求解速度。我首选的是YALMIP加Gurobi组合。YALMIP负责把优化问题从代数形式转换成求解器能接受的格式Gurobi负责实际的QP求解速度比Matlab自带的quadprog快很多学术版license也不难申请。如果只装了Matlab基础包没有Gurobi用CVX加SDPT3也跑得动但速度明显慢尤其是双层迭代需要解几十次QP的时候。CVX的优势是建模语法简洁对新手友好劣势是每次求解前会做一轮预解析循环里反复调用时开销很大。我个人推荐对比一下两种建模方式的适用场景下面这张表是我自己的选型经验。对比项YALMIP GurobiCVX SDPT3建模语法稍微繁琐但线性约束直观简洁接近数学表达求解速度快QP级别慢跑较大规模QP吃力循环内重复求解适合可复用变量定义不适合预解析开销大处理MIQP支持支持但不推荐安装配置复杂度需额外装Gurobi只需Matlab和CVX我的建议是如果目标是把论文里的算法验证跑通CVX够了如果目标是做参数扫描、双层迭代或者后续扩展对比实验趁早换成YALMIP加Gurobi。4.2 代码模块怎么划分写Matlab代码最忌讳的就是把所有逻辑塞到一个大脚本里。我这个项目的组织方式是把每个功能拆成独立函数主脚本只负责调用和汇总结果。基本模块如下main.m主入口设定参数、调用求解、输出结果。init_parameters.m定义车辆参数、道路参数、信号灯参数和优化权重。generate_signal_timing.m根据信号周期生成绿灯窗口序列。solve_upper_speed.m接收SPaT信息和车辆状态返回速度轨迹。solve_lower_energy.m接收速度轨迹返回燃料电池功率、电池功率和SOC轨迹。plot_results.m把速度和能量结果画成图。这样拆分的好处是可以单独调试某个模块比如只调上层速度规划不跑能量管理或者固定速度轨迹只测下层的功率分配逻辑。互不干扰。4.3 关键代码结构示例下面给出一段上层速度规划的YALMIP建模示意代码说明核心的变量定义和约束写法。这段代码不能直接运行只是一个结构模板写清楚sdpvar怎么定义、约束怎么加、求解目标怎么设。% 参数: N 时间步数, dt 时间间隔, v_min/v_max 速度界限 % a_min/a_max 加速度界限, s_stop 停车线位置 x sdpvar(1, N); % 位置 v sdpvar(1, N); % 速度 a sdpvar(1, N); % 加速度 Constraints []; % 初值 Constraints [Constraints, x(1) 0, v(1) v0, a(1) 0]; % 运动学递推 for k 1:N-1 Constraints [Constraints, x(k1) x(k) v(k)*dt 0.5*a(k)*dt^2]; Constraints [Constraints, v(k1) v(k) a(k)*dt]; end % 边界约束 Constraints [Constraints, v_min v v_max]; Constraints [Constraints, a_min a a_max]; Constraints [Constraints, x s_stop]; % 信号灯窗口, 以枚举法为例, 只选某个目标窗口 Constraints [Constraints, x(N) s_stop]; % 停车线到达约束 % 到达时间由位置递推隐含, 若要限制到达时刻在窗口内, 可加时间变量 t_pass N * dt; % 简化示例, 实际需要显式时间变量 Objective sum(N - (1:N)) * dt 0.5 * sum(a.^2); ops sdpsettings(solver, gurobi, verbose, 0); optimize(Constraints, Objective, ops); v_opt value(v);实际中如果要显式约束到达时刻在绿灯窗口内我会增加一个时间变量t然后加约束t N*dt以及窗口上下界。上面代码做了简化。下层能量管理的代码结构类似变量变为P_fc和P_bat核心约束是功率平衡和SOC递推。SOC递推实际上是一组线性等式可以直接写进约束不需要额外调用求解器。4.4 仿真参数和验证场景怎么设置参数的合理性决定仿真结果的说服力。我给一组常见参数是我在项目里用过而且验证过物理合理的参数数值整车质量m1500 kg滚阻系数f_r0.015风阻系数C_d0.3迎风面积A2.2 m^2传动效率eta_drive0.9燃料电池最大功率50 kW电池容量6 AhSOC上下限0.3 / 0.9限速15 m/s信号周期60 s绿灯时长30 s交叉口距离300 m验证场景我建议至少设三组。第一组是“绿灯可直接通过”验证优化器给出匀速通过轨迹。第二组是“红灯无法避免”验证车辆提前减速滑行并在停车线前等待。第三组是“绿灯还剩半程”验证优化器是否选择加速通过还是放弃减速等下一周期这个场景最能体现权重alpha和beta的博弈。仿真完成后对比以下指标总通行时间、总氢耗、SOC终值、最大减速度。理想情况下生态驾驶策略相比不做速度优化的基准策略比如看到红灯才刹车氢耗能降低10%到25%具体数字取决于场景。5. 我踩过的坑和调试经验5.1 凸性被破坏的几种情况凸性问题是这个项目最容易翻车的地方而且翻车方式五花八门。我遇到的最常见的情况是在目标函数里加入了v和a的乘积项比如模拟瞬时功率时写P m * a * v这个项在变量同时包含加速度和速度时很难保持凸性。CVX或者YALMIP会直接报错或者给出非预期的结果。排查这种问题我会写一个最小的测试脚本固定其他变量单独检查目标函数矩阵的特征值。如果目标函数的二次型矩阵不是半正定的就说明目标函数本身不凸。另一个常见的坑是把信号灯窗口的“或”约束直接当成线性约束写进问题导致可行域变成非凸集合。优化器给出的解可能是“穿过”红灯的物理不可行轨迹。这个问题的排查方法是画图看速度轨迹是否在红灯时段越过停车线如果越过了几乎可以断定是约束建模问题。5.2 双层迭代不收敛怎么处理双层迭代不收敛的表现主要有两种一是SOC终值不满足要求二是速度轨迹在两次迭代之间来回跳动总氢耗不下降。SOC问题通常出在下层目标函数里的终端惩罚项权重太小。解决方法是把lambda从100逐步往上调调到500甚至1000直到SOC终值落在参考值附近。还有一种更直接的方式是给SOC设置一个终端硬约束但硬约束会让可行域变小有时候会直接导致问题无解所以要谨慎。速度轨迹来回跳的问题我前面提到过用阻尼更新解决。实际操作中还要注意信息传递的量纲。上层反馈给下层的是速度轨迹下层反馈给上层的是氢耗对功率的敏感系数。如果两边的量级差太多修正项会淹没主目标函数。我习惯把敏感系数除以一个标准化因子让修正项的量级和主目标项保持一致。5.3 求解太慢怎么办求解慢是双层迭代最影响体验的问题。我的经验是先减少时间步数dt从0.1秒放宽到0.2秒N减半求解时间几乎以平方级别下降但速度轨迹的精度损失很小因为城市生态驾驶本来就不需要极高的时间分辨率。第二个有效手段是避免在迭代循环里反复调用sdpvar定义变量。变量定义放在循环外循环里只更新目标函数和约束的具体数值利用YALMIP的assign和value机制复用已有模型速度能提升好几倍。第三个手段是给求解器设置合适的容许误差。QP问题不需要把最优解精度逼到1e-9设置OutputFlag关闭日志输出再把求解器容差从默认的1e-8放宽到1e-4速度提升非常明显而氢耗差异在0.1%以内。5.4 从仿真到高保真验证还有多远Matlab里面跑通的双层凸优化只是第一步离实际部署还差好几个层级。我常用的验证路径是先用低阶模型验证算法逻辑再用高保真模型验证控制效果最后才考虑硬件在环。高保真模型里必须加入低阶模型忽略的项比如坡度、道路曲率、传动系统动态、燃料电池响应延迟。模型精度提升后经常出现的情况是低阶模型里SOC轨迹很平滑高保真模型里因为附加阻力项SOC出现明显波动下层能量管理可能出现频繁切换充放电的状态。这个问题的本质是低阶模型的目标函数里没有加入对电池功率波动的惩罚在高保真验证时加上一个小的波动惩罚项可以显著改善控制效果。通信延迟也是仿真到实车的一大差距。实车场景下V2I信号有几十毫秒到几百毫秒的延迟SPaT信息不是完美的。处理方式是在上层速度规划时对绿灯窗口做保守处理把窗口的结束时间提前0.5秒留出安全余量。我个人做这个方向最大的感受是“双层凸优化”听起来高端实际上大部分时候就是“两个QP来回迭代”加上“把非凸项想方设法写凸”。只要把车辆模型、信号灯约束、下层能量管理写清楚从Matlab到仿真验证的链路其实很快。最后提醒一句不要在信号灯窗口的“或”约束上硬上二进制变量除非问题规模很小。先枚举候选周期大概率够用。如果后续想做实时部署可以把上层换成查表或神经网络下层保留QP这又是一个能写文章的扩展方向。
返回列表