3. 霍尔信号滤波与去抖:硬件RC滤波设计、软件数字滤波、边沿抖动处理

霍尔传感器信号,说白了就是三个方波。但实际电机跑起来,这方波可没那么干净。我见过太多工程师,上来就写中断、算速度,结果波形一抖,整个控制就乱了。霍尔信号滤波,是调参的第一步,也是容易被忽略的一步。

核心观点:霍尔信号滤波的目标不是“消除所有噪声”,而是“在延迟和准确性之间找到平衡”。滤波太狠,换相滞后;滤波太弱,误触发频繁。

3.1 霍尔信号为什么需要滤波?

霍尔传感器输出的本质是磁感应强度变化。电机运行时,有几个因素会让信号变脏:

  • 机械振动:转子抖动,导致霍尔元件感应到的磁场边缘来回跳动
  • PWM开关噪声:功率管开关瞬间,电流突变会耦合到霍尔信号线上
  • 磁滞效应:霍尔元件本身有磁滞,过零点附近输出不稳定
  • 线缆耦合:长线传输时,电机线对霍尔线的串扰

我有个项目,霍尔线跟电机线绑在一起走。一开始没滤波,电机一转,霍尔信号上全是毛刺,速度计算值跳得像心电图。后来加了RC滤波,世界清净了。

3.2 硬件RC滤波设计

硬件滤波是最直接的手段。一个电阻加一个电容,简单粗暴,但参数选不对,反而坏事。

3.2.1 基本原理

RC低通滤波的截止频率公式:

f_c = 1 / (2π × R × C)

霍尔信号的频率范围,取决于电机极对数和转速。举个例子:一个4对极的电机,转速3000RPM,霍尔信号频率就是:

f_hall = (3000 / 60) × 4 = 200Hz

截止频率一般取霍尔基频的3~5倍。这样既能滤掉高频噪声,又不会把有效边沿削得太圆。

3.2.2 参数选择实战

电机类型 典型转速范围 推荐R值 推荐C值 截止频率
低速大扭矩 0~500 RPM 10kΩ 100nF ~159 Hz
中速通用 500~3000 RPM 4.7kΩ 47nF ~720 Hz
高速主轴 3000~10000 RPM 2.2kΩ 22nF ~3.3 kHz

我的习惯:先按表格选一组参数,然后用示波器看霍尔信号的上升沿。如果边沿斜率太缓(超过10μs),就减小R或C。如果毛刺还在,就增大C。调试时,我一般从4.7kΩ+47nF开始试。

3.2.3 施密特触发器配合

RC滤波后的信号边沿会变缓。如果直接进MCU的GPIO,可能会在阈值附近来回跳。这时候需要施密特触发器整形。

我常用的方案:

  • 分立元件:74HC14(六反相施密特触发器),便宜又好买
  • MCU内部:部分MCU的GPIO自带施密特触发输入,比如STM32的某些引脚
  • 软件整形:如果硬件来不及改,可以在中断里做滞回比较

注意:RC滤波会引入延迟。RC时间常数τ = R × C。比如4.7kΩ+47nF,τ ≈ 220μs。这个延迟在低速时影响不大,但高速电机(比如10000RPM以上)可能造成换相角度偏差。我曾经在高速风机项目上吃过这个亏,后来把R改成了1kΩ。

3.3 软件数字滤波

硬件滤波搞定了大部分噪声,但总有些顽固的毛刺。这时候软件滤波上场。

3.3.1 中值滤波

原理很简单:连续采样N次,取中间值作为有效值。对脉冲噪声特别有效。

// 中值滤波示例(N=5)
uint8_t hall_median_filter(uint8_t *buf, uint8_t len) {
    uint8_t temp[5];
    memcpy(temp, buf, 5);
    // 冒泡排序(数据量小,无所谓效率)
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4 - i; j++) {
            if (temp[j] > temp[j+1]) {
                uint8_t t = temp[j];
                temp[j] = temp[j+1];
                temp[j+1] = t;
            }
        }
    }
    return temp[2]; // 取中间值
}

N取奇数,一般3或5就够了。N越大,滤波效果越好,但响应越慢。

3.3.2 均值滤波

连续采样N次,取平均值。对高斯噪声效果好,但对脉冲噪声效果一般。

// 均值滤波示例
uint8_t hall_mean_filter(uint8_t *buf, uint8_t len) {
    uint32_t sum = 0;
    for (int i = 0; i < len; i++) {
        sum += buf[i];
    }
    return (uint8_t)(sum / len);
}

