前两年我们团队接到一个任务:给一款纯电车型做定速巡航控制器的自主开发。说实话,当时大家的第一反应都一样——定速巡航不是已经被供应商玩透的成熟功能了吗?直接找方案商集成一个不就完了,为什么还要自己造轮子?但等需求评审真正做下来,我们发现事情远没有这么简单。电动车特有的扭矩响应特性、整车各控制器之间的仲裁逻辑、功能安全上的证据链要求,都让"自主开发"从一道选答题变成了必答题。这篇文章我就把整个开发过程从头到尾捋一遍,从需求倒推、系统架构、算法设计、标定流程,到安全策略和我们在实测中踩过的坑,尽量把每一个关键决策背后的"为什么"也讲出来。准备做电控系统、嵌入式控制或者整车底层开发的同行,应该能从里面找到一些可直接参考的东西。
1. 为什么一个"成熟功能"也要自研:从需求倒推开始
1.1 市售方案为什么没进我们的候选清单
定速巡航在传统燃油车上的技术成熟度确实很高,但把它直接搬到电动车上,问题就出来了。我们当时列出市售方案的三个硬伤:
第一,黑盒交付。大部分方案商的巡航模块是封闭的,标定参数不开放,我们想针对自己的电池包、电机峰值特性、整车质量重新整定控制参数,几乎不可能。供应商给的都是"通用标定",在A车型上好使,换到我们的车型上未必好使,尤其电动车电机扭矩响应速度比发动机快一个数量级,控制周期要求完全不同。
第二,与整车扭矩仲裁深度耦合。巡航控制本质上是"替驾驶员踩油门",它向整车控制器(VCU)请求扭矩,但这个请求必须和驾驶员踏板请求、制动请求、能量回收请求、各种安全策略做仲裁。市售方案和VCU的接口往往比较"死",改一个优先级逻辑都要供应商配合排期,我们在敏捷迭代的节奏下根本等不起。
第三,功能安全证据链不足。巡航控制直接参与纵向运动控制,如果扭矩请求在故障状态下没有被正确撤出,后果很严重。我们希望整个开发过程留下完整的文档、测试记录、故障注入报告,这部分市售黑盒方案很难提供。
1.2 需求清单:把"能跑"细化成可测试的指标
既然决定自研,第一步就是把需求做扎实。我们参考了行业里对巡航功能的主流定义方式,结合纯电平台的特点,最终形成了一张可测试的需求清单:
| 需求类型 | 具体条目 | 目标值 |
|---|---|---|
| 功能需求 | 巡航激活车速范围 | 30~120 km/h |
| 功能需求 | 目标车速设定步长 | ±1 km/h 可调 |
| 性能需求 | 稳态车速控制精度 | 误差 ±1.5 km/h 以内 |
| 性能需求 | 动态工况恢复时间 | 切入/退出后 6 秒内收敛 |
| 功能需求 | 退出触发源 | 制动踏板、挡位变化、驻车、故障、驾驶员取消 |
| 安全需求 | 扭矩请求失效响应 | 单点故障下 100 ms 内撤出扭矩 |
| 安全需求 | 通信超时判定 | 连续 3 个周期无有效信号即故障 |
| 诊断需求 | 信号合理性监控 | 四轮轮速偏差超过 5% 即报警 |
这里有个容易被忽视的点:"巡航功能正常"和"巡航控制闭环设计合理"是两个层次的问题。前者是需求层面的,后者是控制层面的。很多团队拿到需求直接就开始写PID,跳过了对信号链路、仲裁规则的梳理,后面必然返工。我们的经验是,需求清单里的每一项都要能追溯到具体的软件模块或测试用例,否则它只是一句口号。
2. 控制器架构与信号链路:先定通道,再谈控制
2.1 三层架构:感知、决策、执行怎么分工
整个巡航控制器的逻辑架构,我们分成了三层:感知层、决策层、执行层。这个分层看似老生常谈,但它决定了后续所有模块的边界和接口,值得认真设计。
感知层负责收集所有与车速控制相关的信号:四路轮速脉冲、制动踏板开关、挡位信号、加速踏板位置、电机实际扭矩回读值、母线电压、纵向加速度等。决策层负责模式管理(巡航激活/等待/退出)、目标车速计算、闭环控制(PID加前馈)、质量辨识、以及扭矩请求的最终仲裁。执行层并不在我们控制器内部,而是通过CAN总线把扭矩请求发给VCU,由VCU作为整车扭矩管理的唯一出口去执行。
这里要特别说明一个原则:巡航控制器永远不要绕过VCU直接驱动电机。哪怕我们自己的控制器有扭矩请求能力,也必须把请求做成"建议值",经过VCU的安全仲裁后才真正生效。这样做的原因是,VCU掌握着整车的全局状态,制动优先、踏板优先这些安全策略最终要靠VCU兜底。我们自己只负责把控制算法做好,不做整车级别的仲裁。
2.2 轮速信号从脉冲到车速的完整链路
车速是巡航控制的反馈量,这个信号的质量直接决定控制品质。我们用的是轮速传感器输出的方波脉冲,每个车轮一圈发出固定数量的脉冲(我们车型是48齿每圈)。车速换算公式很简单:
v = f × π × D / N其中 f 是脉冲频率(Hz),D 是轮胎滚动直径(m),N 是每圈脉冲数。比如脉冲频率 500Hz,轮胎直径 0.7m,48齿每圈:
v = 500 × 3.14 × 0.7 / 48 ≈ 22.9 m/s ≈ 82.4 km/h但实际工程里麻烦的不在公式,而在信号调理。轮速脉冲在实车上会有毛刺、抖动、丢失,尤其在颠簸路面。我们做了三层处理:
第一层是硬件调理,RC低通滤波加施密特触发器整形,把传感器原始波形变成干净的方波送入单片机输入捕获引脚。第二层是软件去毛刺,连续两个边沿间隔小于某个阈值(比如 80 μs)时判定为干扰,丢弃。第三层是有效性检查,轮速突变率如果超过物理可能的上限,比如采样周期内车速变化超过 5 km/h,就认为信号异常,进入降级处理。
实车测试中我们发现,只用一个轮速信号做反馈是危险的。某个轮速传感器一旦瞬间丢信号,车速就会跳变,PID立刻产生错误的扭矩修正。所以我们最终采用前轮两个轮速取平均,同时把后轮轮速作为校验源,四轮一致性偏差超过设定阈值时置故障位。这个话题后面安全设计部分还会展开。
2.3 扭矩请求通道与VCU仲裁规则
巡航控制器和VCU之间的扭矩请求通过CAN报文交互,周期 10ms,报文里带滚动计数器和校验字段,方便对端检测通信中断。扭矩请求的基本单位是 Nm,正值代表驱动扭矩请求,负值代表再生制动扭矩请求(用于下坡限速时反拖)。
VCU的仲裁规则大体是这样的优先级:
| 优先级 | 请求来源 | 说明 |
|---|---|---|
| 最高 | 驾驶员制动请求 | 一旦制动踏板开关有效,直接屏蔽所有扭矩请求 |
| 高 | 整车安全策略 | 碰撞、热失控、整车故障等场景 |
| 中 | 加速踏板请求 | 驾驶员踩加速踏板时,以踏板为准 |
| 低 | 辅助驾驶/巡航控制 | 我们控制器的请求只在这一层生效 |
这个仲裁规则意味着,巡航控制器的扭矩请求在VCU那里随时可能被更高优先级的请求压制。我们在算法里必须考虑到这种"请求被拒"的情况:如果巡航请求持续大于实际执行扭矩,偏差就会积累在积分器里,等到高优先级请求消失,积分器会把扭矩猛地推出去,造成车速突增。这个问题我们后续用抗积分饱和策略和扭矩斜率限制解决,在第3节和第6节都有涉及。
3. 车速跟随算法:PID、前馈与质量辨识的组合拳
3.1 基础PID环路:参数的物理意义与整定方向
巡航控制的核心闭环是"目标车速-实际车速-扭矩请求"的负反馈。我们用的依然是经典PID,但每个环节都针对电动车特性做了处理。
控制周期取 10ms(100Hz),和扭矩请求CAN周期一致。PID离散化采用位置式:
u(k) = Kp × e(k) + Ki × Σe(i) × T + Kd × (e(k) - e(k-1)) / T其中 e(k) 是目标车速减实际车速,单位 km/h;u(k) 是扭矩请求,单位 Nm;T 是控制周期 0.01s。
三个参数的物理意义要对应到实际手感上:
- Kp(比例系数):相当于"车速偏差越大,扭矩修正越多"。它的上限受舒适性和系统稳定性约束,单位大约在 20~80 Nm/(km/h) 这个量级。调太大,车会一耸一耸;调太小,跟车速度慢,遇到坡道半天才反应。
- Ki(积分系数):消除稳态误差。平路上PID的P项能搞定大部分情况,但坡道上如果没有I项,车速会稳定在低于目标值的一个固定偏差上。Ki 单位大约在 5~15 Nm/(km/h)/s。
- Kd(微分系数):抑制车速超调和振荡。但轮速信号噪声会通过微分项被放大,所以我们实际用的D项很小,甚至在不同工况下会退化为PD+前馈控制。
我做了一个简单的仿真算例帮大家理解量级。假设车速偏差 3 km/h,Kp=50,那么P项输出 150 Nm,这相当于一个中型轿车在平路维持60km/h匀速所需扭矩的一大部分了。所以Kp超过80就要警惕系统振荡。
积分器的处理是另一个关键。我们用的是**抗积分饱和(Anti-windup)**策略:当积分器输出超过扭矩上下限(比如峰值 350Nm,再生限制 -200Nm),积分累加项停止继续增长。这个策略在巡航频繁加减速时特别重要,否则积分的"惯性"会让驾驶员感觉车在跟自己的意图较劲。
3.2 坡度前馈补偿:让积分器少干活
PID的积分器可以消除坡道造成的稳态误差,但响应速度太慢——积分是慢慢"攒"出来的,车上坡时车速会先掉下去一段,等积分器攒够了再慢慢拉回来,驾驶员能明显感觉到"顿一下"。
解决办法是加坡度前馈。我们可以用纵向加速度传感器(加速度计)的信号来估算坡度,或者用"电机实际扭矩+整车加速度反算坡度"。原理很简单:车辆在坡道上,重力沿坡道方向的分量是一个恒定干扰力:
F_g = m × g × sinθ对应到轮端扭矩:
T_ff = m × g × sinθ × r举个例子,车重 1800kg,坡度 5%(sinθ≈0.05),车轮滚动半径 0.3m:
T_ff = 1800 × 9.8 × 0.05 × 0.3 ≈ 264 Nm这个扭矩量级相当可观,相当于电机峰值扭矩的三分之一。如果前馈计算准确,控制器在进入坡道瞬间就直接补上这 264Nm,车速几乎不跌,积分器只需要处理残余误差。
实际工程中坡度信号不能直接用加速度计原始值,因为车身俯仰、颠簸都会让加速度计输出跳变。我们加了截止频率约 0.5Hz 的低通滤波,让坡度信号响应变慢,宁可前馈反应慢半拍,也不要因为噪声给扭矩带来高频抖动。另外,坡度信号还做了限幅,超过 ±15% 就截断,防止异常数据冲击控制环路。
3.3 整车质量辨识:空载和满载是两个世界
如果认为"车重是固定值",前馈补偿在空载和满载两种工况下会有很大偏差。一辆整备质量 1700kg 的电动车,满载可能到 2100kg,质量差了 20% 以上。同样一个 5% 坡道,前馈扭矩差了 40 多 Nm,积分器又要去填这个坑。
所以我们在算法里加了基于纵向动力学的整车质量在线辨识。纵向动力学模型简化成:
m × a = T/r - 0.5 × ρ × Cd × A × v² - μ × m × g × cosθ - m × g × sinθ这里的 m 是整车质量,a 是纵向加速度,T 是电机实际扭矩,r 是车轮滚动半径,ρ 是空气密度,Cd 是风阻系数,A 是迎风面积,μ 是滚动阻力系数,θ 是坡度角。把常量合并,整理成最小二乘法(RLS,递推最小二乘)的标准形式,就能在行驶过程中实时估计 m。
有几个实现细节值得注意:一是必须有加速度激励才更新,匀速巡航时动力学方程退化成"扭矩=阻力",质量不可辨识,这时候要冻结估计值;二是遗忘因子设在 0.95~0.98 之间,让辨识结果缓慢跟踪真实质量变化;三是需要防止数值发散,估计值限定在 1200kg~2500kg 区间。
这个模块看似增加工作量,但我们的体会是:它带来的收益是整个巡航控制在不同装载工况下的体验一致性。没有质量辨识,标定工程师会在空载和满载之间反复折腾一套参数,永远找不到折中点。
4. 实车标定流程:从"能稳住"到"能抗扰"
4.1 标定路线:先稳后准再抗扰
算法在仿真里跑得再好,最后都要在实车上过一遍。我们采用的标定路线分三个阶段,顺序很重要——跳过任何一步,后期都要返工。
第一阶段:平路空载,先求稳。在一条平直、无车、路面附着良好的测试路段上,从 60km/h 巡航开始调。先给一个很小的Kp,观察车速响应是否出现振荡,然后逐步增大Kp直到车速稳定、没有可见振荡、驾驶员没有突兀感为止。这时候Ki先不给,D给一个很小的值甚至为0。稳定是第一目标,精度是第二目标。
第二阶段:坡道加载,校准前馈。选一条固定坡度的测试坡道(我们用的是5%左右的连续坡),把第一阶段的PID带上,同时逐步加大坡度前馈系数。观察的指标是:进入坡道时车速下跌的幅度是否减小、积分器输出是否在一个较小的区间内摆动。如果前馈系数过大,车辆在坡顶会有一个明显的加速感,说明扭矩给多了。
第三阶段:工况抗扰,考验鲁棒性。颠簸路面、路面接缝、侧风、减速带、连续上下坡切换、高低电量(影响再生制动能力)……所有这些工况都要过一遍,观察稳态误差、超调量、扭矩请求波动幅度是否在可接受范围。
我们最终标定的参数组(示例值,不同车型需要重新标定):
| 参数 | 数值 | 说明 |
|---|---|---|
| 控制周期 | 10 ms | 与CAN报文周期一致 |
| Kp | 45 Nm/(km/h) | 中高速平路调校值 |
| Ki | 8 Nm/(km/h)/s | 保证坡道稳态精度 |
| Kd | 0.5 Nm/(km/h)/s | 仅抑制轻微超调 |
| 扭矩变化率上限 | 300 Nm/s | 舒适性约束 |
| 坡度低通截止频率 | 0.5 Hz | 抑制路面颠簸干扰 |
标定过程中强烈建议整车控制器预留在线标定通道(我们用的是XCP over CAN),否则每次改参数都要重新刷写固件,一个下午只能完成三次调整,效率太低了。
4.2 工况清单与数据分析方法
标定工作最容易被低估的部分是数据记录和分析。我们每次路测都完整记录:目标车速、实际车速、扭矩请求、实际扭矩、坡度信号、踏板状态、挡位、母线电压、刹车开关状态等信号,采样率 100Hz。
分析时重点看三个东西:
一是车速误差包络。把所有测试工况下的车速误差画在时间轴上,观察是否在任何时刻超出 ±2 km/h。只要有一次超标,就要追溯那一秒的扭矩请求和坡度数据,判断是参数问题还是工况极端。
二是扭矩请求的振荡次数。巡航控制器最怕的不是稳态误差,而是扭矩请求持续来回摆动,这会直接烧毁驱动系统的IGBT模块。我们用"扭矩请求方向反转次数"作为稳定性指标,每10秒内反向超过6次就要调低增益。
三是进入坡道/退出坡道的瞬态响应。这里最容易暴露"前馈过补偿"和"积分器饱和"问题。我们有个数据曲线特别说明问题:在连续上坡下坡路段,不加前馈时车速误差在 -4~+2 km/h 之间摆动,加前馈后收窄到 -1.5~+1 km/h。数据是不会骗人的。
5. 功能安全设计:巡航不能"赖着不走"
5.1 退出条件分级:紧急程度决定撤出方式
巡航控制的安全核心就一句话:该退出时要退出,而且要用对方式退出。我们把退出场景分成三级,每一级的撤出方式完全不同。
| 级别 | 触发条件 | 撤出方式 |
|---|---|---|
| 立即退出 | 制动踏板踩下、挡位切换、驻车、CAN通信超时 | 扭矩请求立即清零,控制器状态机回到待机 |
| 快速退出 | 信号合理性故障(轮速偏差过大、双路刹车开关不一致) | 300ms 内把扭矩请求降到 0,避免瞬态冲击 |
| 渐变退出 | 超出工作范围(车速超限、坡度超限、低电量) | 按 200~500 Nm/s 斜率平滑撤出,同时仪表提示 |
为什么要分级别?因为如果所有场景都直接清零扭矩,车辆会出现剧烈的"点头"现象——驾驶员踩刹车期望减速,结果巡航扭矩先撤了,制动系统还没跟上,反而制造了额外的减速度冲击。但反过来,如果所有场景都走渐变退出,紧急刹车时就来不及了。所以分级的本质是"安全性和平顺性之间的权衡"。
5.2 信号合理性检查:不信任何一个单点
巡航控制器的输入信号太多了,每个信号的可靠性都是系统安全的拼图。我们的原则是任何单一信号源都不能直接信任。
具体到实现上:
- 车速信号冗余校验:主反馈取左前轮+右前轮轮速平均值;同时与左后/右后轮速做一致性比较,任一后轮与平均值偏差超过5%,持续100ms以上,判定车速信号不可信。
- 双路刹车开关逻辑校验:刹车主开关通常是双路输出(一路常开、一路常闭),控制器采集两路电平后做异或校验。如果两路电平相同且持续超过100ms,说明其中一路失效,必须进入故障降级逻辑(这个话题第6节还会具体讲到)。
- 扭矩请求的闭环回读验证:我们每发一帧扭矩请求报文,同时监测VCU回发的"实际执行扭矩"信号。如果实际扭矩与请求扭矩偏差超过±20%且持续500ms以上,判断执行链路异常,立即退出巡航并上报故障码。
这个"实际扭矩回读"的监控是很多自研团队的盲区——他们只关心"我发了请求",不关心"执行器到底执行了没有"。在纯电平台上有时候电机会因为温度降额、母线电压低、控制系统故障而无法响应扭矩请求,如果没有回读监控,巡航控制器就会一直闭着眼睛请求一个根本执行不了的扭矩值,最后达到积分饱和,整体失控。
5.3 故障注入测试:把不可能发生的故障变成必然
功能安全设计得再完善,如果测试没覆盖,也是纸上谈兵。而我们团队在故障注入测试里踩的坑,比正常功能测试还多。
故障注入的手段有三种:一是用CAN工具直接篡改报文内容,模拟传感器信号异常;二是物理断开线束,模拟断路;三是通过可编程电源模拟供电跌落和干扰。测试的目标不是把系统搞坏,而是验证"在某种故障下,系统的反应是否符合设计预期"。
我们有一个很典型的案例:轮速信号丢失测试。注入手段是让右前轮速CAN报文在某个时刻停止更新。最初版本的软件里,超时检测窗口是 100ms(连续10个周期无更新才判定故障),结果在实车测试中发现,轮速丢失后系统在约 80ms 的时间里继续输出扭矩请求,车速出现了一次轻微突增。虽然幅度不大,但足够暴露问题——判定窗口太长。后来我们把超时窗口缩短为 30ms(连续3个周期),同时增加"更新跳变检测":只要轮速值跳变量超过物理可能,立即置故障位,不再等超时。这个案例也让我意识到,故障注入测试不能只测"系统最终会不会退出",更要测"在故障发生后到退出前这段时间,系统做了什么"。
6. 实测中的三个典型问题与根因处理
6.1 下坡再生制动受限引发的"窜车"现象
这个问题是我们在山路下坡路段测出来的。现象是:车辆在下坡维持巡航速度时,电机进入负扭矩(再生制动)状态,车速稳定;但电池电量接近满电时,整车会限制再生制动功率,电机实际负扭矩被逐步削减直至为0。这时候重力分量失去了平衡,车辆开始加速,驾驶员的感觉就是"车突然往前窜"。
根因在于我们最初的算法把"请求负扭矩"等同于"能获得负扭矩",没有把母线电压、SOC对再生能力的限制考虑进去。后来加了两个修正:
第一,从VCU读取再生制动能力状态字,当受限时,把前馈补偿中的负扭矩部分同步削减,避免盲目请求。第二,闭环控制里对积分器增加"受限状态保持"逻辑——当系统处于再生受限状态,积分器冻结更新,防止积分量持续积累,等再生能力恢复时一次性释放导致车速突增。
这个问题的核心启示是:控制器不能只关注自己的算法,还要理解执行器的物理限制和整车能量管理策略。
6.2 谐波路面上的车速小幅振荡
某一段时间我们收到试车员的反馈,说在某段特定的测试路面上,巡航时车速有"很轻微的抖动",人耳甚至能听到电机扭矩周期性变化的"嗡嗡"声。从记录的数据看,车速在目标值附近以约 2Hz 的频率小幅振荡,幅度约 ±0.8km/h。
排查过程很有意思。我们一开始怀疑是PID增益太大,把Kp和Ki同时降了20%,结果振荡幅度确实小了,但坡道表现变差。后来把车速误差信号做FFT频谱分析,发现振荡频率集中在 1.8~2.2Hz,而且只在特定路面出现。继续排查路面特征,发现该路段有规律性的伸缩缝,车辆轮胎通过时的纵向加速度扰动正好在这个频段。
问题明确了:路面激励频率落在控制环路的敏感频带内,PID控制器把这个周期性扰动当作车速偏差进行了响应。解决方案是在车速反馈信号上增加一个中心频率约2Hz的带阻滤波器(陷波器),把这个频段的扰动滤除。加好之后,特定路面上的振荡完全消失,其他路况没有明显影响。
这个经历让我对"反馈信号质量"的重视提升了一个等级——有时候不是控制器参数错了,而是反馈信号里带了不该有的周期性成分。
6.3 双路刹车信号的瞬态不一致
最后一个问题来自刹车开关本身。刹车开关双路信号的逻辑校验我们一开始设定得非常严格:只要两路电平不一致,立刻判断为故障并退出巡航。结果在某个批次的实车测试中,巡航频繁随机退出,频率高到无法正常完成测试。
排查过程是这样的:我们同时采集刹车开关一路和二路的波形,发现在驾驶员踩下刹车的瞬间,两路信号存在 20~50ms 的响应时间差——这是开关内部机械结构的固有差异,不是失效。我们的控制器如果在这个时间差内做"不一致就退出"判断,就会把正常的踩刹车动作误判为故障。
处理方式是调整不一致判定逻辑:从"立即判定"改为"持续不一致超过100ms才判定为故障"。这样既容忍了瞬态差异,又能真正覆盖持续失效的场景。为了验证100ms窗口不会掩盖真实的开关单边失效,我们做了专门的故障注入测试:断开一路信号并保持,确认系统在100ms后正确进入故障状态。
这个问题的价值在于它提醒我们:冗余信号的逻辑校验不能只看理论,必须对齐实际器件的时序特性,否则冗余设计会变成"过度敏感导致的可用性灾难"。
写在最后的一点体会
定速巡航控制器这个项目做下来,我最大的感触是:一个看似"谁都能做"的功能,从需求拆解、架构设计、算法调优再到安全验证,每个环节都有大量细节在等你踩坑。特别是电动车平台,电机响应的快速性、再生制动的约束、扭矩仲裁的复杂度,都会把传统燃油车上不明显的问题放大好几倍。如果你也正在做类似的自研控制器项目,我最后想分享几个原则:永远把反馈信号质量放在控制算法之前去解决;永远给退出策略做分级设计,别一刀切;永远用故障注入去测试你的安全逻辑,而不是等着故障自然发生。把这些基础打牢,后面所有看似"高级"的控制策略,都只是在一个可靠的底座上盖楼而已。