两轮BMS 防打火策略详解

BMS 防打火策略详解

本文基于量产 60V BMS(STM32F0 + SH367306 AFE),拆解"防打火"的原理与软件实现。


一、为什么会打火?

电动两轮车的电机控制器输入端有大容量电解电容(通常几千 µF)。BMS 输出端与控制器之间通过接插件连接。

复制代码
BMS 电池 ────[接插件]──── 电机控制器
                             │
                          ┌──┴──┐
                          │ 电容  │  ← 几千 µF,初始电压 0V
                          └─────┘

当接插件插入瞬间,电池电压(60V+)直接加到 0V 的电容上。电容充电电流 I = C × dV/dt,在 µs 级时间内电流可达几百安------接插件触点处产生电弧(打火),长期会烧蚀触点、熔毁接插件、甚至击穿 MOS。


二、怎么防?------预充电路

在放电主回路之外加一条预充支路:一个小功率 MOS 串一个 22Ω 限流电阻。

复制代码
电池+ ──┬── 放电 MOS ──────────┬── 负载+
        │                      │
        └── 预充 MOS ── 22Ω ──┘

流程:

复制代码
1. 预充 MOS 导通,放电 MOS 断开 → 电流经 22Ω 电阻给负载电容充电
   最大电流 = 60V / 22Ω ≈ 2.7A(安全)

2. 等待电容电压充到接近电池电压(数百 ms)

3. 放电 MOS 导通,预充 MOS 断开 → 正常放电,无打火

三、软件实现:Idle 状态机

PMM 模块在空闲模式下有一个四级状态机(PMM.c:35-39):

复制代码
kIdleInit ──→ kIdleFirePrevent ──→ kIdlePowerOn ──→ 放电模式

3.1 kIdleInit ------ 初始化防打火

  • 防打火使能:闭合充电 MOS + 预充 MOS,断开放电 MOS
  • 检测到放电电流 > 30mA(用户拧电门)→ 进入 kIdleFirePrevent
  • 防打火未使能:直接闭合放电 MOS,跳过预充

3.2 kIdleFirePrevent ------ 执行预充时序

PMM.c:156-175

c 复制代码
// 阶段1:预充 MOS 断开 200ms
s_TimeDelay < 200ms → PreMos_Ctrl = 0

// 阶段2:预充 MOS 闭合 500ms(电流经 22Ω 给电容充电)
200ms ≤ s_TimeDelay < 900ms → PreMos_Ctrl = 1

// 阶段3:预充完成,标记防打火结束,跳转
s_TimeDelay ≥ 900ms → FirePreventEnd_flg = 1 → 进入 kIdlePowerOn

整个预充时序持续约 900ms:200ms 断开(等待) + 500ms 导通(充电容) + 200ms 余量。

3.3 kIdlePowerOn ------ 闭合主回路

预充完成后,放电 MOS 闭合,预充 MOS 断开,进入正常放电模式。

3.4 防打火重新开启

放电结束后电流归零超过 2 秒s_FirePreventEnd_flg 自动清零,下次再有电流时重新走防打火流程。

3.5 什么时候触发防打火?

防打火只在 "负载电容可能已经放空了" 的时候触发,不是每次有电流都走。

触发(三种情况):

场景 触发原因
BMS 上电启动 默认 IdleStMachine = kIdleInit,直接走防打火
空闲时无电流 > 2s 电容可能通过漏电流放空,FirePreventEnd_flg 自动清零
充电 > 3 分钟后回空闲 IdleStMachine 重置为 kIdleInit

不触发:

从放电模式退出回空闲时,跳过防打火 ,直接跳到 kIdlePowerOn

c 复制代码
// 从放电退出到idle模式,先到poweron模式
// (退到init中立刻拧电门有掉电的风险)
IdleStMachine_enum = kIdlePowerOn;

因为刚放完电,负载电容还满着------不需要预充。而且用户可能是等红灯松油门,马上又要拧------如果每次都走 700ms 防打火,体验会很差。