我的经验:均值滤波适合用在速度计算上,不适合用在换相判断上。换相需要的是“边沿时刻”,而不是“平均电平”。我曾经在换相中断里用了均值滤波,结果换相时刻被平均掉了,电机反而抖得更厉害。

3.3.3 卡尔曼滤波(轻量版)

卡尔曼滤波听起来高大上,但用在霍尔信号上,其实可以简化成一维模型。核心思想:用上一刻的估计值和当前测量值,加权平均得到新的估计值。

// 一维卡尔曼滤波(简化版)
typedef struct {
    float x;      // 估计值
    float p;      // 估计误差协方差
    float q;      // 过程噪声协方差
    float r;      // 测量噪声协方差
    float k;      // 卡尔曼增益
} Kalman1D;

uint8_t kalman_update(Kalman1D *kf, uint8_t z) {
    // 预测
    kf->p = kf->p + kf->q;
    // 更新
    kf->k = kf->p / (kf->p + kf->r);
    kf->x = kf->x + kf->k * ((float)z - kf->x);
    kf->p = (1 - kf->k) * kf->p;
    return (uint8_t)(kf->x + 0.5f);
}

参数调优:

  • q(过程噪声):霍尔信号变化快,q设大一点,比如0.1~0.5
  • r(测量噪声):噪声大,r设大,比如1.0~5.0
  • 初始p:设1.0就行,卡尔曼会自己收敛

注意:卡尔曼滤波在MCU上跑,要注意浮点运算开销。如果MCU没有FPU,建议用定点数实现。我在一个Cortex-M0项目上用过,每个通道更新一次大约需要50μs,三个通道就是150μs,对高速电机来说有点吃力。

3.4 边沿抖动处理

边沿抖动,是霍尔信号滤波里最头疼的问题。明明电机在匀速转,霍尔边沿的时间间隔却忽大忽小。

3.4.1 抖动的来源

  • 机械原因:转子偏心、轴承间隙、负载波动
  • 电气原因:PWM开关噪声、地弹、电源纹波
  • 传感器原因:霍尔元件安装偏差、磁钢充磁不均匀

3.4.2 软件去抖策略

我常用的几种方法:

  1. 边沿锁定(Debounce Timer):检测到边沿后,启动一个定时器,在定时器超时前忽略后续边沿
  2. 连续确认:连续采样N次,如果N次都检测到边沿,才认为有效
  3. 窗口比较:记录上一次边沿的时间,设定一个窗口(比如±20%),只有落在窗口内的边沿才接受
// 边沿锁定示例
#define DEBOUNCE_US 50  // 50μs去抖时间

volatile uint32_t last_edge_time[3] = {0};

uint8_t hall_edge_debounce(uint8_t channel, uint32_t now_us) {
    if ((now_us - last_edge_time[channel]) > DEBOUNCE_US) {
        last_edge_time[channel] = now_us;
        return 1;  // 有效边沿
    }
    return 0;  // 抖动,忽略
}

避坑指南:我曾经在一个项目里,去抖时间设了200μs。结果电机高速运行时,霍尔信号周期只有500μs,去抖时间吃掉了一半的周期,换相完全乱套。后来我把去抖时间改成了20μs,配合硬件RC滤波,效果很好。记住:去抖时间不能超过霍尔信号最小周期的10%。

3.5 滤波方案选择与组合

没有一种滤波方案是万能的。实际项目中,我通常这样组合:

霍尔信号滤波方案组合 原始霍尔信号 硬件RC低通滤波 R=4.7kΩ + C=47nF + 施密特整形 信号是否干净? 是 否 边沿锁定去抖 Debounce Timer 50μs 软件数字滤波 中值/均值/卡尔曼 干净霍尔信号 建议:先硬件滤波,再软件去抖。如果硬件滤波后信号仍不干净,再启用数字滤波。

我的推荐组合:

应用场景 硬件滤波 软件滤波 去抖策略
低速大扭矩(<500 RPM) 10kΩ + 100nF 中值滤波(N=3) 边沿锁定 100μs
中速通用(500~3000 RPM) 4.7kΩ + 47nF 均值滤波(N=5) 边沿锁定 50μs
高速主轴(>3000 RPM) 2.2kΩ + 22nF 卡尔曼滤波 连续确认(N=3)
高噪声环境(如电钻) 10kΩ + 220nF 中值+均值组合 窗口比较 ±15%

总结:霍尔信号滤波,硬件是基础,软件是补充。先搞定RC参数,再根据实际波形决定要不要加软件滤波。别一上来就上卡尔曼,很多时候一个简单的边沿锁定就够用了。记住:滤波的目的是让信号可用,不是让信号完美。