4、电流极性检测:相电流采样技术、电流过零点检测难点、零电流钳位效应

各位同学,咱们今天聊的这个话题,可以说是死区补偿里最让人头疼的一环——电流极性检测。

你想想看,死区补偿的核心是什么?说白了就是根据电流方向去调整占空比。但问题来了:你怎么知道电流方向?靠采样。可采样本身就有误差,尤其是电流过零点附近,那叫一个“剪不断理还乱”。

我个人习惯把这个问题拆成三块来讲:采样技术、过零点检测、零电流钳位。咱们一个一个啃。

4.1 相电流采样技术

先说说采样。目前主流的方法就两种:

  • 单电阻采样:成本低,但重构电流需要技巧
  • 双电阻/三电阻采样:精度高,但硬件成本上去了

我在项目中遇到过最尴尬的事——用单电阻采样,结果低速下电流重构失败,电机抖得像筛子。后来我学乖了,低速工况下强制切换到双电阻模式。

这里给个采样时序的参考代码,我用的是STM32G4系列:

// 三相电流采样触发配置
// 使用定时器1的CH1/CH2/CH3作为触发源
void CurrentSampling_Init(void)
{
    // 配置ADC注入组,由TIM1_TRGO触发
    ADC_InjectedConfTypeDef sConfigInjected = {0};
    
    sConfigInjected.InjectedChannel = ADC_CHANNEL_1;  // U相
    sConfigInjected.InjectedRank = ADC_INJECTED_RANK_1;
    sConfigInjected.InjectedSamplingTime = ADC_SAMPLINGTIME_7CYCLES_5;
    HAL_ADCEx_InjectedConfig(&hadc1, &sConfigInjected);
    
    // 注意:采样点必须避开PWM开关边沿
    // 我一般设置在PWM计数器的中间点
    __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, 
                          (PWM_PERIOD >> 1) - 50);
}
我的经验:采样时刻的选择比采样精度更重要。宁可少采几个点,也别采到开关噪声上。我曾经为了省一个采样周期,结果采到了IGBT关断的尖峰,整个电流环都崩了。

4.2 电流过零点检测难点

好,采样搞定了,接下来是重头戏——过零点检测。

为什么要检测过零点?因为死区补偿在过零点附近最容易出错。你想想,电流接近0的时候,方向判断稍微偏一点,补偿方向就反了,结果越补越乱。

难点主要有三个:

  1. 采样噪声:电流小的时候,信噪比极低。我见过有人用10位ADC采0.1A的电流,那结果基本是随机数。
  2. ADC量化误差:12位ADC在满量程3.3V下,1LSB对应约0.8mV。如果采样电阻是0.01Ω,那1LSB对应的电流就是80mA。你想想,80mA以下的电流根本测不准。
  3. 延时问题:从采样到计算出结果,中间有延时。电流过零点变化很快,延时可能导致方向判断滞后。

怎么解决?我个人的做法是:

  • 用硬件比较器做快速过零判断,不依赖ADC
  • 或者用软件滤波+滞回比较

下面是我常用的滞回比较算法:

// 电流过零点检测 - 滞回比较
// 滞回窗口:±50mA
#define HYSTERESIS_CURRENT  50  // 单位mA

typedef enum {
    CURRENT_POSITIVE,
    CURRENT_NEGATIVE,
    CURRENT_ZERO
} CurrentPolarity_t;

CurrentPolarity_t DetectPolarity(int16_t current_mA)
{
    static CurrentPolarity_t last_polarity = CURRENT_ZERO;
    static int16_t last_current = 0;
    
    // 先做一阶低通滤波,截止频率约500Hz
    int16_t filtered = (last_current * 3 + current_mA) >> 2;
    last_current = filtered;
    
    // 滞回判断
    if (filtered > HYSTERESIS_CURRENT) {
        last_polarity = CURRENT_POSITIVE;
    } else if (filtered < -HYSTERESIS_CURRENT) {
        last_polarity = CURRENT_NEGATIVE;
    }
    // 在滞回区内保持上次状态
    
    return last_polarity;
}
注意:滞回窗口不能设太大,否则过零点附近会有死区。我一般取额定电流的1%~2%。太大影响补偿效果,太小又滤不掉噪声。

4.3 零电流钳位效应

这个现象,我估计很多做驱动的朋友都遇到过,但未必知道它叫什么。

零电流钳位,说白了就是:当相电流接近0时,由于死区时间的存在,电流会被“卡”在0附近,下不去也上不来。就像钳子夹住了一样。

为什么会这样?

你想想,死区时间内上下桥臂都关断,电流只能通过续流二极管走。如果电流很小,二极管上的压降不足以维持电流继续流动,电流就会“断流”。等下一个PWM周期来了,电流又从0开始爬升。

结果就是:电流波形在过零点附近出现一段平坦的“台阶”,谐波含量暴增。

我在做伺服驱动器时遇到过这个问题,电机在低速运行时嗡嗡响,FFT一看,5次、7次谐波特别大。排查了半天,最后发现是零电流钳位搞的鬼。

解决思路有两个:

方法 原理 优缺点
最小脉冲注入 在电流过零点附近,强制插入一个最小宽度的脉冲,打破钳位 实现简单,但会增加开关损耗
死区时间动态调整 根据电流大小动态调整死区时间,电流小时减小死区 效果好,但需要精确的电流检测

我个人更倾向于第二种方法。下面是我在项目中用过的动态死区调整代码:

// 动态死区时间调整
// 根据相电流绝对值调整死区时间
uint16_t CalcDeadTime(int16_t phase_current_mA)
{
    uint16_t base_deadtime = 2000;  // 基础死区 2us
    uint16_t min_deadtime = 500;    // 最小死区 0.5us
    
    int16_t abs_current = abs(phase_current_mA);
    
    // 电流越小,死区越小
    // 当电流大于1A时,使用基础死区
    if (abs_current > 1000) {
        return base_deadtime;
    }
    
    // 电流在0~1A之间,线性减小死区
    uint16_t deadtime = base_deadtime - 
                        (base_deadtime - min_deadtime) * abs_current / 1000;
    
    // 确保不低于最小值
    return (deadtime < min_deadtime) ? min_deadtime : deadtime;
}
核心要点:零电流钳位效应是死区补偿绕不开的坎。你可以在算法层面做各种补偿,但如果硬件上电流检测精度不够,一切都是白搭。我建议大家在设计阶段就把采样电阻、ADC精度、滤波电路这些基础打好,后面会省很多事。

嗯,关于电流极性检测,今天就聊这么多。下一章咱们讲具体的死区补偿算法实现,到时候会用到今天讲的这些内容。记得把代码跑一跑,有问题随时交流。