前车一脚刹车,后车要过零点几秒才有反应,如果中间还隔着一辆车,这个延迟还会继续叠加——这就是传统自适应巡航(ACC)在小间距队列里的尴尬。做这个基于滑模控制(SMC)的三车协同自适应巡航项目之前,我一直觉得只要把自己的跟车逻辑调好就行,直到在仿真里看到第三辆车的误差越放越大,才意识到单车视角远远不够。这篇内容我会把整个探索过程整理出来,从模型建立、控制率设计、三车通信拓扑,到仿真实测和抖振处理,基本覆盖我踩过的所有关键点,希望能帮到正在折腾CACC或者纵向队列控制的同学。
顺带说个术语:标题里写的是“滑膜控制SMC”,控制圈通用写法其实是滑模控制(Sliding Mode Control),“滑膜”更多是以讹传讹的写法。我后文统一叫滑模,读起来和查文献都对得上。
1. 为什么是三辆车:小队列里的大学问
1.1 从ACC到CACC:单车视角到车队视角
传统ACC的原理很简单:本车用雷达或摄像头测前方车辆的距离、相对速度,然后调整油门刹车,让车距保持在一个安全范围。但它的局限也很明显——你只能看到前车,看不到前前车。
想象一个三车队列,领航车因为前方路况突然减速。第二辆车还能通过传感器直接测到车距变化,虽然有一点滞后,但反应不算太慢。第三辆车呢?它看着的是第二辆车的车尾,第二辆车开始减速的那一刻,第三辆车实际上是感知不到的,只有等两车之间距离开始缩短,它的传感器才有反应。这一级一级的传递延迟,在紧急制动场景下会被放大得非常难看。
协同自适应巡航(CACC)就是冲着这个问题去的。它引入车车通信(V2V),把前车甚至领航车的速度、加速度、位置信息直接广播给后车。后车不用等车距变化,就能提前知道前面发生了什么。这是CACC和ACC最本质的区别:ACC靠“探测”,CACC靠“知道”。
1.2 三辆车的队列恰好构成一个最小完整链条
有人可能觉得,三辆车太少了,为什么不做五辆十辆?我的看法是,三辆车是能验证问题的最小完整系统。
队列控制有一个核心指标叫“队列稳定性”。它说的是:间距误差沿着车队向后传播时,不能越传越大。如果第二辆车的误差是0.5米,第三辆车的误差应该收敛到更小,而不是变成0.8米。这个性质是保证大车队长距离运行不撞车的前提。
第二辆车只需要跟随一个前车,但第三辆车同时面对两个问题:一方面是误差从第二辆车传递过来,另一方面是它自己也要做出正确的跟随判断。这三辆车恰好形成一个完整的误差传播链。
还有一个很实际的原因:实验成本。三辆车不需要大规模路测场地,仿真里加两个跟随车就能验证控制逻辑。等三辆车的误差不发散、鲁棒性足够好,再往五辆、十辆扩展就只是参数调整和通信压力的问题了。
2. 滑模控制入局:一个对模型不挑剔的控制器
2.1 车辆纵向模型的不确定性来自哪里
很多做控制的人上来就喜欢用PID,我也一样。但PID在单车ACC上表现可以,放到三车队列就会出现一个尴尬的问题:车辆模型本身是不准的。
一辆车的纵向动力学受什么影响?载重、风阻、道路坡度、轮胎打滑、刹车片温度、执行器响应延迟……这些参数每个时刻都在变。车上坐两个人还是坐五个人,质量差了几百公斤;高速上顺风逆风,气动阻力差10%都有可能。PID在标称工况下调好,遇到满载、下坡、侧风一组合,性能就明显下降。
滑模控制不太care这件事。它不需要精确的模型,只需要一个名义模型,再知道扰动的大致上界。它的设计思路是:不用精确模型去抵消干扰,而是用足够大的“开关式”控制量把干扰硬生生压下去。这让我在探索阶段不用花大量时间做参数辨识,先把协同逻辑跑通再说。
2.2 滑模面的构造与“吸”住误差的直觉
滑模控制的核心思想其实很形象。先定义一个误差状态的函数,叫滑模面,然后设计控制律,让系统状态在有限时间内被“吸”到这个面上,之后再沿着这个面滑向平衡点。
打个比方。给你一个高尔夫球放在斜坡上,你想让它到达某个高度。最省力的办法不是在斜坡上铺一条完美轨道,而是直接挖一条沟槽横穿球所在的位置。球被推进沟槽以后,不管它从哪个方向滚进来,只要沟槽存在,它就会沿着沟槽一直滚到终点。滑模面就是那条沟槽。
对跟车来说,误差状态主要是两个:车距误差 e 和相对速度 vr。滑模面取为:
s = vr + λ·e如果 s=0,意味着 vr = -λ·e。这是一个一阶收敛关系,误差会按指数衰减,衰减速度由 λ 决定。λ=1 的时候,误差大约每秒衰减到原来的36.8%,这个收敛速度对跟车场景来说是够用的。
控制律也分成两块:等效控制负责把系统保持在滑模面上,切换控制负责把偏离的状态推回滑模面。切换控制器的增益只要比扰动上界大,建模误差和外部干扰就都会被吸收。这就是“对模型不挑剔”的真正原因。
2.3 与PID、MPC的横向对比:为什么先试滑模
做技术选型的时候,我把三种主流方案放在一起比过,结论很清楚。
| 方面 | PID | 滑模控制SMC | MPC |
|---|---|---|---|
| 模型依赖 | 很低,但参数需要反复调试 | 低,需要名义模型和扰动上界 | 高,需要较准确的预测模型 |
| 鲁棒性 | 一般,工况变化大时易失稳 | 强,对匹配不确定性不敏感 | 对模型失配敏感,可加鲁棒设计 |
| 计算量 | 极低 | 低 | 高,每步在线优化 |
| 约束处理 | 难以处理 | 不好直接加约束 | 天然支持加速度、间距等约束 |
| 主要痛点 | 参数难在线自适应 | 抖振需要处理 | 计算资源和模型精度 |
在这个项目里,我最想验证的是“三车协同+通信”这个架构本身。如果一上来就上MPC,模型不准导致预测漂移,出了问题很难说是控制算法的问题还是通信拓扑的问题。滑模实现简单、鲁棒性高,把这个阶段最核心的“协同机制”能不能成立验证清楚。至于MPC,那是模型足够完善之后的事。
注意:滑模的鲁棒性是有条件的,主要针对匹配不确定性。对纵向控制来说,风阻、坡度、执行器延迟都能近似归到这一类,但如果结构不确定性占主导,滑模也会失效。
3. 建模与间距策略:把三辆车写成能仿真的数学表达
3.1 纵向动力学与执行器滞后的处理
仿真第一步是把车写成数学表达。如果写得太复杂,控制律设计会被淹没在细节里;写得太简单,仿真结果又不具备参考意义。我用的是一种很常见的方式:点质量模型加一阶惯性环节。
点质量模型理解为:车是一个有质量、有速度和加速度的点,所有外力最后都归结为加速度作用在这个点上。一阶惯性环节则是描述执行器滞后的关键:
τ · ā' + a = a_des其中 a 是实际加速度,a_des 是期望加速度,τ 是驱动/制动系统的时间常数。这句话的意思是:你发出“加速2米每平方秒”的指令,车辆实际加速度不会立刻到2,而是要经过一个指数响应过程,大概经过 τ 时间后接近目标值。乘用车油门刹车系统的 τ 通常在0.2秒到0.5秒之间。
这个环节太重要了。很多刚做CACC的人忽略它,结果仿真里控制律输出一个加速度,车辆立即就达到这个加速度,反馈回路被人为地“变简单”了。真实情况下,执行器滞后会让切换控制的抖振进一步恶化,所以必须在一开始就放在模型里。
3.2 固定车距还是固定时距
间距策略决定“期望车距是多少”。典型的两种策略:固定车距和固定时距。
固定车距好理解,不管车速多少,两车始终保持比如5米的净距。好处是车队紧凑、能提高道路利用率,坏处是低速时对控制和通信的精度要求极高,误差稍微大一点就可能撞上。固定时距(CTH)则是让期望间距随着车速线性变化:
d_des = h · v_i + d_minh 是车头时距,单位是秒;v_i 是本车速度;d_min 是最小安全间距。车速30米每秒时,我们期望间距大一些;车速5米每秒时,我们期望间距小一些。这符合人类驾驶直觉。
三车探索我用的是固定时距,h取1.2秒,d_min取5米。为什么不用固定车距?因为在这个阶段我想先把控制逻辑验证清楚,不希望间距策略本身给系统增加额外的稳定性压力。做完稳定性的验证之后,再尝试固定间距策略才有意义。
3.3 通信拓扑:谁的信息给到谁
三车编队里信息流向怎么设计,直接影响控制效果的优劣。常见有前车跟随式(PF)和领航者广播式(LD)两种。
PF拓扑下,每辆车只接收直接前车的信息。好处是通信链路短、数据量小;坏处是误差沿队列传递时,每一级都有一点延迟,级联起来之后放大效应明显。LD拓扑下,每辆跟随车同时接收领航车的广播和直接前车的信息。第二辆车看到的是“领航车刹车了”和“前车正在刹车”两层信息,反应自然更快。
| 对比项 | 前车跟随式(PF) | 领航者广播式(LD) |
|---|---|---|
| 通信数据量 | 小 | 略大,多一路广播 |
| 对通信时延的敏感度 | 高,逐级累积 | 低,前车延迟可前馈补偿 |
| 误差传递特性 | 逐级放大风险高 | 更利于队列稳定性 |
| 实现复杂度 | 简单 | 多一路数据解析,但可控 |
这次项目我用的LD拓扑,但控制率里只把直接前车的速度、加速度放进滑模面,领航车的广播信息主要用于监控和可视化。后来才发现,如果这条信息也进控制率做前馈,误差还能再压一截。这部分我放到最后再细说。
4. 协同滑模控制器设计到仿真通关
4.1 控制律推导:等效控制加切换控制
定义第 i 辆跟随车的车距误差:
e_i = Δx_i - (h·v_i + d_min)其中 Δx_i = x_{i-1} - x_i - L_i,是车间净距。误差大于0表示实际间距过大,控制应该让车稍微加速追近一点;小于0表示间距过小,控制该减速。
误差变化率为:
ė_i = vr_i - h·a_ivr_i = v_{i-1} - v_i 是相对速度。滑模面取:
s_i = vr_i + λ_i·e_i接下来把滑模面求导。注意 vr_i 的导数就是前后车的加速度差 a_{i-1} - a_i,代入车辆纵向模型后,可以让状态维持在滑模面上:
ṡ_i = λ_i·(vr_i - h·a_i) + a_{i-1} - a_i我希望滑模面的收敛满足趋近律:
ṡ_i = -k_i·s_i - ε_i·sat(s_i/Δ)两个式子联立,解出目标加速度:
u_i = [λ_i·vr_i + a_{i-1} + k_i·s_i + ε_i·sat(s_i/Δ)] / (1 + λ_i·h)这就是控制器的核心表达式。前车加速度 a_{i-1} 是通过通信拿到的,不是估出来的。这个细节很关键,也是CACC区别于ACC的地方。
4.2 仿真环境搭建与关键参数
仿真我用Python搭的,离散时间步长 dt=0.01秒,相当于100Hz控制频率,比较贴近真实车载控制器的节奏。车辆参数按照普通乘用车设定:
| 参数 | 数值 | 说明 |
|---|---|---|
| 质量 m | 1500 kg | 常规乘用车 |
| 执行器时间常数 τ | 0.3 s | 油门/制动响应 |
| 车头时距 h | 1.2 s | 固定时距策略 |
| 最小安全间距 d_min | 5 m | 静止时保持的间距 |
| 车长 L | 4.5 m | 仿真中用作净距计算 |
控制器参数我一开始只凭经验拍脑袋,用过一组值:
| 参数 | 数值 | 作用 |
|---|---|---|
| λ | 0.8 | 滑模面收敛速率 |
| k | 1.5 | 指数趋近项,帮助状态快速回到滑模面 |
| ε | 0.6 | 切换增益,克服扰动上界 |
| Δ | 0.05 | 边界层厚度,抑制抖振 |
4.3 核心控制器代码片段
控制器主逻辑不长,核心就是上面那个表达式:
def sat(x, delta): # 饱和函数,代替符号函数以抑制抖振 if x > delta: return 1.0 elif x < -delta: return -1.0 else: return x / delta def cacc_controller(ego, prev, params): # ego: 本车状态 [x, v, a] # prev: 前车状态 [x, v, a] dx = prev.x - ego.x - ego.length # 车间净距 d_des = params.h * ego.v + params.d_min e = dx - d_des # 车距误差 vr = prev.v - ego.v # 相对速度 s = vr + params.lambda_ * e # 滑模面 # 控制律 u = (params.lambda_ * vr + prev.a + params.k * s + params.eps * sat(s / params.delta)) / (1 + params.lambda_ * params.h) return u仿真主循环里,每一辆跟随车先从通信模块拿前车状态,调用这个函数得到期望加速度 u,然后把 u 送进一阶惯性环节更新实际加速度,再积分得到速度和位置。整个三车队列以领航车给定的速度轨迹为输入,循环推进。
仿真前记得先把三辆车的初始车间距设成期望值。很多第一次跑的人上来就把三辆车整齐排列成等间距,结果初始误差不为零,控制器开局就输出一个很大的加速度,吓一跳还以为算法写错了。
4.4 三种典型工况下的结果分析
我跑了几组典型工况。第一组是匀速巡航:领航车以20米每秒匀速行驶,三车初始误差设为零,观察整个队列能不能保持稳定。结果间距误差始终收敛在0.1米以内,没有发散迹象。这一组主要验证控制器的基础跟踪能力。
第二组是正弦变速:领航车速度在20米每秒基础上做±3米每秒的正弦波动,周期10秒。这模拟的是城市快速路上前车根据自己的驾驶节奏轻踩油门刹车的情况。曲线上看,跟随车的间距误差出现周期性波动,但峰值在0.25米左右,而且第二辆跟随车的误差峰值比第一辆跟随车小。这说明误差没有沿队列放大,队列稳定性是达标的。
第三组是紧急制动:领航车从20米每秒以-6米每秒平方的减速度刹停。这一组最有说服力。没有通信前馈的情况下,第三辆车的最大间距误差接近0.9米;把通信数据用起来之后,压到0.6米以内。虽然是仿真,但这个量级已经能说明问题:CACC确实比单纯ACC在紧急工况下有明显优势。
5. 抖振、时延与调参:跑仿真之外的实战问题
5.1 抖振的产生机理与边界层处理
滑模控制最被人吐槽的问题就是抖振。原理上讲,切换控制用的是符号函数 sign(s),它在 s=0 处是一个跳变。理想情况下,控制量以无穷大频率在正负之间切换,系统“平均上看”是稳定的。但实际中不可能有这种频率,仿真有步长,执行器有带宽,通信有延迟,结果就是控制量在滑模面附近来回震荡,车辆加速度曲线会肉眼可见地毛糙。
处理办法最常见的是把符号函数换成饱和函数:
sat(s/Δ) = s/Δ (当 |s| < Δ) sat(s/Δ) = sign(s) (当 |s| ≥ Δ)Δ是边界层厚度。在边界层内部,控制量是连续变化的,不再跳变,这叫准滑模控制。Δ越大,控制越平滑,但代价是稳态误差变大;Δ太小,抖振依然存在。我这里取Δ=0.05,是在油耗和跟踪精度之间折中的结果。
很多人忽略了这个环节,觉得仿真里抖振无所谓。等将来把控制器放到实车或者硬件在环平台才会发现,抖振会让执行器快速磨损,也会激起车辆结构的振荡频率。所以即便在纯仿真阶段,也应该把抖振当成一个真实问题来对待。
5.2 我的调参顺序和一些参数参考值
调参顺序很重要,别上来就乱调。我的经验是:先定λ,再动k,最后再摸ε。
λ决定滑模面的收敛斜率,是整个控制器最主导的参数。λ太小,误差收敛慢,跟车显得迟钝;λ太大,误差是压下去了,但激发高频的迹象很明显。对于乘用车跟车场景,λ在0.6到1.2之间比较好,我从0.8开始。
然后是k,指数趋近项。k的作用是让状态更快回到滑模面。k过小,状态偏离滑模面以后回来得慢,鲁棒性打折;k过大,控制量的峰值会很高,起步和制动时会感觉突兀。我的参考值是1.5左右,具体可以根据你的执行器限幅调整。
最后是ε。切换增益只要比扰动上界大就行,不需要一味增大。ε过大,抖振加重,边界层也救不回来。怎么判断扰动上界?可以看模型里风阻、坡度带来的加速度扰动范围估算,比如给0.5到1.0就好。我最后落在0.6。
调参的时候还有一个容易忽略的坑:初始误差。如果仿真开始时三车间距离期望值差得远,切换项会瞬间输出很大的加速度。建议实现一个“启动平滑”,让加速度指令在最初1到2秒内渐进释放,而不是一上来就全力执行。
5.3 通信时延和执行器滞后带来的连锁反应
CACC离不开通信,但通信有延迟是现实。V2V通信延迟一般在几十到两百毫秒之间。我在仿真里给控制器加入固定时延后,问题就来了:延迟到100毫秒时,未做补偿的情况下,紧急制动工况的最大间距误差从0.6米直接涨到0.9米附近,正弦工况的误差峰值也放大约三分之一。
处理这个问题的思路有几个方向。一个是用领航车广播的“加速度指令”而非“估计值”,指令本身含有的延迟信息可以被建模进控制器;另一个是把执行器时间常数τ和通信延迟合并考虑,做一个等效一阶延迟的相位补偿。
我在这个阶段用了最简单的预测补偿:在控制步里,不直接用收到的前车加速度,而是用一个一步前向的状态预测器,根据前车的速度和加速度外推几十毫秒后的状态,再喂给控制器。补偿之后,100毫秒延迟的紧急制动误差峰值从0.9米降到了0.45米左右。虽然没有完全消除影响,但方向是对的。更复杂的史密斯预估器和状态观测器,可以作为下一步的改进项。
6. 试完之后的真心话与扩展思路
经过这几轮折腾,我对滑模控制在三车协同巡航里的定位有了比较清晰的认识。它最大的优势是鲁棒性和实现简单,不需要精确模型也能把三车的间距误差压在一个让人满意的范围里。如果你的目标是快速验证一个队列控制架构是否成立,滑模是性价比很高的选择。
但要说未来往产品级走,我认为MPC会更合适。CACC本质上是一个带约束的跟踪问题——加速度有上下限,车距不能低于安全值,乘员还要舒适。MPC能把这些约束写进优化问题,滑模虽然能通过限幅硬性截断控制量,但那是“事后限制”,不是“事前规划”。
另外还有一个我留到最后的改进点:把领航车广播的加速度也纳入控制率做前馈。我目前只把它用于监控,但仿真数据显示,做完这个前馈之后,第三辆车对前前车减速的反应速度还能再快一点。这是最值得继续往下做的方向,也算给看到这里的同学留一个可以自己动手验证的小课题。
如果你打算复现这个项目,我的建议是别急着堆功能。先把固定时距加滑模面这一套跑通,再看曲线,理解每个参数在误差曲线上留下的痕迹。然后逐步加通信时延、换工况、改拓扑,每一步只做一处改动。控制这东西,只有亲手调过的参数才会真正长在脑子里。