4、EPS关键算法:扭矩信号处理与滤波、电机电流环PID控制、车速感应式助力曲线设计、故障诊断与容错策略
各位同学,欢迎来到第四章。这一章,咱们要啃的可是EPS控制器的“灵魂”——关键算法。
说实话,很多刚入行的工程师觉得硬件设计最难,画板子、选芯片。但以我多年的经验来看,真正决定一台EPS手感好坏、安不安全、耐不耐用的,恰恰是软件算法。你硬件再牛,算法写得稀烂,那方向盘转起来就跟开拖拉机似的,又涩又抖。
这一章内容比较多,我把它拆成四个核心模块:扭矩信号处理、电流环PID、助力曲线,还有故障诊断。咱们一个一个来。
4.1 扭矩信号处理与滤波
EPS控制器的第一个输入信号,就是驾驶员施加在方向盘上的扭矩。这个信号来自扭矩传感器,通常是模拟电压或者数字SPI接口。
但问题来了——传感器信号天生就带着噪声。发动机的振动、路面颠簸、电磁干扰,都会叠加在扭矩信号上。如果你直接拿这个带毛刺的信号去算助力,那电机就会跟着一起抖,方向盘手感会非常糟糕。
所以,滤波是第一道关。
我个人习惯,在扭矩信号处理上采用两级滤波策略:
- 硬件滤波:在传感器输出端加一个RC低通滤波器,截止频率通常设在100Hz左右。这能干掉大部分高频噪声。
- 软件滤波:进入MCU的ADC之后,再做一次软件滤波。我常用的是滑动平均滤波和限幅滤波的组合。
这里给出一段我常用的滑动平均滤波代码,窗口大小我一般取8或16,兼顾响应速度和平滑度:
#define FILTER_WINDOW_SIZE 8
static uint16_t torque_buffer[FILTER_WINDOW_SIZE];
static uint8_t buffer_index = 0;
uint16_t torque_filter(uint16_t raw_adc_value)
{
uint32_t sum = 0;
uint8_t i;
// 存入最新值
torque_buffer[buffer_index] = raw_adc_value;
buffer_index = (buffer_index + 1) % FILTER_WINDOW_SIZE;
// 求和
for (i = 0; i < FILTER_WINDOW_SIZE; i++)
{
sum += torque_buffer[i];
}
// 返回平均值
return (uint16_t)(sum / FILTER_WINDOW_SIZE);
}
除了滑动平均,我还会加一个限幅滤波。说白了,就是判断相邻两次采样值的差值。如果差值超过一个阈值(比如50个ADC码值),我就认为这是干扰,直接丢弃,用上一次的值代替。
为什么会这样?因为驾驶员的手力变化再快,也不可能在1ms内突变50个码值。这招对付尖峰脉冲特别有效。
4.2 电机电流环PID控制
扭矩信号处理好了,接下来就要算助力了。但助力最终是通过电机电流来实现的。你给电机发一个目标电流值,电机就得老老实实输出这个电流。这就是电流环PID的活。
EPS电机通常是三相无刷直流电机(BLDC)或者永磁同步电机(PMSM)。电流环控制的核心,就是让实际电流快速、准确地跟踪目标电流。
我一般用PI控制器,不加微分项。为什么?因为电流环的响应速度要求很高,微分项容易引入高频噪声,反而让系统不稳定。
PI控制器的离散化公式很简单:
u(k) = Kp * e(k) + Ki * sum(e(k))
其中,e(k)是目标电流与实际电流的误差,u(k)是输出的PWM占空比。
这里我贴一段实际项目中的PI代码,带积分限幅和输出限幅:
typedef struct {
float Kp;
float Ki;
float integral;
float integral_max; // 积分限幅
float output_max; // 输出限幅
} PI_Controller;
float pi_update(PI_Controller *pi, float target, float actual)
{
float error = target - actual;
float output;
// 积分项,带限幅
pi->integral += pi->Ki * error;
if (pi->integral > pi->integral_max)
pi->integral = pi->integral_max;
else if (pi->integral < -pi->integral_max)
pi->integral = -pi->integral_max;
// 比例+积分
output = pi->Kp * error + pi->integral;
// 输出限幅
if (output > pi->output_max)
output = pi->output_max;
else if (output < -pi->output_max)
output = -pi->output_max;
return output;
}
电流环的采样频率,我建议至少10kHz,最好是20kHz。这样PWM频率(通常20kHz)和电流采样能同步,控制效果最好。
4.3 车速感应式助力曲线设计
助力曲线,说白了就是“给多少力”的问题。低速时,方向盘要轻,方便泊车;高速时,方向盘要重,保证稳定。这就是车速感应式助力。
我习惯用二维查表法来实现。横轴是方向盘扭矩,纵轴是车速,表格里存的是目标助力电流。
一个典型的助力曲线长这样:
| 车速 (km/h) | 扭矩 0 Nm | 扭矩 2 Nm | 扭矩 4 Nm | 扭矩 6 Nm | 扭矩 8 Nm |
|---|---|---|---|---|---|
| 0 | 0 A | 10 A | 20 A | 30 A | 35 A |
| 30 | 0 A | 8 A | 16 A | 24 A | 28 A |
| 60 | 0 A | 5 A | 10 A | 15 A | 18 A |
| 100 | 0 A | 2 A | 4 A | 6 A | 8 A |
你看,车速越高,同样扭矩下给的助力电流越小。这就是“高速稳重”的来源。
实际应用中,表格里的数据不是随便填的。你需要根据整车匹配标定。我记得有一次,我们给一款SUV做标定,低速助力给得太大了,结果方向盘轻得像玩具,完全没有路感。后来反复调整曲线斜率,才找到平衡点。
查表之后,还需要做线性插值。因为车速和扭矩不一定是表格里的整数点。插值算法很简单,我就不贴代码了,但要注意:插值计算要快,最好用定点数,别用浮点,否则CPU扛不住。
4.4 故障诊断与容错策略
最后,也是最重要的——故障诊断。EPS是安全件,一旦失效,驾驶员可能失去转向能力。所以,你必须提前想到“如果这个坏了怎么办”。
我一般把故障分为三级:
- 一级故障(致命):比如扭矩传感器完全失效、电机短路、MCU死机。此时必须立即切断助力,并点亮故障灯,提醒驾驶员。
- 二级故障(严重):比如电流传感器偏差过大、温度过高。此时可以降级运行,比如限制最大助力电流,或者只提供50%助力。
- 三级故障(轻微):比如CAN通信偶尔丢帧、ADC采样值轻微漂移。此时可以尝试恢复,或者记录故障码,下次上电再处理。
容错策略的核心思想是:宁可没助力,也不能给错误助力。你想想看,如果传感器坏了,电机却还在乱转,那后果不堪设想。
我举一个具体的例子——扭矩传感器冗余设计。很多EPS会用两个扭矩传感器(主传感器和副传感器)。正常工作时,取两个传感器的平均值。如果其中一个失效,就切换到另一个。如果两个都失效,那就立即切断助力。
诊断逻辑大概是这样:
if (abs(sensor1 - sensor2) > THRESHOLD)
{
// 两个传感器偏差过大,判断哪个失效
if (sensor1 超出合理范围)
{
use_sensor = 2; // 切换到传感器2
set_fault_code(FAULT_TORQUE_SENSOR1);
}
else if (sensor2 超出合理范围)
{
use_sensor = 1;
set_fault_code(FAULT_TORQUE_SENSOR2);
}
else
{
// 两个都正常但偏差大,可能是干扰,暂时用上一次值
use_sensor = LAST_VALID;
}
}
另外,看门狗是必须的。万一程序跑飞了,看门狗能复位MCU。但要注意,复位后不能立即恢复助力,要先做一次自检,确认所有传感器都正常,才能重新上电。
好了,这一章的内容就到这里。扭矩滤波、电流环PID、助力曲线、故障诊断,这四个模块构成了EPS算法的骨架。下一章,咱们会把这些模块整合起来,讲一讲整个软件架构怎么搭。