如何使用scipy.signal确定信号峰值的上升时间与衰减时间
如何用scipy.signal计算峰值的上升时间与衰减时间
嗨,作为信号处理新手能走到这一步已经很棒啦!你想要获取的上升时间(左宽度点到峰值的距离)和衰减时间(峰值到右宽度点的距离),其实完全可以通过你已经在使用的peak_widths函数的结果直接推导出来,不用额外去折腾一阶矩或者导数(当然如果之后想做更精细的分析,导数方法也可以尝试,先给你最贴合当前工作流的方案)。
直接利用peak_widths的返回值
首先你得知道,peak_widths返回的是一个包含4个元素的元组,其中后两个元素正好是我们需要的宽度点位置:
results_full[2]:每个峰值左宽度点的插值位置(因为宽度点可能落在两个采样点之间,所以是浮点数值)results_full[3]:每个峰值右宽度点的插值位置
而你已经得到的peaks数组里是峰值对应的整数采样索引,所以直接做减法就能得到时间差(因为采样间隔是固定的,这里ECG信号的采样率是360Hz,如果你需要转换成实际时间,再除以采样率就行)。
完整示例代码
先补全你之前的代码(记得导入numpy),然后计算并可视化这些时间:
import matplotlib.pyplot as plt import numpy as np from scipy.misc import electrocardiogram from scipy.signal import find_peaks, peak_widths # 获取ECG信号片段 x = electrocardiogram()[2000:4000] # 检测峰值 peaks, _ = find_peaks(x, height=0) # 计算完整的峰值宽度信息(rel_height=1对应峰值底部到顶部的宽度,你也可以调整比如0.5求半高宽) results_full = peak_widths(x, peaks, rel_height=1) # 计算上升时间和衰减时间 rise_times = peaks - results_full[2] decay_times = results_full[3] - peaks # 可视化展示 plt.plot(x, label='ECG Signal') plt.plot(peaks, x[peaks], "x", label='Peaks') # 画出左宽度点和右宽度点 plt.plot(results_full[2], results_full[1], "o", label='Left Width Points') plt.plot(results_full[3], results_full[1], "o", label='Right Width Points') plt.plot(np.zeros_like(x), "--", color="gray") # 标注第一个峰值的上升/衰减时间(示例) plt.annotate(f"Rise Time: {rise_times[0]:.2f} samples", xy=(results_full[2][0], results_full[1][0]), xytext=(results_full[2][0]-50, results_full[1][0]+0.5), arrowprops=dict(arrowstyle='->')) plt.annotate(f"Decay Time: {decay_times[0]:.2f} samples", xy=(results_full[3][0], results_full[1][0]), xytext=(results_full[3][0]+50, results_full[1][0]+0.5), arrowprops=dict(arrowstyle='->')) plt.legend() plt.show() # 打印所有峰值的时间信息 print("上升时间(采样点):", rise_times) print("衰减时间(采样点):", decay_times) # 如果要转换成实际时间(ECG采样率360Hz) sampling_rate = 360 print("上升时间(秒):", rise_times / sampling_rate) print("衰减时间(秒):", decay_times / sampling_rate)
可选:用导数方法检测上升/下降沿
如果你想更精确地捕捉信号从基线上升到峰值的时间(而不是基于某个相对高度的宽度点),可以通过计算信号的一阶导数来实现:
- 用平滑滤波器(比如
savgol_filter)预处理信号,减少噪声对导数的影响 - 计算一阶导数,找到导数由负变正的点(上升沿起点)和由正变负的点(下降沿终点)
- 计算这些点到峰值的距离
示例代码片段:
from scipy.signal import savgol_filter # 平滑信号并计算导数 smoothed_x = savgol_filter(x, window_length=11, polyorder=2) dx = np.gradient(smoothed_x) # 找上升沿:导数从负变正的位置(基线到峰值的起点) rise_edges = np.where((dx[:-1] < 0) & (dx[1:] > 0))[0] # 找下降沿:导数从正变负的位置(峰值到基线的终点) decay_edges = np.where((dx[:-1] > 0) & (dx[1:] < 0))[0] # 注意:需要把上升/下降沿和对应的峰值匹配,这里简单取最近的点 # (实际应用中可能需要更严谨的匹配逻辑) for peak in peaks: closest_rise = rise_edges[np.argmin(np.abs(rise_edges - peak))] closest_decay = decay_edges[np.argmin(np.abs(decay_edges - peak))] print(f"峰值{peak}的上升时间:{peak - closest_rise} 采样点") print(f"峰值{peak}的衰减时间:{closest_decay - peak} 采样点")
这种方法更贴近“沿”的物理定义,但需要处理噪声和匹配问题,适合对时间精度要求更高的场景。
内容的提问来源于stack exchange,提问作者user1631306
相关产品推荐
相关产品推荐

