不要用超时保护开环步进电机
> 脉冲溢出才是唯一可靠的硬保护——一段真实事故分析
## 一个看起来合理的保护
```
PRESS_OPEN: 轴2回 IN1 限位
1. 启动脉冲(连续模式,target_pulses=0)
2. 等待 IN1 触发
3. 10 秒超时 → 报警
```
这看起来天经地义。轴跑 10 秒还没碰到传感器?肯定出问题了,报警。
但这是错的。
## 开环步进的两个自我保护逻辑
开环步进轴(无限位开关、无原点传感器)只有两种停止条件:
1. **有限脉冲完成** — ISR 正常计步,到达目标脉冲数后自动停
2. **脉冲溢出** — 实际脉冲超出目标 + 保护余量,触发硬停止 + 报警
超时保护不在这张表里。原因很微妙:**超时信号和脉冲是同一个 ISR 产生的**。
如果 ISR 正常运转 → 脉冲计数正常 → 有限脉冲会先于超时结束
如果 ISR 挂了 → 脉冲不走了 → 超时也走不准
所以超时对于纯开环轴,本质上是靠不住的。
## 真实事故现场
在我们的四轴步进系统中,轴 1 负责出袋(BAG_OUT ≈ 50611 脉冲),轴 3/4 负责压口闭合(PRESS_CLOSE ≈ 3333 脉冲)。
轴 2 PRESS_RETRACT 的脉冲数计算有误差,实际需要的脉冲比配置值多。ISR 正常计步,但永远到不了 done。系统没有脉冲溢出保护,靠一个 10 秒超时兜底。
结果:超时触发→报警→停机。但是 ISR 明明没问题,脉冲一直在走。超时成了唯一的故障检测手段,而它触发的时候机械已经跑到不可预测的位置了。
## 正确的保护策略
### 开环轴(无传感器反馈)
```
唯一硬保护:脉冲溢出
目标脉冲 + 10000 脉冲(APP_CFG_STEPPER_PULSE_OVERFLOW_MARGIN)
超限 → 立即停轴 → APP_ALARM_STEPPER_PULSE_OVERFLOW
超时在此场景无意义,不加。
```
### 有传感器反馈的轴
```
第一道保护:传感器触发(IN1/IN2 限位)
第二道保护:脉冲溢出(传感器失灵时兜底)
辅助保护:超时(可保留,但仅作辅助)
```
### 轴 2 回 IN1 的特殊口径
```
轴 2 回位 → 连续模式(target_pulses=0)
→ IN1 触发即为成功
→ 脉冲溢出保护 = 20000 脉冲(IN1 失灵时防卡死)
→ 超时 10s 保留,仅作为 IN1 + 脉冲溢出双双失灵的最后兜底
```
## 反模式:隐式补丁
最危险的写法是在"快到了"时动手脚:
```c
// ❌ 反模式:剩余 2000 脉冲提前斩杀
if (remaining_pulses <= 2000) {
bsp_stepper_stop();
}
```
这个 2000 是哪里来的?为什么不是 1500 或 2500?下一个人看这段代码,不敢删,不敢改,只能往上叠加新的 magic number。
正确做法:用显式的 `finite_stop_mode_t` 字段建模。
```c
typedef enum {
FINITE_STOP_TARGET, // 到目标脉冲停
FINITE_STOP_OVERFLOW, // 脉冲溢出停
FINITE_STOP_CONTINUOUS // 连续模式,外部触发停
} stop_mode_t;
```
每个阶段的停止策略清晰声明,不依赖隐式阈值。
## 总结
给开环步进轴加超时保护,就像给断了刹车线的车装喇叭——发声没问题,但停不下来。
真正可靠的保护链:**脉冲计步 ISR 正常 → 有限脉冲 done;ISR 异常 → 脉冲溢出报警**。没有第三条路。
Comments NOTHING