
1. 五辆电动车排成一队难点到底在哪先说个场景高速路上五辆电动汽车以相同速度保持编队行驶车距 15 米左右。前面那辆稍微点一下刹车后面四辆必须在几百毫秒内同时降速而不是等前车刹车灯亮起、再一辆接一辆地被动反应。这就是协同自适应巡航控制CACC要解决的核心问题。而把这个系统做出来控制算法选什么、架构怎么搭会直接影响整个项目能不能跑通。这个项目选择了模型预测控制MPC作为核心算法采用分布式控制架构让 5 辆车各自携带局部控制器通过车与车之间的通信交换状态信息最终利用上下分层控制方式完成从决策到执行的闭环。我最初接触这个课题时团队给的要求非常明确一定要用 MPC、一定要分布式、一定要能扩展到任意车辆数。后面才发现这三个“一定要”背后其实藏着大量工程细节不是把论文里的公式抄一遍就能跑通的。如果你正准备做类似的编队控制课题或者已经在做自适应巡航但想换用 MPC 方案这篇文章值得花十分钟读完。我会把为什么选 MPC、分层架构怎么拆、分布式通信怎么设计、仿真怎么搭、以及我在实测中踩过的坑全部按实际项目的推进顺序讲一遍。有些结论可能会颠覆你最初的直觉比如分布式 MPC 最大的难点根本不是控制器设计而是通信时序和代价函数权重之间的配合。2. 为什么偏偏是 MPC从单车道跟随到队列协同的必然选择2.1 PID/LQR 在队列场景下的三个软肋很多做自适应巡航的入门项目都从 PID 开始。单车上确实可行读取前车距离和相对速度算个差值PID 输出油门和刹车。问题在于一旦放到五车队列里PID 的局限会被迅速放大。第一个软肋是“事后纠错”。PID 是反馈控制只有误差已经出现才会动作。在队列场景中前车减速的信息传到后车需要时间后车反应又需要时间这个滞后在五辆车里会像波纹一样逐级放大最终导致“车队追尾式振荡”。术语叫弦不稳定string instability通俗讲就是第一个车只抖了一下后面的车越抖越厉害。第二个软肋是没法处理硬约束。队列控制必须保证车间距不低于安全下限加速度不能超过舒适性要求速度不能超限。这些在 PID 框架里只能靠限幅器截断而截断意味着控制输出被硬生生丢掉了一截系统可能因此失去稳定性。LQR 虽然能优化一段轨迹但它同样不擅长处理不等式约束。第三个软肋是“看不见未来”。队列里经常出现前车在未来几步内要急减速的情况如果控制器只能基于当前时刻做反应那它永远处于被动状态。而 MPC 的基本思路恰恰是“基于预测做滚动优化”——它会把未来一段时间的状态变化算出来再根据这个预测选择当前最优动作。2.2 MPC 的预测能力正好命中协同的痛点MPC 的预测能力在做队列协同时有三个直接收益提前减速前车发送未来的加速度轨迹后后车控制器能提前规划自己的减速曲线而不是等距离逼近了才大幅度刹车。硬约束内联安全车距、加速度上限、速度上限可以直接写进优化问题的约束条件控制器输出的解天然满足这些条件不需要额外截断。多目标权衡间距误差、速度一致性、控制量平稳性、能量消耗这些常常互相矛盾的指标可以统一放进代价函数里用权重系数调节优先级。在五车编队中我最看重的是第二点。做纯底层的执行控制你可能感觉不到约束的价值但到了队列层面安全车距这种东西就像高压线——你不能靠“尽量不碰”来保证安全必须把它变成硬约束。2.3 一个不得不提的误区MPC 等于玄学调参不少同行看到 MPC 就先皱眉头觉得代价函数权重、预测时域这些参数全是玄学不如 PID 三个系数好调。这个观点有一部分是对的但只适用于你把 MPC 当成一个黑盒来用的情况。如果理解了每个参数的物理含义调参是有迹可循的预测时域代表“你看多远”控制时域代表“你一次决定未来几步”权重代表“你对哪类误差更不能容忍”。后面我会专门用一节讲这组参数怎么配这里先记住一个结论MPC 参数多但每个参数都能对应到实际工程语言比传统方法多出来的自由度恰恰是它能适应复杂需求的资本。3. 上下分层架构决策层面和执行层面分开管3.1 为什么不能用一个 MPC 管到底做整车纵向控制一个最自然的想法是用一个 MPC输入前车状态直接输出电机扭矩和制动压力一步到位。我最初也这么想过但被一位资深工程师一句话点醒“你想让 MPC 在 50 毫秒内算完队列决策又想让它在 1 毫秒内跟上电机响应——两种频率根本没法共存。”这就是分层架构的根本原因上层决策对时间精度要求低但涉及复杂优化下层执行对时间精度要求高但逻辑相对简单。把两者合并到一个控制器里要么牺牲决策质量去迁就计算耗时要么牺牲响应频率去迁就优化计算两头都不讨好。所以系统被拆成两层上层协同决策层。每个采样周期我最终用的是 50ms接收自车状态、前车状态、通信链路传来的前车预测轨迹通过求解 MPC 优化问题输出一个“期望加速度”。下层执行跟踪层。以更高频率比如 10ms 或 20ms 周期接收期望加速度通过逆纵向动力学模型把它换算成电机扭矩指令和制动压力指令并驱动执行机构达到指定加速度。上层只关心“这辆车下一步应该以多快的加速速度走”下层只关心“怎么让实际加速度老老实实等于期望值”。职责清晰调试时也好定位问题——如果整车加速响应慢你不会去怀疑 MPC 模块而是直接查下层执行器的响应带宽。3.2 上层决策到底在算什么上层 MPC 的输入输出都非常简洁。输入包括自车当前的位置、速度、加速度前车通过通信发送过来的位置、速度、加速度预测轨迹期望车距由当前车速和设定的时距计算道路限速等约束条件输出只有一个值期望加速度。别小看这个单值输出。它是在未来一段时间内对整车行为模拟推演后的最优当前决策。举个例子如果前车在 0.5 秒后要急刹车上层会根据预测在 0.5 秒之前就开始输出负加速度而不是等相对距离突然变近了才反应过来。3.3 下层执行器怎么跟上去下层收到期望加速度之后需要把加速度需求转换成物理控制量。这一步的换算逻辑是F_demand m * a_expect F_resistance F_aero其中 F_resistance 是滚动阻力F_aero 是空气阻力。以我使用的模型参数为例整车质量取 1500kg滚动阻力系数 0.012空气阻力系数 0.28迎风面积 2.4m²在车速 60km/h、期望加速度为 1.2m/s² 时需要的驱动力大约是F_demand 1500 * 1.2 0.012 * 1500 * 9.8 0.5 * 0.28 * 2.4 * (16.7)² ≈ 1800 176 94 ≈ 2070 N对应到电机扭矩还需要考虑轮胎半径和传动比。如果是再生制动与液压制动协同还要根据减速度大小决定分配比例。这一层没有任何优化逻辑纯粹是“伺候好执行机构”的体力活但它是整个系统能不能精确复现期望加速度的关键。根据我个人的对比测试下层控制器的响应带宽至少要比上层决策频率高一个数量级。如果上层 20Hz下层只有 30Hz那整个闭环的实际表现会大打折扣因为上层轨迹还没执行完下一次决策已经来了执行器会一直处于追赶状态。3.4 分布式架构和集中式的取舍既然聊到架构就不得不解释为什么这个项目坚持采用分布式而不是集中式计算。集中式方案一辆“中心车”收集全部 5 辆车的状态一次性求解一个 5 辆车联合的 MPC 优化问题。这个方案在理论推导时很清爽——所有耦合关系都在一个优化问题里全局最优性有保证。但它的瓶颈也非常明显优化变量数量随车辆数增长问题规模成倍增加求解时间难以控制在实时范围内。而且一旦中心节点故障整个车队控制就瘫痪了。分布式方案每辆车只“看到”自己和前车的状态各自求解局部 MPC 问题。控制目标里把“与前车保持期望距离”和“速度跟踪前车”作为耦合项写进代价函数。这样每辆车的优化问题规模完全不受车队总长度影响加一辆车就多一个局部控制器不用重新设计通信拓扑。当然分布式不是没有代价。局部求解意味着它本质上不是全局最优但工程上这个损失完全可以接受因为分布式带来了两个实打实的好处单点故障不会拖垮全队以及车队规模可以任意扩展。在做这个模拟项目时我一开始还担心分布式会不会让队列尾部震荡失控后面通过调节权重系数发现只要通信周期稳定、权重配比合理分布式方案的表现和集中式几乎没差别。4. 分布式队列 MPC 的核心算法预测模型、代价函数与约束配置4.1 车辆纵向运动学模型做队列控制不需要上什么复杂的 7 自由度动力学模型——那是给底盘控制用的。队列层面的 MPC 用三阶运动学模型就够了p_i(k1) p_i(k) Ts * v_i(k) 0.5 * Ts² * a_i(k) v_i(k1) v_i(k) Ts * a_i(k) a_i(k1) a_i(k) Ts * u_i(k)其中 p 是位置v 是速度a 是加速度u 是控制输入。这里的关键是控制量 u 选的是“加速度变化率”jerk而不是加速度本身。这样设计的好处有两个约束加速度变化率可以直接写进 MPC 约束条件而加速度变化率直接关联到乘坐舒适性——如果这个值太大乘员会明显感到“冲”或“顿挫”控制量变成了平滑性惩罚项输出的加速度轨迹不会上下跳变。Ts 是采样周期我最终取的是 50ms。状态空间形式可以写成x(k1) Ad * x(k) Bd * u(k) y(k) Cd * x(k)实际做离散化时直接套用精确离散化公式不用特别担心近似误差因为 50ms 的采样间隔对纵向运动学来说足够密。4.2 代价函数设计三部分怎么平衡MPC 的每一步优化本质上都在最小化一个代价函数。我把每辆车的代价函数拆成三大项第一项间距误差惩罚J_distance w1 * Σ ( d_i(kj) - d_desired(kj) )²其中 d_i 是实际车距d_desired 是期望车距。这里用上了恒定时距策略CTHd_desired t_h * v_i d0t_h 是时距参数我取 0.8 秒d0 是最小停车间距取 2 米。在实际车速 60km/h16.7m/s下期望车距大约就是 15.4 米。CTH 策略比固定间距策略好的地方在于车速越高跟车距离越大高速下的安全裕量天然有保障。第二项速度跟踪惩罚J_velocity w2 * Σ ( v_i(kj) - v_i-1(kj) )²这一项让每辆车的速度尽量贴上前车。D 注意这里用的是“速度误差”而不是“相对速度误差”因为队列控制的目标是让所有车最终以同一速度行驶同时维持期望间距。如果只消除相对速度整个队列可能整体漂移。第三项控制量平滑惩罚J_control w3 * Σ u_i(kj)²这一项惩罚加速度变化率避免 MPC 输出急剧抖动的加速度指令。如果你在实车测试中发现乘员抱怨顿挫第一件要检查的事就是 w3 是否太小。完整代价函数加一个终端项用来保证稳定性终端项是一个关于状态偏差的二次型矩阵通过求解离散代数黎卡提方程得到。这个细节在仿真中影响不大但如果你后续要做稳定性证明终端代价是绕不开的。4.3 约束条件怎么定才既有安全性又不至于太保守约束条件直接决定了控制器的行为边界也给队列系统提供硬性安全保证。我的配置如下约束项取值范围说明加速度上限[-3, 2] m/s²制动比加速更激进符合紧急减速需求jerk 上限[-1.5, 1.5] m/s³舒适性约束过大容易晕车最小车距≥ d0(2m)停车状态下的安全下限车速上限≤ 120 km/h道路限速约束可调这里有两点经验值得展开。第一个是加速度上下限不是对称的。车辆的加速能力通常弱于制动能力所以我把上限设为 2 m/s²下限设为 -3 m/s²——急减速工况比急加速更常见也更关键。如果你全当成 ±2.5 m/s² 对称设置紧急制动的表现会被压缩安全性反而下降。第二个是 jerk 上限非常容易被新手忽略。让我举个例子如果不约束 jerkMPC 在紧急情况下可能直接输出一个从 0 到 -3 m/s² 的阶跃加速度。虽然加速度大小不超标但乘员感受到的冲击力是巨大的。加了 jerk 约束之后减速过程会变得“有铺垫”舒适性提升非常明显。4.4 二次规划求解这一步的实时性是怎么保证的MPC 的数值求解实际上是一个标准的二次规划QP问题。每轮采样周期里系统要做这样的事把状态空间模型展开成预测矩阵把代价函数整理成 QP 标准形式调用求解器得到最优控制序列取出序列的第一个值作为当前控制量。在 50ms 采样周期约束下留给求解器的时间只有 30-40ms。初始方案我用的是通用内点法求解器测试发现单轮求解要 40ms 以上时不时超时。后来做了三件事才把求解时间压到 5ms 左右缩短控制时域。预测时域 Np 保持 20 步对应 1 秒预测控制时域 Nc 从 10 步降为 3 步。控制时域越短优化变量越少求解速度大幅提升。启用热启动。上一轮的最优解作为下一轮的初始值因为相邻采样周期的状态量变化很小热启动能让求解器在几百微秒内收敛。设置最大迭代次数。让求解器在迭代次数达到上限时返回当前最优可行解而不是继续计算。时间确定性比一点点的精度提升更重要。做完这三步单轮最坏情况求解时间从 40ms 级别降到 7ms 级别完全满足 50ms 周期要求甚至可以把采样周期再缩短到 30ms。这给后面的实测留出了余量。5. 五车队列的通信拓扑与控制时序分布式怎么落地5.1 通信拓扑选择前车跟随式结构分布式控制的物理基础是车与车之间的信息交互。这个项目里采用的是最经典的“前车跟随式”PFL拓扑每辆车只接收前车的信息只有头车直接获取参考轨迹比如道路限速、期望车速。为什么是 PFL 而不是全互联我一开始也想过让所有车互相通信觉得信息越全越好。但实际一算就发现全互联需要每辆车同时接收 4 辆车的广播数据通信带宽占用大而且数据处理逻辑复杂——每辆车都要维护一个其他状态的数据库。PFL 的通信量小了一个数量级逻辑也简单第 i 辆车只需要一个通道接收第 i-1 辆车的状态第 1 辆车负责接收全局参考。PFL 唯一的隐患是中间车辆出故障时后面所有车都会失去“信息源”。解决方法是加一层简单的状态估计器如果收不到前车数据就用自己的历史状态和上一周期接收到的数据外推前车轨迹撑过丢包窗口后恢复正常。5.2 通信延迟、丢包与时间戳对齐V2V 通信不是零延迟的这个项目模拟的通信延迟在 20ms 到 100ms 之间波动。如果你把收到的前车状态直接当成“当前状态”用后果是控制动作总是滞后于真实情况队列尾部会越来越不稳定。处理延迟的办法不是单纯地加大权重让它“忍着”而是要把延迟建模进预测模型。具体做法是收到前车数据时先检查时间戳确认这个数据是“多少毫秒以前”的然后在 MPC 的预测方程里把前车状态按运动学模型外推到当前时刻。外推公式很简单p_est p_received t_delay * v_received v_est v_received t_delay * a_received别小看这一步它直接改变了闭环的相位特性。我在仿真里做过对照实验不补偿延迟时队列在 70km/h 巡航工况下出现明显的尾部振荡补偿之后同样的工况下最大间距误差直接降了一半以上。丢包的处理逻辑相对简单连续丢包不超过 3 帧时直接把前车上一周期发送的轨迹保持原样代入 MPC超过 3 帧则降级为单车自适应巡航模式脱离队列控制等通信恢复后再重新切入。5.3 采样周期与调度时序的匹配整个系统的时序我最终这样安排模块周期说明通信广播20ms每辆车周期广播自身状态含时间戳上层 MPC 决策50ms通信与决策不同步以最新数据为准下层执行跟踪10ms高频跟踪期望加速度车辆状态采集10ms提供最新传感器反馈这里有一个容易被忽略的细节上层 MPC 的 50ms 采样周期和通信的 20ms 广播周期不同步可能导致 MPC 在一部分周期里拿到的数据不是“最新一帧”。解决方法是不要在 MPC 触发时主动去读通信缓存而是让通信模块在每次收到新数据时打上时间戳MPC 启动时取时间戳最新的那帧数据。这个“以数据驱动触发而非以周期驱动读取”的习惯我后来在多个项目里都沿用了非常实用。6. 仿真验证从 Simulink 到车辆动力学闭环6.1 仿真环境搭建思路这个项目的闭环仿真环境由三部分组成车辆动力学模型、上层 MPC 控制器、V2V 通信模拟。车辆动力学部分没有用简单的三阶运动学模型而是用专业的车辆纵向/横向动力学仿真软件模拟整车行为包括轮胎、传动、制动、空气阻力等非线性特性。这样上层 MPC 面对的已经是一个“不够听话”的执行环境仿真结果更贴近真实。上层 MPC 实现采用嵌入式 MATLAB Function 将求解器封装进去通过 S-Function 接入 Simulink。因为 QP 问题规模不大控制时域只有 3 步优化变量很少嵌入式实现完全跑得动。通信模拟则用一个单独模块负责数据广播和延迟抖动方便单独调节延迟幅值和丢包率来做鲁棒性测试。6.2 三组典型测试场景与判读指标场景一队列启动与稳定巡航。头车从静止加速到60km/h后面四辆车初始间距分布有偏差比如第2辆车间距比期望值多3米第4辆车少2米。观察目标是全队能否在10秒内收敛到相同速度并且所有车间距误差稳定在 ±1 米以内。场景二头车紧急制动。头车以 -3 m/s² 急减速从60km/h降到20km/h。核心指标是所有车都不能低于最小车距且尾部车辆最大减速度不能超过约束上限。场景三前车切入。在第3辆车前面突然插入一辆速度较低的车。此时第3辆车的前车数据源切换MPC需要在数百毫秒内重新规划动作。这是检验算法鲁棒性的关键场景比前两个场景更能暴露问题。6.3 参数整定的实战经验仿真阶段我有一张调参记录表最核心的一条经验是权重不要五个数字同时调一次只能动一个。最佳顺序是先把 w1间距误差权重调大到系统不震荡的边界再调 w2速度误差权重让队列速度一致性满意最后动 w3控制量平滑权重用最小调整消除抖振。样例如下当 w115、w210、w31 时队列间距误差收敛时间在 5 秒左右但加速度曲线有轻微高频抖动把 w3 升到 3 之后加速度曲线明显平滑代价是间距误差收敛时间延长到 6 秒。这个权衡没有标准答案完全取决于你更介意舒适性还是更介意响应速度。提示预测时域 Np 不是越大越好。我试过把 Np 从 20 增加到 40结果间距误差没有明显变小反而因为“看得太远”导致权重曲线上多出不必要的波动控制器动作反而变碎。对纵向队列控制来说1 秒左右的预测时域在大多数工况下都够用。7. 实测阶段三个最典型的坑振荡、超时与抖振7.1 波浪效应为什么会放大仿真里一个很常见的现象是头车在小幅加速时后面车辆的间距误差依次放大到第5辆车时振荡幅度已经不可接受。这就是前面提到的弦不稳定问题。通常原因有两个。第一个是时距参数 t_h 设置得太小。t_h 越小目标间距越短整个队列对微小扰动的响应越“紧张”误差容易沿队列放大。把 t_h 从 0.8 提到 1.0 秒会让系统更稳定代价是同等车速下车距变大队列长度增加。第二个是 w2速度误差权重相对 w1 太弱。间距误差权重高意味着系统优先追求“恢复期望间距”而速度一致性权重低意味着一辆车速度偏离前车时不会立刻被拉回来。方向性调整方法是先小幅增加 w2看看队列尾部振荡是否收敛如果无效再考虑加大时距。当我调参把 w115、w212、w32且 t_h0.9 时尾部最大间距误差从 2.4 米降到了 0.9 米效果立竿见影。7.2 求解超时与“解不出来”的应对实测环境里会遇到仿真里碰不到的麻烦运算资源有限、传感器数据噪声导致状态跳变进而导致 QP 问题在某些极端时刻求解不收敛或者超时。对策分为三个层次问题规模这是最根本的。我前面把控制时域 Nc 压到 3优化变量数只有 3 个问题规模对求解器非常友好。约束松弛给状态约束尤其是最小车距约束加一个松弛变量。正常工作时松弛变量为0遇到极端状态时允许它小幅偏离约束避免优化问题直接无解。当然松弛变量要加惩罚项让它尽量不要被激活。安全降级一旦检测到连续多个周期求解超时直接切换到底层紧急制动逻辑以最大减速度减速。安全优先级永远高于控制品质。7.3 加速度指令抖振舒适性的隐形杀手MPC 天然存在一个问题每次滚动优化得到的最优控制序列虽然平滑但每个采样周期只取第一步执行下一步重新计算后可能和上一周期的第一步差异较大导致输出期望加速度出现阶梯状跳变。我最终用了三层手段消灭了这个问题控制输入明确使用 jerk 而非加速度平滑性在模型层就得到保障在代价函数中把控制变化量 Δu 也加入惩罚项让相邻周期的控制量不允许突变输出端加一个中心频率合适的一阶低通滤波器滤掉高频跳变。注意低通滤波会带来相位延迟所以滤波时间常数不能过大。我最终取的是 100ms 时间常数对正常巡航没有任何负面影响但在紧急制动场景下会略微延迟响应所以我把滤波旁路设计成“当目标减速度超过 -2 m/s² 时直接旁路”保证紧急情况下响应速度不受影响。跑完整个项目回头来看分布式 MPC 加分层架构的组合真正的价值不在于算法本身有多前沿而在于它把“队列协同”这个大问题拆成了若干小问题通信模块管数据、上层控制器管决策、下层控制器管执行。每一层都能独立调试、独立验证、独立更换。这种模块化的工程思维比任何单一算法都更能决定一个系统的可靠性。如果你准备在类似项目里动手我个人的建议是不要先从五辆车开始先做两车编队把通信时序和代价函数权重跑通再逐步扩展到三车、五车。每一步增加的车都会暴露新的耦合问题但这些问题在规模小的时候定位成本低得多。另外仿真里的动力学模型越接近真实整车越好别用纯三阶运动学模型“自嗨”太久否则第一天上实车就会被各种非线性特性打脸。