如何对不同条件下的肱二头肌弯举EMG信号曲线进行时间校准?
解决肱二头肌弯举EMG信号的时间对齐问题
我之前处理过不少肌电信号的动作对齐需求,你说的“时间校准”其实就是动作时序对齐——核心是把不同条件下的信号,基于动作的关键时间点(起始点/峰值点)统一到同一个时间轴上,解决起始动作不同步导致的曲线偏移问题。下面给你几个从简单到进阶的实用方案,附代码示例:
方案1:基于动作起始点的对齐(最常用)
这个方法的核心是找到每个EMG信号中动作开始的第一个时间点,然后把所有信号都平移到这个起始点对齐。步骤如下:
- 先预处理EMG信号:做带通滤波(比如20-500Hz)、去除基线漂移(用滑动窗口均值或者高通滤波),减少噪声干扰;
- 计算基线信号的标准差,设置一个阈值(比如基线标准差的3-5倍),当EMG信号连续N个采样点超过这个阈值时,判定为动作起始点;
- 把每个条件下的信号,以各自的起始点为原点,平移到同一时间轴(比如所有信号的起始点都对应时间t=0)。
示例代码(Python + NumPy/SciPy):
import numpy as np from scipy.signal import butter, filtfilt import matplotlib.pyplot as plt def preprocess_emg(emg_signal, fs=1000): # 带通滤波(20-500Hz) b, a = butter(4, [20/(fs/2), 500/(fs/2)], btype='band') filtered_emg = filtfilt(b, a, emg_signal) # 去除基线漂移(滑动窗口均值,窗口大小0.2s) window_size = int(0.2 * fs) baseline = np.convolve(filtered_emg, np.ones(window_size)/window_size, mode='same') return filtered_emg - baseline def find_start_point(processed_emg, fs=1000, threshold_factor=3, window=10): # 计算基线标准差(取前0.5s的信号作为基线) baseline = processed_emg[:int(0.5*fs)] baseline_std = np.std(baseline) threshold = baseline_std * threshold_factor # 找连续window个点超过阈值的第一个位置 for i in range(len(processed_emg) - window): if np.all(processed_emg[i:i+window] > threshold): return i return 0 # 找不到的话返回起始点 # 假设你有多个条件的EMG数据,存在一个列表里:emg_conditions = [emg1, emg2, emg3, ...] fs = 1000 # 采样频率 # 这里用模拟数据代替真实EMG,实际使用时替换成你的数据 emg_conditions = [ np.random.randn(3000) + np.pad(np.sin(np.linspace(0, 3*np.pi, 1500)), (500, 1000), mode='constant'), np.random.randn(3000) + np.pad(np.sin(np.linspace(0, 3*np.pi, 1500)), (800, 700), mode='constant'), np.random.randn(3000) + np.pad(np.sin(np.linspace(0, 3*np.pi, 1500)), (300, 1200), mode='constant') ] aligned_emgs = [] max_length = 0 # 先预处理并找到每个信号的起始点 processed_emgs = [] start_points = [] for emg in emg_conditions: processed = preprocess_emg(emg, fs) processed_emgs.append(processed) start = find_start_point(processed, fs) start_points.append(start) # 记录最长的有效信号长度(对齐后需要截断或补零) max_length = max(max_length, len(processed) - start) # 对齐所有信号到起始点t=0 for i in range(len(processed_emgs)): start = start_points[i] aligned = processed_emgs[i][start:start+max_length] # 如果长度不够,补零 if len(aligned) < max_length: aligned = np.pad(aligned, (0, max_length - len(aligned)), mode='constant') aligned_emgs.append(aligned) # 归一化并叠加展示 normalized_aligned = [emg / np.max(np.abs(emg)) for emg in aligned_emgs] time_axis = np.arange(max_length) / fs for idx, emg in enumerate(normalized_aligned): plt.plot(time_axis, emg, label=f'Condition {idx+1}') plt.legend() plt.xlabel('Time (s)') plt.ylabel('Normalized EMG Amplitude') plt.title('Aligned Bicep Curl EMG Signals') plt.show()
方案2:基于峰值点的对齐(适合起始点难捕捉的情况)
如果EMG信号的起始潜伏期差异大,或者起始点不好判定,可以直接对齐动作峰值点——也就是每个条件下EMG信号的最大值对应的时间点,把所有峰值都移到同一个时间位置(比如t=1s)。
示例代码片段:
# 假设已经预处理过的信号存在processed_emgs列表 peak_points = [] max_length = 0 for emg in processed_emgs: peak_idx = np.argmax(np.abs(emg)) # 取绝对值最大的点作为峰值点 peak_points.append(peak_idx) # 计算对齐后的最大长度(以峰值为中心,前后各取相同长度) half_length = max(peak_idx, len(emg) - peak_idx) max_length = max(max_length, 2*half_length) aligned_emgs = [] target_peak_pos = max_length // 2 # 把峰值对齐到时间轴中间位置 for i in range(len(processed_emgs)): peak_idx = peak_points[i] emg = processed_emgs[i] # 计算需要平移的量 shift = target_peak_pos - peak_idx # 平移信号(补零) if shift > 0: aligned = np.pad(emg, (shift, max_length - len(emg) - shift), mode='constant') else: aligned = np.pad(emg, (0, max_length - len(emg)), mode='constant')[abs(shift):] aligned_emgs.append(aligned)
方案3:动态时间规整(DTW)——进阶方案,适合动作时长不一致的情况
如果不同条件下的动作时长差异很大(比如有的受试者弯举快,有的慢),上面的平移对齐就不够了,这时候可以用动态时间规整(DTW),它能把不同长度的信号非线性对齐到同一时间维度,保留动作的时序特征。
示例代码(需先安装fastdtw:pip install fastdtw):
from scipy.spatial.distance import euclidean from fastdtw import fastdtw # 选一个参考信号(比如第一个条件的信号作为基准) reference_emg = processed_emgs[0] aligned_emgs = [reference_emg] for emg in processed_emgs[1:]: # 计算DTW路径 distance, path = fastdtw(emg, reference_emg, dist=euclidean) # 根据路径对齐信号 aligned = np.zeros_like(reference_emg) count = np.zeros_like(reference_emg) for i, j in path: aligned[i] += emg[j] count[i] += 1 # 平均对齐后的点(处理一对多映射) aligned = aligned / count aligned_emgs.append(aligned)
注意事项
- 预处理优先:对齐前一定要做滤波和去基线,不然噪声会干扰起始点/峰值点的判定;
- 归一化时机:建议先对齐再归一化,避免对齐过程中信号幅值被影响;
- 多受试者情况:如果是多个受试者的数据,要先对每个受试者的每个条件单独对齐,再跨条件/跨受试者叠加;
- 阈值调整:方案1中的阈值因子(3-5倍标准差)可以根据你的数据调整,如果噪声大就调大,噪声小就调小。
内容的提问来源于stack exchange,提问作者Mac.
相关产品推荐
相关产品推荐

