4. 软件过流保护算法:数字滤波、有效值计算与自适应阈值
软件过流保护,说白了就是让伺服驱动器的“大脑”——DSP或MCU——自己判断电流是不是超标了。硬件保护像保险丝,动作快但粗暴;软件保护则更精细,能区分是真正的过流还是噪声干扰。我个人习惯把软件过流保护拆成三个环节:先把电流信号洗干净(数字滤波),再算出能反映真实发热或峰值的量(有效值/峰值检测),最后根据工况动态调整保护门限(自适应阈值)。
4.1 数字滤波算法:把噪声“滤”出去
电流采样信号里总有噪声——开关管动作、电机反电动势、传感器干扰。直接拿原始值做保护,你会被误报警烦死。我在一个高速主轴项目里就吃过这个亏,采样值跳变幅度超过20%,硬件保护没触发,软件却天天报过流。后来加了滤波,世界清净了。
4.1.1 移动平均滤波
移动平均的思路很简单:取最近N个采样点的平均值,作为当前输出。它像一个“滑动窗口”,窗口越大,信号越平滑,但响应也越慢。
// 移动平均滤波示例(C语言伪代码)
#define WINDOW_SIZE 16
uint16_t buffer[WINDOW_SIZE] = {0};
uint8_t index = 0;
uint32_t sum = 0;
uint16_t moving_average(uint16_t new_sample) {
sum -= buffer[index]; // 减去最旧的值
buffer[index] = new_sample; // 存入新值
sum += new_sample; // 加上新值
index = (index + 1) % WINDOW_SIZE;
return sum / WINDOW_SIZE; // 返回平均值
}
4.1.2 中值滤波
移动平均对高斯噪声效果好,但对脉冲噪声(比如偶尔一个尖峰)就力不从心了。中值滤波专治这种“野值”——把窗口内的数据排序,取中间那个值。脉冲噪声会被直接扔掉。
// 中值滤波示例(取3点中值,最常用)
uint16_t median_filter_3(uint16_t a, uint16_t b, uint16_t c) {
if (a > b) { uint16_t t = a; a = b; b = t; }
if (b > c) { uint16_t t = b; b = c; c = t; }
if (a > b) { uint16_t t = a; a = b; b = t; }
return b; // 中间值
}
4.1.3 两种滤波的对比
| 特性 | 移动平均滤波 | 中值滤波 |
|---|---|---|
| 噪声类型 | 高斯噪声、随机噪声 | 脉冲噪声、尖峰干扰 |
| 响应速度 | 较慢(窗口越大越慢) | 较快(窗口小) |
| 计算量 | 低(只需加减和移位) | 中等(需要排序) |
| 对波形影响 | 平滑波形,可能削峰 | 保留波形边缘,去除野值 |
| 推荐场景 | 稳态电流监测 | 瞬态电流检测 |
实际项目中,我经常把两者结合使用:先用中值滤波去掉脉冲噪声,再用移动平均做平滑。这叫“串级滤波”,效果比单用任何一种都好。
4.2 有效值计算与峰值检测
滤波之后,我们需要从干净的电流信号里提取出“特征量”,用来判断是否过流。最常用的两个量是有效值(RMS)和峰值。
4.2.1 有效值(RMS)计算
有效值反映的是电流的发热效应。电机绕组能不能扛住,看的就是有效值。计算公式很简单:先平方,再平均,最后开方。
// 有效值计算(滑动窗口法)
#define RMS_WINDOW 128
int32_t square_sum = 0;
int16_t rms_buffer[RMS_WINDOW] = {0};
uint8_t rms_index = 0;
uint16_t calc_rms(int16_t current_sample) {
// 减去最旧值的平方
square_sum -= (int32_t)rms_buffer[rms_index] * rms_buffer[rms_index];
// 存入新值
rms_buffer[rms_index] = current_sample;
// 加上新值的平方
square_sum += (int32_t)current_sample * current_sample;
rms_index = (rms_index + 1) % RMS_WINDOW;
// 返回均方根值(开方用查表法或近似算法)
return isqrt(square_sum / RMS_WINDOW);
}
4.2.2 峰值检测
有效值保护的是“热”,峰值保护的是“瞬时”。IGBT或MOSFET的短路耐受时间只有几微秒到几十微秒,必须用峰值检测快速响应。
// 峰值检测(带衰减)
int16_t peak_value = 0;
const float decay_factor = 0.99; // 衰减系数
int16_t detect_peak(int16_t current_sample) {
int16_t abs_val = abs(current_sample);
if (abs_val > peak_value) {
peak_value = abs_val; // 上升沿跟随
} else {
peak_value = (int16_t)(peak_value * decay_factor); // 缓慢衰减
}
return peak_value;
}
4.3 过流阈值自适应调整策略
固定阈值有个大问题:电机低速时电流大,高速时电流小;重载时电流大,轻载时电流小。如果阈值设死了,要么低速时频繁误报,要么高速时保护失效。自适应调整就是让阈值跟着工况走。
4.3.1 基于转速的阈值调整
电机转速越低,反电动势越小,同样的负载下电流越大。所以阈值应该随转速升高而降低。
// 基于转速的阈值调整
uint16_t base_threshold = 3000; // 基准阈值(单位:mA)
int16_t rated_speed = 3000; // 额定转速(单位:rpm)
uint16_t adaptive_threshold_speed(int16_t current_speed) {
// 转速越低,阈值越高;转速越高,阈值越低
float factor = 1.0 + (float)(rated_speed - current_speed) / rated_speed * 0.5;
if (factor < 0.8) factor = 0.8; // 下限
if (factor > 1.5) factor = 1.5; // 上限
return (uint16_t)(base_threshold * factor);
}
4.3.2 基于电流变化率的阈值调整
有时候电流突变不是故障,而是负载突变。比如机械手抓取工件时,电流会瞬间增大。这时候如果阈值不变,就会误报。我建议根据电流变化率(di/dt)动态抬高阈值,给系统一个“容忍窗口”。
// 基于电流变化率的阈值调整
int16_t last_current = 0;
uint16_t threshold_dynamic = 3000;
void update_threshold_by_didt(int16_t current) {
int16_t delta = current - last_current;
if (abs(delta) > 500) { // 电流变化超过500mA
// 临时抬高阈值,持续50ms后恢复
threshold_dynamic = 4500;
// 启动定时器,50ms后恢复
} else {
threshold_dynamic = 3000;
}
last_current = current;
}
4.4 知识体系总览
下面这张图把本章的核心逻辑串起来了。你可以看到,从原始采样到最终保护动作,中间经过了滤波、特征提取、阈值判断三个环节。每个环节都有多种选择,具体用哪种,取决于你的应用场景。