You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对不同条件下的肱二头肌弯举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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:16:25