4、软件保护策略:逐周期限流、平均值保护、有效值保护
硬件保护再快,也扛不住工况变化。我个人的习惯是,硬件保护做最后一道防线,软件保护做日常主力。为什么?因为软件能“看趋势”,能“做预判”,还能“自适应”。
这一章我们聊三个最常用的软件保护策略:逐周期限流、平均值保护、有效值保护。它们各有各的脾气,用对了地方,电机怎么折腾都不怕。
4.1 逐周期限流(Cycle-by-Cycle Current Limiting)
说白了,就是每个PWM周期都盯着电流。一旦超过阈值,立刻关掉当前周期的驱动脉冲,等下一个周期再重新开通。有点像“打地鼠”——电流冒头就拍下去。
核心思想:不让电流在单个周期内持续增长,把峰值电流锁死在设定值以下。
我在做低压无刷驱动时遇到过一个问题:启动瞬间电流飙升,硬件比较器触发了,但关断后重启又冲上去,反复震荡。后来改用逐周期限流,每个周期都做一次判断,电流被“削”得整整齐齐。
4.1.1 实现方式
通常有两种做法:
- 比较器直接触发PWM关断:硬件比较器输出接PWM模块的故障输入引脚,响应快(几十ns),但灵活性差。
- ADC采样+软件判断:每个PWM周期中间点采样电流,软件比较后决定是否关断。响应慢一些(几μs),但可以加滤波、加滞回。
我个人更倾向第二种。虽然慢一点,但能做的事情多。比如可以记录过流次数,判断是瞬时干扰还是真过流。
小技巧:ADC采样点选在PWM导通中间段,避开开关噪声。我一般会在导通后的30%~70%区间采样,效果最好。
4.1.2 代码示例(伪代码)
// PWM中断服务函数,每个周期执行一次
void PWM_ISR(void) {
uint16_t adc_val = ADC_GetCurrent(); // 读取当前相电流
if (adc_val > CURRENT_LIMIT_THRESHOLD) {
PWM_DisableOutput(); // 关断当前周期输出
overcurrent_count++; // 记录过流次数
} else {
PWM_EnableOutput(); // 正常输出
overcurrent_count = 0; // 清零计数
}
// 如果连续过流次数超过N次,进入故障保护
if (overcurrent_count > 3) {
Motor_Stop(); // 停机保护
Error_Flag_Set(ERROR_OVERCURRENT);
}
}
4.1.3 优缺点
| 优点 | 缺点 |
|---|---|
| 响应快,一个周期内就能限流 | 对采样时序要求高,容易受噪声干扰 |
| 实现简单,资源占用少 | 只能限制峰值,不能反映真实发热 |
| 适合启动、堵转等瞬时大电流场景 | 频繁关断会导致转矩脉动 |
4.2 平均值保护(Average Current Protection)
逐周期限流管的是“瞬间”,平均值保护管的是“一段时间”。它计算若干个PWM周期内的电流平均值,如果平均值超过阈值,就认为电机处于过载状态。
你想想看,电机发热靠的是平均电流,不是峰值电流。峰值电流再大,只要占空比小,平均下来也不一定烧。反过来,峰值电流不大,但一直持续,电机照样过热。
注意:平均值保护不能替代逐周期限流。它们是互补关系——一个管瞬时,一个管持续。
4.2.1 实现方式
常用的计算方法有两种:
- 滑动窗口平均:维护一个长度为N的采样数组,每次新采样替换最旧的值,然后求平均。内存占用大,但实时性好。
- 一阶低通滤波:用公式
avg = avg * (1 - α) + sample * α,α是滤波系数。内存占用小,但响应有滞后。
我一般用第二种。嵌入式系统内存金贵,能省就省。α取0.01~0.05之间,具体看PWM频率和需要的响应时间。
4.2.2 代码示例
#define ALPHA 0.02f // 滤波系数
float current_avg = 0.0f;
void AverageProtection_Task(void) {
uint16_t adc_val = ADC_GetCurrent();
float current = (float)adc_val * ADC_TO_CURRENT;
// 一阶低通滤波
current_avg = current_avg * (1.0f - ALPHA) + current * ALPHA;
if (current_avg > AVERAGE_LIMIT) {
// 平均值超限,降低输出功率
Motor_ReducePower(50); // 降额50%
avg_overcurrent_timer++;
if (avg_overcurrent_timer > TIMEOUT_TICKS) {
Motor_Stop(); // 持续超限则停机
}
} else {
avg_overcurrent_timer = 0;
Motor_RestorePower(); // 恢复正常输出
}
}
4.2.3 应用场景
- 启动过程:启动电流大但时间短,平均值保护不会误触发。
- 堵转保护:堵转时电流持续偏高,平均值会快速上升。
- 过载保护:负载波动大时,平均值能平滑反映真实负载。
4.3 有效值保护(RMS Current Protection)
有效值保护,说白了就是算电流的“真发热量”。平均值只能反映直流分量,但电机电流是交流的,尤其是无感启动阶段,电流波形可能很不规则。这时候有效值比平均值更准确。
我记得有一次做水泵项目,启动时电流波形畸变严重,平均值保护一直没触发,但电机温度却蹭蹭往上涨。后来换成有效值保护,问题就解决了。
有效值公式:IRMS = √( (1/N) * Σ(ik²) )
说白了就是:先平方,再平均,再开方。
4.3.1 实现方式
有效值计算比平均值复杂,主要开销在平方和开方。嵌入式MCU做浮点开方很慢,所以一般用查表法或近似算法。
我常用的做法:
- 滑动窗口平方和:维护一个窗口,每次采样后累加平方值,减去最旧的平方值。开方用快速近似函数。
- 分段线性近似:把平方和映射到有效值,用查表+插值实现。
4.3.2 代码示例
#define WINDOW_SIZE 64 // 窗口长度
uint32_t square_sum = 0;
uint16_t buffer[WINDOW_SIZE];
uint8_t index = 0;
float FastSqrt(float x) {
// 快速开方近似,精度约1%
if (x < 1e-6f) return 0.0f;
float y = x;
for (int i = 0; i < 3; i++) {
y = (y + x / y) * 0.5f;
}
return y;
}
void RMSProtection_Task(void) {
uint16_t adc_val = ADC_GetCurrent();
float current = (float)adc_val * ADC_TO_CURRENT;
// 移除最旧值,加入新值
square_sum -= buffer[index] * buffer[index];
buffer[index] = (uint16_t)(current * 1000); // 放大后存整型
square_sum += buffer[index] * buffer[index];
index = (index + 1) % WINDOW_SIZE;
// 计算有效值
float rms = FastSqrt((float)square_sum / WINDOW_SIZE) / 1000.0f;
if (rms > RMS_LIMIT) {
Motor_ReducePower(30); // 降额30%
if (rms > RMS_LIMIT_CRITICAL) {
Motor_Stop(); // 严重超限直接停机
}
}
}
4.3.3 三种策略对比
| 策略 | 响应速度 | 计算开销 | 保护对象 | 适用场景 |
|---|---|---|---|---|
| 逐周期限流 | 最快(1个PWM周期) | 低 | 峰值电流 | 启动、堵转、短路 |
| 平均值保护 | 中等(几十个周期) | 低 | 平均发热 | 过载、持续大电流 |
| 有效值保护 | 较慢(取决于窗口长度) | 高 | 真实发热 | 波形畸变、不规则负载 |
4.4 三种策略的协同工作
实际项目中,我不会只用一种策略。三种策略各有侧重,组合起来才能覆盖所有工况。
我一般这样搭:
- 逐周期限流做第一道防线,管住峰值。
- 平均值保护做第二道防线,管住持续过载。
- 有效值保护做第三道防线,管住真实发热。
优先级上,逐周期限流最高,有效值保护最低。一旦逐周期限流频繁触发,平均值和有效值保护也会跟着响应,形成“三级联动”。
避坑指南:我曾经把三种保护的阈值设得太接近,结果启动时三个保护同时触发,电机直接停机。后来把逐周期限流阈值设高10%,平均值和有效值设低一些,让它们分层次动作,效果就好多了。
4.5 本章知识体系
下面这张图展示了三种软件保护策略的关系和协同逻辑:
嗯,这张图把三种策略的关系讲清楚了。从电流采样开始,分三路进入不同的保护逻辑,最终汇聚到保护动作决策。虚线表示策略之间的协同——比如逐周期限流频繁触发时,可以动态调整平均值保护的阈值。