四、短路保护联动

短路发生后,即使 AFE 已经在 50µs 关断了放电 MOS,MCU 端的 PreChgIf 也会主动闭合预充 MOS(PreChgIf.c:122-125):

c 复制代码
// 发生短路,强制进行预充
if(g_Afe1SampleRawData.ShortCircuitPreChg_Cnt != 0)
{
    g_PreChg_cmd = 1U;  // 强制闭合预充 MOS
}

短路可能是负载电容充电的瞬时大电流导致的误判------强制预充相当于给电容一次"温柔充电"的机会,如果充满后不再触发短路,说明是容性负载而非真短路。


五、预充电路故障检测

预充支路通过 ADC 检测预充电阻上的电流(PreChgIf.c:137):

c 复制代码
// 22Ω 采样电阻,ADC 读数换算为电流
g_PreChgCurrent_mA = ADC值 × 33 × 10 × 1000 / (4095 × 22);

如果预充电流 > 2A 持续 1s ,判定预充电路故障(电阻短路或 MOS 击穿),上报 PreChgCircuitFlt,同时禁用防打火功能。


六、关键参数

参数 说明
预充电阻 22Ω 限流,60V 电池最大预充电流 ~2.7A
预充等待 200ms 断开等待
预充导通 500ms 电容充电时间
预充总时长 ~900ms
防打火重开 无电流 2s 自动重置
预充故障判定 >2A 持续 1s 电阻短路/MOS 击穿

七、200ms 断开阶段的作用

用户拧电门 → kIdleInit 检测到电流 → 进入 kIdleFirePrevent → 预充 MOS 先断开 200ms。

这 200ms 不是充电容,而是状态复位

  • 进入 kIdleFirePrevent 之前,预充 MOS 在 kIdleInit 里已经是闭合的。先断开才有明确的预充起点
  • 给电路一个静默窗口,等残余电流或接触抖动消退
  • 如果 200ms 内电流归零,说明用户只是碰了一下油门而非真要骑车,不需要后续流程

八、为什么用时间预估而不是检测外侧电压?

更好的方案是:外侧加电压采集 → 读到 V_out ≈ V_bat 就切主 MOS。但这需要一路 ADC + 两个分压电阻(60V → 3.3V),板子上没做纯粹是省成本。

时间预估为什么够用?最差情况:

复制代码
预充电阻 22Ω,负载电容 5000µF:
  τ = 22 × 0.005 = 110ms
  5τ = 550ms → 充到 99%

实际给 500ms,电容再大也充到 95%,压差仅 ~3V,残余电流 < 140mA → 不打火

如果有外侧电压采集,逻辑可以简化成两个状态,还能额外获得:自适应电容、预充超时故障检测、MOS 粘连检测、接插件松动检测------但代价就是那两个电阻。


本文基于量产 BMS 代码分析。

相关推荐
wabs6662 小时前
关于动态规划【力扣1035.不相交的线和53.最大子数组和的思考】
算法·leetcode·动态规划
退休倒计时2 小时前
【每日一题】LeetCode 199. 二叉树的右视图 TypeScript
算法·leetcode·typescript
可编程芯片开发2 小时前
通过MATLAB实现PID控制器,积分分离控制器以及滑模控制器
算法
kebidaixu2 小时前
两轮BMS 短路保护策略详解
算法
zwenqiyu3 小时前
非线性字符串数据结构串讲
数据结构·c++·学习·算法
z小猫不吃鱼3 小时前
03 Optimal Brain Surgeon 详解:Hessian 剪枝为什么有效?
算法·机器学习·剪枝
硕风和炜3 小时前
【LeetCode: 1301. 最大得分的路径数目 + DP】
java·算法·leetcode·动态规划·dp·记忆化搜索
用户99045017780093 小时前
做了一个AI诊断,参考倪海厦中医理论,科学养生
算法
努力中的编程者3 小时前
STL-vector的模拟实现
开发语言·c++·算法·stl·vector