简介:EQTransformer 是面向地震学研究者、台网分析人员和深度学习实践者的开源 Python 工具包,基于带注意力机制的深度神经网络完成地震信号检测与 P、S 相位拾取,可同时给出概率预测和模型不确定性,并提供预训练模型与可扩展模块,适用于连续波形处理、震相识别及模型训练评估。压缩包共 92 个文件、约 31.34MB,包含 20 个 py 源码、16 个 rst 文档、6 个 ipynb 示例、2 个 h5 预训练模型以及测试数据,覆盖数据下载、预处理、检测拾取、简单相位关联和建模测试等模块。已有 1176 人学习下载。读者可借助示例笔记本和预训练权重快速跑通流程,结合训练与测试脚本理解分层网络和多任务学习实现,并参考文档完成环境配置、模型复现与结果可视化,便于在地震监测和科研实验中开展二次开发。
1. 一条波形的判决:为什么地震检测要从「逐采样点的概率曲线」开始
把一段三分量波形丢进 EQTransformer,它不会告诉你「这里有个地震」,而是吐出三条与输入等长的概率曲线:检测概率、P 到时概率、S 到时概率。这个反直觉的输出形式,正是地震信号检测与相位拾取被统一成逐采样点二分类的直接后果——检测不再是一个独立的决策模块,而是和相位拾取共享同一副骨干网络、同一个时间轴。搞懂这条概率曲线怎么被造出来、又怎么被还原成震相,比记住几个调用参数值钱得多,因为所有误检、漏检、到时偏差的根因,几乎都能回溯到这条曲线的生成方式上。
2. 输入表征:三分量波形怎么变成网络能读的张量
2.1 三分量不是 RGB:通道语义决定了网络学什么
图像里三个通道可以互换,地震波形不行。E、N、Z 三个分量携带的物理信息是互补且不对称的:Z 分量对 P 波的初动最敏感,因为 P 波质点运动主要在垂直方向;水平两分量对 S 波能量更敏感,SH 成分在水平面上摆动。这意味着网络能学到一条非常具体的联合模式——「Z 上出现一个尖锐跳变,随后一到十几秒内水平分量上出现一段强振幅」——这条模式在单通道的短时平均/长时平均(STA/LTA)方法里根本表达不出来,因为传统方法每个通道各算各的比值,通道间的时序关系被丢掉了。
这条性质反过来解释了实践中大量「玄学」故障。三分量文件只给了 Z、通道顺序被打乱成 N/E/Z、某个分量极性接反,模型的输出都会塌掉,而且塌得很隐蔽:检测概率可能还勉强能看,P/S 概率直接变成噪声。常见的实现思路是在预处理阶段就做一次硬校验——检查通道数、通道命名、以及三分量之间的相关系数是否落在合理区间,宁可报错也不要让模型去猜。
2.2 窗口长度与采样率:模型的「视野」是被写死的
EQTransformer 这类模型的输入尺寸基本是固定的:约 60 秒窗长、100 Hz 采样率,对应 6000 个采样点、3 个通道。这个数字不是随手定的,它同时被物理和算力两头夹住。
先说物理。窗口必须足够长,才能装下 P 与 S 的到时差。震中距几十到一百多公里时,S-P 时差可以到十几秒,再加上 P 之前的背景噪声段和 S 之后的尾波段,60 秒是一个能覆盖大多数近区域事件的折中。窗口太短,S 直接被切在边界外,检测头能给出响应但 S 头没有标签可学;窗口太长,一个窗里塞进两个事件,标签生成时两个 P 峰互相干扰,模型学到的是「一条曲线可以有多个起点」,推理时配对逻辑就会乱。
再说算力。序列长度对自注意力的成本是平方级的,6000 点的注意力矩阵是三千六百万量级的元素,单层就能把显存吃光。把采样率统一下降到 100 Hz,既砍掉了对震相定位帮助不大的高频成分(很多台站的高频段本来就是噪声主导),也把序列长度压到了可训练的范围。这就是为什么模型对输入采样率极其敏感:拿一个 200 Hz 的台站数据直接喂进去,模型看到的「60 秒」其实是 30 秒的物理时间,P 和 S 全挤在一起,输出必然是错的。
幅值尺度同样致命。原始波形单位是仪器 counts,不同台站增益差几个数量级,直接送进网络,激活值分布完全跑偏。常用的做法是逐窗标准化——减均值、除标准差,把每个窗口拉到零均值单位方差。注意这里有个细节:标准化必须在整个窗口上做,而不是分段做,分段会破坏振幅的相对关系,而振幅衰减速率恰恰是区分近震与远震、真实事件与脉冲噪声的重要线索。
2.3 标签构造:高斯平滑是模型输出的「形状记忆」
网络输出的是连续概率而非 0/1 判决,这个设计从标签阶段就开始了。如果标签是硬脉冲——事件时刻为 1、其余全为 0——那么正样本占比在千分之一量级,二元交叉熵的梯度几乎被负样本淹没,模型很快学会「全输出 0」这个平凡解。把脉冲用高斯核卷一下,让峰值附近若干采样点也拿到 0.3、0.5 这样的软标签,等价于告诉模型「差一点点也算对」,梯度信号一下子厚了起来。
import numpy as np def gaussian_label(length, picks, sigma_samples, amp=1.0): """把离散到时转换成逐采样点的软标签。 length : 窗口采样点数,例如 6000 picks : 该窗口内所有到时(采样点索引)列表,可为空 sigma_samples: 高斯核标准差,决定标签的「宽容度」 """ label = np.zeros(length, dtype=np.float32) half = int(3 * sigma_samples) # 3 sigma 截断,够用且省算力 t = np.arange(-half, half + 1) kernel = np.exp(-0.5 * (t / sigma_samples) ** 2) kernel /= kernel.max() # 峰值归一,避免边界削顶 for p in picks: lo, hi = p - half, p + half + 1 # 窗口边界处要裁剪核,否则会越界 k_lo = max(0, lo) k_hi = min(length, hi) label[k_lo:k_hi] = np.maximum( label[k_lo:k_hi], amp * kernel[k_lo - lo: k_hi - lo] ) return label这段逻辑里最值得琢磨的是sigma_samples。它直接决定了模型时间定位精度的上限:标签模糊,输出就模糊。设得太宽,检出的到时会在真值附近摊开几百毫秒,做走时反演时误差会一路传下去;设得太窄,又退回到硬标签的梯度稀疏问题。经验上,检测头的核宽可以比拾取头大一些——检测只需要圈出「事件大概在哪」,拾取才要求「精确到几十毫秒」,两个任务对时间粒度的需求本来就不同。另外np.maximum那一步不能省,两个事件靠得很近时标签会重叠,取最大值而不是相加,避免出现大于 1 的非法概率目标。
标签的另一个坑来自「负样本的定义」。整窗都是背景噪声,标签全 0,这没问题;但如果窗口里含有一个事件的尾波却没有清晰的 P 起点(比如事件其实在窗口起点之前就发生了),标签也给全 0,模型就会把「尾波」学成「噪声」。这类污染样本在数据里占比不高但杀伤力大,常见的做法是按事件目录做一次时间重叠检查,把跨越窗口边界的残缺事件整窗丢弃,而不是硬着头皮标负。
3. 编码-解码的骨架:卷积、Transformer 与 BiLSTM 各自负责哪段时间
3.1 卷积编码器:先把采样率降下来,注意力才有得算
为什么不在 6000 个采样点上直接做自注意力?算一下就清楚了:6000 的平方是三万六千的量级乘一千,也就是三千六百万个注意力权重,再乘上头数和层数,任何单卡都撑不住。所以骨架的第一步必然是降采样,而卷积是干这活最合适的工具。
典型的编码器由若干「卷积-批归一化-激活-残差」块串联,每个块里做两次一维卷积,块与块之间用步长或池化做 2 倍下采样。堆叠五次之后,序列长度从 6000 降到约 187,通道数从 3 涨到上百。这一步的收益是双份的:注意力成本降了一千倍,同时卷积本身的局部感受野天然匹配波形的短时形态——初动的陡度、尾波的指数衰减、短周期噪声的振荡,这些特征都是几十个采样点尺度上的,正好落在卷积核的窗口里。
还有一个容易被忽略但极其重要的性质:卷积对时移近似等变。事件在窗口里提前 0.5 秒到达,特征图整体平移 0.5 秒,其他结构不变。这正是后面能把输出定位回具体采样点的前提。如果换成全连接网络,时间信息在展平的那一刻就丢了,模型只能学到「这个窗里有事件」,学不到「在第几秒」。
卷积块数不是越多越好。下采样五次到 187 步,已经能覆盖 60 秒里的主要结构;再往下压到几十步,一个时间步对应一秒多物理时间,相位拾取的精度就彻底没了。这里的取舍很清晰:编码器的压缩程度必须与任务要求的时间分辨率匹配,检测任务宽容,拾取任务苛刻,两个任务共享骨架,就必须向苛刻的那一端看齐。
3.2 Transformer 自注意力:解决卷积看不到的全局依赖
在 187 步的序列上做多头自注意力,每个时间步都能以 O(1) 的路径长度直接访问其他所有步。这一点和循环结构有本质区别:循环网络里,第 1 步的信息要传到第 187 步,得穿过一百多次状态更新,梯度衰减和遗忘几乎不可避免;注意力里任意两步之间只隔一次加权。
地震判读的证据恰恰经常横跨很远的时间距离。最典型的一条:判断某处是不是 P 波,除了看它本身的初动方向,还要看它后面若干秒有没有对应的 S 波、以及两者的时差是否落在合理震中距范围内。卷积只能一层层间接扩大感受野,堆到能覆盖十几秒物理时间的时候,参数和计算量都不划算。注意力一步到位。
位置编码在这类模型里不能省。注意力本身是置换不变的——打乱时间步顺序,输出的集合不变——而时间顺序在这里是百分之百的物理信息。没有位置编码,模型无法区分「P 在前 S 在后」和「S 在前 P 在后」,而后者在物理上意味着完全不同的震源机制或者干脆是误检。
注意力权重矩阵本身是个可观测的黑匣子。把某一层的注意力可视化出来,会看到 P 波附近的时间步普遍把权重挂在 S 波位置上,这就是模型学到的 P-S 关联。这个观察在调试时很好用:如果注意力图看起来像均匀噪声,多半是训练不充分或者输入标准化出了问题;如果注意力全都挤在窗口首尾,那要怀疑是不是窗口边界效应在主导。
3.3 BiLSTM 与解码器:全局上下文怎么回到采样点
Transformer 之后接一层双向 LSTM,这一步经常被当成冗余——既然注意力已经全局了,还要循环结构干什么。实际作用有两个。
第一是归纳偏置。注意力没有顺序感,在强噪声段容易「乱连」,把一个远处的噪声峰当成当前时间步的关键证据。LSTM 的递归结构强制信息沿着时间轴一步步流动,天然带平滑约束,能压住这类跳跃式误判。
第二是短序列上的稳定性。187 步不长,Transformer 层数堆不深,单靠注意力对局部时序模式的刻画不如循环结构扎实。双向意味着每个位置都能同时看到过去和未来的上下文——对到时判断来说这非常关键,人眼判读时也是先看前后文再定点的。
# 结构示意,非可运行代码,重点看张量形状怎么变 x = input_tensor # (batch, 6000, 3) # --- 编码器:5 次 2 倍下采样 --- for ch in [8, 16, 32, 64, 128]: # 通道数量级示意 x = residual_conv_block(x, ch) # 两次 1D 卷积 + BN + ReLU + 残差 skips.append(x) # 存起来给解码器做跳跃连接 x = maxpool1d(x, 2) # 长度 6000 -> 3000 -> ... -> 187 # --- Transformer:在低采样率序列上做全局建模 --- x = x + positional_encoding(x) # 位置编码不可省 for _ in range(num_layers): # 层数量级:个位数 x = x + multi_head_self_attention(layer_norm(x)) x = x + feed_forward(layer_norm(x)) # --- BiLSTM:时序归纳偏置 + 双向上下文 --- x, _ = bidirectional_lstm(x) # 形状不变,(batch, 187, hidden) # --- 解码器:逐级上采样 + 跳跃连接 --- for skip in reversed(skips): x = upsample1d(x, 2) # 187 -> ... -> 6000 x = concat([x, skip], axis=-1) # 把高频细节接回来 x = residual_conv_block(x, skip.shape[-1]) # --- 三个输出头,各自映射回 6000 长度 --- det = sigmoid(conv1d(x, 1)) # 检测概率 p = sigmoid(conv1d(x, 1)) # P 到时概率 s = sigmoid(conv1d(x, 1)) # S 到时概率解码器里最关键的一行是concat([x, skip])。上采样本质是插值,插出来的高频细节是猜的,而编码器对应层里存着原始分辨率下的卷积特征——初动的陡峭边缘、波峰的精确位置。把这些跳接过来,输出才能保住采样点级的锐度。去掉跳跃连接的模型通常也能训到收敛,检测概率曲线看着还挺漂亮,但 P 和 S 的峰值会明显糊开,到时误差从几十毫秒退化到一百毫秒以上。这个差距在检测指标上几乎看不出来,只有做走时定位的时候才会翻车。
上采样的方式也有讲究。转置卷积学得出更好的细节,但容易产生棋盘状伪影;线性插值加卷积更平滑、参数更少。常见的实现思路是混用——浅层用转置卷积,深层用插值,代价是实现复杂度上升。
4. 多任务输出头:一次前向为什么能同时给出检测与 P/S 到时
4.1 三个头共享躯干:多任务在这里是一种正则化
三个输出头结构上完全一样:一层把通道压到 1 的卷积,接 sigmoid,输出长度 6000 的概率曲线。区别只在监督信号——检测头学「这附近有没有事件」,P 头学「P 波精确在哪一个采样点」,S 头同理。三者共享前面那一大坨编码器、Transformer、LSTM 和解码器。
共享带来的收益不是简单的「省一次前向」。检测任务的样本密度远高于拾取任务:一个 60 秒窗里只要有一个事件,检测标签就有一个可学的峰;而 P 和 S 各只有一个点,正样本稀疏得多。如果三个任务各训一个独立网络,检测网络很容易把特征学得很粗——它只需要分辨「有能量」和「没能量」,不需要保留高频细节。共享之后,拾取头对时间精度的苛刻要求会反向约束骨干网络,逼它保留那些对定位有用的细粒度特征。反过来,检测头提供的大样本信号帮助骨干网络在前面几层学到更稳健的能量表征。这是一种双向的软约束,效果类似数据增强。
还有一个更隐蔽的好处:一致性。三条曲线来自同一副参数、同一次前向,检测峰和拾取峰在时间上天然对齐(前提是标签生成时用的是同一套到时)。如果拆成三个模型分别推理,可能出现「检测说有事件但 P 头在同一位置没响应」这种自相矛盾的输出,做配对时得额外处理。
4.2 损失权重与类别极不平衡
正样本占比在千分之一量级,这个数字决定了损失函数必须做加权。加权有两个维度。
一是正负样本之间。二元交叉熵在极不平衡数据上会被负样本主导,模型很快滑向「全预测 0」。除了前面说的标签高斯平滑,另一个手段是给正样本更高的损失权重,或者改用对易分负样本降权的损失形式。前者简单但会放大标签噪声,后者更稳但要调一个额外的聚焦参数。
二是三个任务之间。检测任务相对容易,正样本密集且模式明显;P 拾取难在中段,S 拾取最难——S 波的能量经常被 P 波尾波掩盖,水平分量上的初动也不如 Z 分量上的 P 波清晰。实践中 S 的到时误差普遍大于 P,这不是模型 bug,是物理决定的。既然难度不同,损失权重就不该平均分配,通常给 P 和 S 更大的权重,让梯度更多地流向难任务。
注意:损失权重是训练期的超参数,推理阶段完全不生效。拿别人的预训练权重做推理时,不需要关心权重怎么设;但一旦要微调,权重设错会让本来已经学会的拾取能力退化。微调时先冻结骨干、只训输出头,是更安全的做法。
4.3 从概率到到时:峰值、阈值与配对
概率曲线本身不是结果,得还原成离散的到时。这一步的规则简单,但每个参数都是一个漏检/误检的开关。
def pick_from_prob(det, p_prob, s_prob, sr=100, det_th=0.3, pick_th=0.3, max_ps_gap=15.0): """从三条概率曲线还原成 (P 到时, S 到时) 列表。 det_th : 检测阈值,低于此值不认为窗口内有事件 pick_th : 拾取阈值,P/S 峰必须高于此值 max_ps_gap: P 与 S 的最大允许时差(秒),对应最大震中距 """ if det.max() < det_th: return [] # 整窗无事件,直接返回 # 只保留超过阈值的局部极大值,避免一个宽峰被拆成好几个 p_idx = local_maxima(p_prob, height=pick_th) s_idx = local_maxima(s_prob, height=pick_th) pairs = [] for pi in p_idx: # 找该 P 之后、且在时差上限内的最近 S cand = [si for si in s_idx if 0 < (si - pi) / sr <= max_ps_gap] if cand: si = min(cand, key=lambda x: x - pi) pairs.append((pi / sr, si / sr)) return pairs三个参数各有各的失效模式。det_th调低,背景噪声段开始冒出成串的假事件,检测指标看着召回率很高,实际全是垃圾;调高,低信噪比台站的真实事件被整窗丢弃,而且丢得很彻底——一旦整窗被判无事件,后面 P/S 曲线再漂亮也没用。pick_th的作用类似,但它管的是单点质量而非整窗,调低会出现「一个宽峰被拆成几个拾取」的情况,需要local_maxima的最小间距参数兜底。
max_ps_gap是最容易被忽视的一个。它表面上是个物理约束(震中距决定 S-P 时差的上下限),实际上是个强力的误检过滤器:把每个 P 强制配一个合法的 S,配不上的 P 就被丢弃。在连续噪声上,P 概率经常会零星地超过阈值,单独看是一堆假拾取,配上配对约束之后大部分会被自动清掉。参数设得太大,假配对增多,S 的到时严重偏离真值;设得太小,远震的合法 P-S 对被拆散,召回率掉得莫名其妙。
5. 推理链路排查:滑窗、阈值与峰值配对的常见翻车点
5.1 现象:文件首尾几秒永远检不到事件
这是滑窗推理最经典的血泪经验。一段几十分钟的连续波形按 60 秒切窗、窗间留少量重叠,落在文件最前面和最后面的那部分,凑不满一个完整窗口,常见的处理是补零或者直接丢弃。补零的话,模型看到的是「一大段死寂突然接上真实波形」,这个突变本身会被当成一个强初动,同时补零段把窗口内的真实 P 波挤到了边缘位置,注意力被边界噪声吸走;直接丢弃的话,那几秒就永远没有输出。
原因在于模型的视野是写死的:它只在完整 60 秒、两端都有上下文的条件下训练过,从没见过被硬截断的输入。解决办法是把连续波形两端各延长半个窗长再做滑窗,延长部分用镜像或者用相邻时段的数据填充,然后把首尾补出来的部分从输出里裁掉。这样每个真实采样点都至少出现在一次「上下文完整」的推理中。
5.2 现象:同一个事件在输出里出现三四次
连续波形滑窗、窗间有重叠,同一个事件必然出现在多个窗口里,每个窗口都会给出一个 P/S 到时,轻微的时间偏移导致最后拿到四组几乎一样的解。不去重的话,后续定位环节会把一个事件当成四个,走时残差全乱。
原因很清楚,但处理起来有细节。不能简单地按时间聚类取平均——不同窗口给出的到时可靠性不一样,靠近窗口中心的拾取通常比靠近边界的准。常见的思路是按「距离窗口中心的远近」加权,配合一个合并半径(比如 0.5 秒),把半径内的拾取合成一个。合并半径设太小,去重不干净;设太大,真正相邻的两个小事件会被错误合并成一个,震群序列里这种错误尤其致命。
5.3 现象:换成高采样率台站后整批漏检
同一批数据,换个台网就全废了。查半天模型没坏、权重没坏、阈值也没改,问题出在采样率上。模型是在固定采样率下训练的,输入序列长度对应固定的物理时长。200 Hz 的数据不重采样直接喂进去,模型看到的「60 秒窗」实际只有 30 秒物理时间,P 和 S 挤在一起,S 甚至落在窗口外,检测头勉强还有响应,拾取头全崩。
解决方式是在预处理阶段强制统一采样率,并记录原始采样率用于把输出索引换算回真实时间。这里有个隐蔽的坑:抗混叠滤波必须在降采样之前做,直接抽点会把高频噪声折叠到低频段,正好落在 P 波的主频带里,比不重采样还糟。
5.4 现象:连续噪声段上冒出一长串 P 波拾取
一整天的背景噪声,输出里密密麻麻几百个 P 到时,检测概率曲线看着还挺高。第一反应是阈值太低,调高之后数量降下来但真实事件也跟着少了。
真正的原因往往不在阈值,而在配对约束缺失或者过于宽松。纯噪声段上,P 概率会随机地小幅超过阈值,这些孤立的峰没有任何 S 与之对应——因为没有真实事件。一旦强制「每个 P 必须配一个合法 S」,绝大多数假拾取自动消失。所以调参的顺序应该是先调配对窗口,再调阈值:配对窗口能干掉的是结构性误检,阈值只能干掉强度不够的误检,两者的作用层次不一样。反过来,如果配对窗口设得过宽,噪声段上两个随机峰也能凑成一对,那这个过滤器就失效了。
5.5 现象:同一批数据换台站后概率曲线整体偏低
模型没改、阈值没改,换一批台站数据后所有概率值都压得很低,勉强过阈值的寥寥无几。八成是幅值标准化没做或者做错了。原始波形单位是仪器 counts,不同台站的增益差几个数量级,不做逐窗标准化,网络第一层的激活值分布就完全偏离训练时的分布,后面所有层的输出都跟着偏移。
排查方法很直接:随便挑几个窗口,打印标准化前后的均值和标准差。正常应该是零均值、单位方差;如果均值偏离很远或者标准差是个很小的数(说明这一段几乎是死信号,除标准差相当于把噪声放大成满量程),那输出异常就有了合理解释。还有一种情况是直流偏置没去掉,某些台站的记录带一个恒定的基线偏移,减均值那一步必须做在整窗上,分段做会留下台阶。
提示:上面这五条排查路径有先后顺序。先查采样率和幅值(输入层),再查滑窗边界(推理框架层),最后才动阈值和配对参数(决策层)。顺序反了,会在错误的层次上反复调参,越调越乱。
6. 设计取舍:全局注意力换来的精度与代价
把 CNN、Transformer、BiLSTM 和 U-Net 式解码器缝在一起,看起来像是把能用的模块都堆了一遍,但每一块的存在都有明确的、不可替代的理由,同时也都带来了对应的代价。
最核心的取舍在注意力这一层。用自注意力换来了卷积拿不到的全局依赖,代价是平方级的计算复杂度和对位置编码的强依赖。这也是为什么它必须放在降采样之后——如果坚持在全采样率上做注意力,精度或许还能再涨一点,但显存和推理时间会涨到没法在批量处理里用。这个取舍的边界很清楚:注意力负责「在哪里」的语义关联,卷积负责「是什么形态」的局部特征,两者不能用其中一个替代另一个。纯卷积模型在低信噪比台站上的漏检率明显更高,而纯注意力模型在小样本上根本训不收敛。
第二个取舍是多任务共享。三个头共享骨干省了参数、提供了正则化、保证了输出一致性,代价是任务之间的相互牵制。检测任务希望特征粗一点、抗噪一点,拾取任务希望特征细一点、保留高频。共享意味着必须找一个折中点,而这个折中点对不同的数据分布并不相同。实践中见过的一种做法是给解码器分成两段:浅层共享,深层分开,让任务在靠近输出的地方各自特化。这比完全共享灵活,但参数量上去了,小数据集上更容易过拟合。
第三个取舍是输出形式本身。逐采样点概率比「事件列表」信息量大得多——保留了不确定性、保留了次峰、可以事后用不同阈值重新决策。代价是必须额外写一套从概率到事件的还原逻辑,而这一层逻辑里的每个参数都是新的失效点。这也是为什么同一份权重在不同人手里效果能差出一截:模型是死的,还原规则是活的。
验证自己的部署有没有跑偏,我一般会做三件事。一是拿一段人工合成的、P 和 S 位置完全已知的波形过一遍全流程,看输出的峰是否落在预期位置、偏差是否在几十毫秒内——这一步排除的是预处理和索引换算错误。二是把连续波形喂进去,统计单位时间内的拾取数量,正常台站应该是一个稳定的低值,如果冒出成百上千,多半是配对约束没生效。三是画几张注意力热图,看 P 波位置是否稳定关注到 S 波区域,这是模型真的学到了物理关系、还是在拟合数据偏置的直接证据。
最后一条是花了不少时间才换来的教训。早期我习惯先看检测指标——召回率、精确率都挺好看,就认为部署没问题了。直到做走时反演时残差大得离谱,回头去查才发现拾取头的到时整体偏了半个波长,而检测指标对此毫无感知,因为检测只看「有没有峰」不看「峰在哪」。从那以后我每次接新数据,都强制走一遍合成波形验证,先在已知答案上把整条链路的偏差量出来,再去碰真实数据。希望帮到你。
本文还有配套的精品资源,点击获取