第3章 肌电信号预处理:工频干扰与滤波

说实话,刚接触肌电信号那会儿,我最头疼的就是预处理。你想想看,辛辛苦苦贴好电极,让受试者做动作,结果采集出来的信号长这样——一条弯弯曲曲的线,上面还叠着好多毛刺。嗯,这其实不是真正的肌电信号,而是被各种噪声污染过的原始数据。

我习惯把预处理比作「洗菜」。菜买回来不能直接下锅,得先摘掉烂叶子、洗掉泥土。肌电信号也一样,不洗干净,后面的分析全是白搭。今天咱们就重点聊三个最常见的「脏东西」:工频干扰、基线漂移、运动伪影。

核心知识点速览

  • 工频干扰 → 50Hz陷波滤波
  • 基线漂移 → 高通滤波(截止频率5-20Hz)
  • 运动伪影 → 带通滤波(10-500Hz)

3.1 工频干扰与50Hz陷波

工频干扰,说白了就是电网的「嗡嗡声」。在中国,电网频率是50Hz,这个频率会通过空间电磁场耦合到你的电极线和放大器上。我在实验室里遇到过最夸张的一次——信号里50Hz的幅度比肌电本身还大,整个波形看起来就像一条粗毛线。

怎么判断是不是工频干扰?很简单。你让受试者完全放松,肌肉不发力,这时候采集到的信号如果还有明显的50Hz正弦波,那就是工频干扰。我个人的经验是:先检查接地,再考虑滤波。很多时候接地没做好,滤波也救不了。

陷波滤波器的设计,我推荐用IIR滤波器,阶数不要太高,2阶就够了。太高阶会引入相位失真,反而把有用的肌电信号给削了。

# Python示例:50Hz陷波滤波器
from scipy import signal
import numpy as np

# 设计一个2阶50Hz陷波滤波器
fs = 1000  # 采样率1000Hz
f0 = 50.0  # 陷波频率
Q = 30.0   # 品质因数

b, a = signal.iirnotch(f0, Q, fs)

# 应用滤波
filtered_signal = signal.filtfilt(b, a, raw_emg)

我的小技巧:用filtfilt代替lfilter,可以实现零相位延迟。虽然计算量翻倍,但信号不会发生偏移,这对后续的时序分析很重要。

3.2 基线漂移与高通滤波

基线漂移是什么?你让受试者握拳再松开,会发现信号整体在上下浮动,就像海浪一样。这主要是因为电极与皮肤之间的接触阻抗在变化,或者受试者身体有轻微移动。

我曾经犯过一个错误:为了去除基线漂移,直接把高通滤波的截止频率设到了50Hz。结果肌电信号的高频成分也被滤掉了,动作识别准确率直接掉到60%。后来才明白,肌电信号的有效能量集中在20-150Hz,基线漂移的频率通常低于5Hz。所以,高通滤波的截止频率设在5-20Hz之间就足够了。

# Python示例:高通滤波去除基线漂移
from scipy import signal

fs = 1000
cutoff = 10  # 截止频率10Hz
order = 4

# 设计高通滤波器
b, a = signal.butter(order, cutoff, btype='high', fs=fs)

# 应用滤波
emg_no_drift = signal.filtfilt(b, a, emg_after_notch)

注意:高通滤波的截止频率不能太高。如果设到30Hz以上,你会把肌电信号中低频的力信息也滤掉。我见过有人设到100Hz,结果信号看起来干净了,但肌肉发力时的幅度变化也消失了——这等于把婴儿和洗澡水一起倒掉。

3.3 运动伪影与带通滤波

运动伪影,这是最让人头疼的噪声。它来自电极线晃动、肌肉收缩时的机械形变,甚至衣服摩擦。频率范围很宽,从几Hz到几百Hz都有。我刚开始做康复机器人控制时,受试者一抬手,信号里就出现一个巨大的尖峰,控制器直接误判成肌肉痉挛。

处理运动伪影,我建议用带通滤波。把通带设在10-500Hz,既能保留肌电信号的主要能量,又能滤掉低频的运动伪影和高频的电子噪声。注意,500Hz以上的成分基本是噪声,没必要保留。

# Python示例:带通滤波去除运动伪影
from scipy import signal

fs = 1000
lowcut = 10
highcut = 500
order = 4

# 设计带通滤波器
b, a = signal.butter(order, [lowcut, highcut], btype='band', fs=fs)

# 应用滤波
emg_clean = signal.filtfilt(b, a, emg_no_drift)

预处理流程总结

  1. 原始肌电信号 → 50Hz陷波(去工频)
  2. 陷波后信号 → 10Hz高通(去基线漂移)
  3. 高通后信号 → 10-500Hz带通(去运动伪影)
  4. 输出:干净的肌电信号,可用于后续特征提取

3.4 滤波顺序有讲究吗?

有。我个人习惯先做陷波,再做高通,最后做带通。为什么?因为工频干扰的幅度往往很大,如果先做高通,50Hz的干扰会被放大,后面再陷波效果就差了。你想想看,先去掉最大的噪声,后面的处理会轻松很多。

当然,也有同行喜欢先做带通再做陷波。我试过,效果差不多,但计算量会大一些。嗯,这个没有绝对的对错,你习惯哪种就用哪种。

3.5 避坑指南

我曾经踩过一个坑:用移动平均滤波去基线漂移。移动平均本质上是一个低通滤波器,它会把肌电信号的高频成分也平滑掉。结果信号看起来是平了,但肌肉活动的细节全没了。后来我改用高通滤波,效果立竿见影。

还有一个坑:滤波器的阶数。阶数越高,滤波效果越好,但相位失真也越严重。我建议IIR滤波器不超过4阶,FIR滤波器不超过50阶。超过这个范围,信号延迟会大到无法接受,尤其是在实时控制系统中。

快速检查滤波效果的方法:滤波前后对比频谱图。如果50Hz处有明显的凹陷,说明陷波有效;如果10Hz以下的能量被大幅衰减,说明高通有效;如果10Hz以下和500Hz以上的能量都被衰减,说明带通有效。用眼睛看波形是不够的,一定要看频谱。

3.6 本章知识体系

下面这张图是我自己画的,把本章的核心逻辑串起来了。你看一遍,应该就能记住预处理的全流程。

肌电信号预处理流程 原始肌电信号 50Hz陷波滤波 去除工频干扰 高通滤波(10Hz) 去除基线漂移 带通滤波(10-500Hz) 去除运动伪影 干净肌电信号 滤波顺序:陷波 → 高通 → 带通 滤波器类型:IIR Butterworth,阶数2-4 实现方式:scipy.signal.filtfilt(零相位)

好了,这一章的内容就到这里。预处理是肌电分析的地基,地基打不牢,后面盖什么楼都会歪。我建议你拿到数据后,先花10分钟做一遍预处理,看看频谱图,确认噪声被去掉了,再开始做特征提取。别急着跳步,这一步省下来的时间,后面会加倍还给你。


公众号:蓝海资料掘金营,微信deep3321