4. 肌电信号预处理:滤波、去噪与基线校正
好,咱们进入第四章。说实话,肌电信号预处理这块,是我在实验室里踩坑最多的地方。你想想看,从皮肤表面采集到的信号,那叫一个「脏」——工频干扰、运动伪迹、基线漂移,啥都有。如果不把这些乱七八糟的东西处理干净,后面做肌肉协同分析,结果基本没法看。
我个人习惯把预处理分成三步走:滤波、去噪、基线校正。每一步都有它的门道。咱们一个一个来聊。
4.1 为什么要做预处理?
先问个问题:你从电极上直接采到的信号,真的是肌肉活动产生的吗?
答案是:不完全是。原始EMG信号里,混杂着各种噪声。我遇到过最夸张的一次,受试者只是轻轻动了一下手臂,信号里就出现了一个巨大的尖峰,差点把放大器给饱和了。后来一查,是电极线接触不良。
所以,预处理的目的就三个:
- 滤掉无关频率——比如50Hz的工频干扰
- 去除突发噪声——比如电极松动产生的尖峰
- 校正基线漂移——让信号回到零电平附近
说白了,就是让信号「干净」到能反映真实的肌肉激活模式。
4.2 滤波:把该留的留下,该扔的扔掉
滤波是预处理的第一步。EMG信号的有效能量主要集中在20-500Hz之间。低于20Hz的,大多是运动伪迹;高于500Hz的,基本是高频噪声。
我常用的滤波器组合是这样的:
| 滤波器类型 | 截止频率 | 作用 |
|---|---|---|
| 高通滤波器 | 20-30 Hz | 去除低频漂移和运动伪迹 |
| 低通滤波器 | 450-500 Hz | 去除高频噪声 |
| 带阻滤波器 | 48-52 Hz | 去除工频干扰(50Hz) |
嗯,这里要注意:滤波器的阶数不要选太高。我见过有人用8阶巴特沃斯滤波器,结果信号相位失真得一塌糊涂。我个人建议用4阶,零相位滤波(用filtfilt函数),这样能避免相位偏移。
给你看一段我常用的Python代码:
import numpy as np
from scipy.signal import butter, filtfilt
def preprocess_emg(signal, fs=1000):
# 高通滤波 20Hz
b, a = butter(4, 20/(fs/2), btype='high')
signal = filtfilt(b, a, signal)
# 低通滤波 450Hz
b, a = butter(4, 450/(fs/2), btype='low')
signal = filtfilt(b, a, signal)
# 带阻滤波 50Hz
b, a = butter(4, [48/(fs/2), 52/(fs/2)], btype='bandstop')
signal = filtfilt(b, a, signal)
return signal
这段代码我用了好几年,基本没出过问题。但有一点要提醒你:采样率fs一定要设对。如果采样率是2000Hz,那奈奎斯特频率就是1000Hz,截止频率要相应调整。
4.3 去噪:别让噪声毁了你的数据
滤波能解决大部分问题,但有些噪声是滤不掉的。比如电极突然松动产生的尖峰,或者受试者咳嗽引起的肌电爆发。这些噪声在频域上和真实信号重叠,没法用滤波器分离。
那怎么办?我一般用两种方法:
- 阈值法:设定一个幅度阈值,超过阈值的点直接剔除或替换。阈值怎么定?我习惯用信号标准差的3-5倍。
- 小波去噪:把信号分解到不同尺度,把噪声所在的小波系数置零,再重构信号。这个方法对非平稳噪声特别有效。
我曾经遇到过一个案例:受试者在实验过程中打了个喷嚏,EMG信号里出现了一个巨大的尖峰。用阈值法处理后,数据基本恢复了正常。但要注意,阈值不能设得太低,否则会把真实的肌肉爆发信号也给削掉。
4.4 基线校正:让信号回到零电平
基线漂移是个很烦人的问题。你想想看,受试者刚开始放松时,信号应该在0mV附近。但过了一段时间,由于电极极化、皮肤出汗等原因,基线可能慢慢飘到了0.1mV甚至更高。
基线校正的方法很简单:
- 取一段静息状态的数据(比如受试者完全放松时的信号)
- 计算这段数据的平均值
- 从整个信号中减去这个平均值
但这里有个坑:静息状态的数据段怎么选?我建议在实验开始前,先记录5-10秒的静息数据。如果实验过程中需要多次校正,可以在每个动作之间插入一段静息期。
代码实现也很简单:
def baseline_correct(signal, baseline_segment):
baseline_mean = np.mean(baseline_segment)
corrected_signal = signal - baseline_mean
return corrected_signal
嗯,就这么几行。但效果立竿见影。
4.5 预处理流程总览
说了这么多,咱们来画个流程图,把整个预处理流程串起来。我个人觉得,可视化能帮你更好地理解每一步之间的关系。
你看,整个流程是线性的。但实际工作中,我经常需要来回调整。比如滤波后发现噪声还是很大,那就得回去调整截止频率或者去噪参数。预处理不是一锤子买卖,而是一个迭代优化的过程。
4.6 常见问题与解决方案
最后,我总结几个我在项目中遇到过的问题,供你参考:
- 问题:滤波后信号幅度变小了
原因:滤波器增益设置不当,或者截止频率太靠近信号主频。
解决:检查滤波器参数,确保通带增益为1。 - 问题:去噪后信号出现「阶梯状」
原因:阈值法处理时,对超过阈值的点直接置零,导致信号不连续。
解决:改用插值或平滑方法替换异常点,而不是直接置零。 - 问题:基线校正后信号仍然有漂移
原因:静息数据段选择不当,或者漂移是非线性的。
解决:使用多项式拟合或高通滤波来去除低频漂移。
嗯,预处理这块就聊到这儿。记住一句话:预处理做得好,后续分析事半功倍。我见过太多人急着做肌肉协同提取,结果因为预处理没做好,得出来的协同模式全是噪声。别急,先把信号洗干净。
- 滤波:高通20Hz + 低通450Hz + 带阻50Hz
- 去噪:阈值法(3-5倍标准差)或小波去噪
- 基线校正:减去静息段均值
- 预处理是迭代过程,需要反复调整参数