简介:这份资源面向地震勘探与地球物理领域的学习者和工程技术人员,聚焦面波数据处理与速度剖面连接这一关键环节,提供CCSWSWIN与CCSWSMAP两款软件的配套工具与说明文档,可用于浅部地壳结构分析、速度分层建模及多测线剖面拼接等实际场景。压缩包共91个文件,约1.8MB,以exe可执行程序、htm帮助页面、gif与bmp界面图示为主,辅以doc使用说明、sys与vxd驱动文件及少量c、h源码,兼顾软件运行、加密狗驱动安装与操作指引。资源已有923人学习下载,说明其在同类工具中具备一定参考价值。读者可从中获得面波预处理、相位解缠、频散分析到速度模型构建与剖面连接的完整软件环境,配合图文帮助快速理解参数设置与结果解读,适合初学者入门及技术人员对照实践。
1. 面波处理及剖面连接软件:一份能直接跑起来的地震面波工程工具
第一次拿到这个资源包时,我正被一条测线的面波数据卡住——频散曲线提取出来是散的,相邻两个排列的剖面接不上,手工在表格里对坐标对到凌晨。这个软件解决的正是这类问题:把面波数据的整理、频散分析、剖面拼接串成一条可复现的流程,而不是散落在若干脚本和 Excel 里。它适合做浅层地震勘探、岩土波速测试、场地评价的工程人员,也适合想二次开发面波处理链路的开发者。下面按「能干什么、怎么跑、怎么改」拆开讲,重点放在目录结构、核心处理模块和剖面连接的具体实现上,读完你应该能自己把数据喂进去、把结果导出来。
2. 工程结构与运行环境:从目录到第一条频散曲线
2.1 目录布局与模块职责
这类面波处理软件通常按「数据层—算法层—界面层—输出层」四块组织,拿到包先别急着点运行,把目录扫一遍能省掉后面一半的排查时间。典型结构如下:
project/ ├── main.py # 程序入口,负责参数解析与流程调度 ├── config/ │ └── default.yaml # 采样率、道间距、频散参数等默认配置 ├── data/ │ ├── raw/ # 原始 SEG-2 / SAC / txt 记录 │ └── processed/ # 预处理后的中间结果 ├── core/ │ ├── io_reader.py # 多格式地震记录读取 │ ├── preprocess.py # 去直流、滤波、道均衡 │ ├── dispersion.py # 频散曲线提取(f-k / 相移法) │ ├── inversion.py # 层状模型反演(可选) │ └── profile_join.py # 剖面连接与坐标对齐 ├── ui/ │ └── main_window.py # 图形界面(若为 GUI 版本) ├── output/ │ └── reports/ # 频散图、剖面图、文本结果 └── requirements.txtcore是真正要读的部分,ui只是壳。我一般先看io_reader.py支持哪些格式,再看dispersion.py用的是哪种频散提取方法,最后看profile_join.py怎么处理相邻剖面的重叠段——这三处决定了软件能不能接你的实际数据。
2.2 环境准备与依赖安装
面波处理绕不开数值计算和绘图,依赖基本集中在科学计算栈。建议用虚拟环境隔离,避免和系统里的旧版 numpy 打架:
python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install -r requirements.txtrequirements.txt里通常包含numpy、scipy(滤波与信号处理)、matplotlib(频散图与剖面图)、pyyaml(读配置),如果带 GUI 还会有pyqt5或tkinter相关项。装完先跑一次导入自检,确认没有版本冲突:
import numpy, scipy, matplotlib, yaml print(numpy.__version__, scipy.__version__)版本对不上时,最常见的是 scipy 的滤波接口签名变化,后面避坑章节会细说。
2.3 配置文件与第一次运行
config/default.yaml是整条流程的「黑匣子」入口,采样率、道间距、偏移距、频散扫描范围都在这里。先按你的采集参数改,再运行:
acquisition: sample_rate: 1000 # 采样率 Hz dt: 0.001 # 采样间隔 s channel_spacing: 2.0 # 道间距 m offset: 5.0 # 最小偏移距 m dispersion: fmin: 2.0 # 扫描最低频率 Hz fmax: 50.0 # 扫描最高频率 Hz velocity_min: 50.0 # 相速度下限 m/s velocity_max: 800.0 # 相速度上限 m/s改完执行入口:
python main.py --config config/default.yaml --input data/raw/line01.dat程序会依次走读取、预处理、频散提取,结果落到output/reports/。第一次跑建议拿包里自带的示例数据,确认链路通了再换自己的数据,否则读文件和算法问题混在一起,排查会很痛苦。
3. 核心处理链路拆解:读取、预处理与频散提取
3.1 地震记录读取与道头解析
面波数据的麻烦在于格式杂,SEG-2、SAC、纯文本都可能遇到。io_reader.py一般用统一接口封装,按扩展名分发:
import numpy as np def read_segy_like(path): """读取简单二进制/文本记录,返回 data(道×样点) 与道头信息""" if path.endswith(".txt"): data = np.loadtxt(path) # 约定:第一列为时间,其余列为各道振幅 traces = data[:, 1:].T dt = data[1, 0] - data[0, 0] else: # 二进制格式按项目约定解析,此处给出占位结构 raw = np.fromfile(path, dtype=np.float32) n_channels = 24 traces = raw.reshape(n_channels, -1) dt = 0.001 return traces, {"dt": dt, "n_channels": traces.shape[0]}这里的关键是traces的维度约定为「道 × 样点」,后续所有算法都按这个方向处理。dt从数据本身或配置读取,千万别两处各写一个值——我见过因为道头 dt 和配置 dt 不一致导致频散轴整体偏移的案例,图看着正常,速度全错。
3.2 预处理:去直流、带通滤波与道均衡
原始记录里常有直流偏置和低频漂移,直接做频散会污染低频段。预处理按「去均值 → 带通 → 道均衡」顺序走:
from scipy.signal import butter, filtfilt def preprocess(traces, dt, fmin=2.0, fmax=100.0): # 1) 去直流 traces = traces - traces.mean(axis=1, keepdims=True) # 2) 零相位带通,避免相位畸变影响频散 nyq = 0.5 / dt b, a = butter(4, [fmin/nyq, fmax/nyq], btype="band") traces = filtfilt(b, a, traces, axis=1) # 3) 道均衡:按各道最大振幅归一,压制强能量道 max_amp = np.abs(traces).max(axis=1, keepdims=True) traces = traces / (max_amp + 1e-12) return tracesfiltfilt是零相位滤波,正反各滤一次,相位不偏移,这对频散提取很重要;butter的阶数 4 是经验值,阶数太高容易在截止频率附近振铃。道均衡那一步用1e-12防除零,别省。
3.3 频散曲线提取:f-k 变换与峰值拾取
频散提取是这类软件的核心。常见做法是频率—波数(f-k)变换或相移法,这里给 f-k 的简化实现:
def fk_dispersion(traces, dt, dx): n_ch, n_samp = traces.shape # 二维 FFT:时间轴 -> 频率,空间轴 -> 波数 fk = np.fft.fftshift(np.fft.fft2(traces), axes=1) freqs = np.fft.fftfreq(n_samp, d=dt) k = np.fft.fftshift(np.fft.fftfreq(n_ch, d=dx)) # 相速度 = 频率 / 波数,逐频取能量峰值对应的速度 amp = np.abs(fk) curve = [] for i, f in enumerate(freqs[:n_samp//2]): if f <= 0: continue idx = np.argmax(amp[i, :]) k_peak = k[idx] if abs(k_peak) > 1e-9: curve.append((f, f / abs(k_peak))) return np.array(curve)fft2同时变换时间和空间,fftshift把零波数移到中间便于取峰。逐频取argmax是最朴素的峰值拾取,实际数据里会有多模态,工程上一般加能量阈值和连续性约束,避免跳到高阶模态。dx是道间距,单位要和速度单位一致,米和秒配米每秒,别混。
4. 剖面连接模块:相邻测线的坐标对齐与拼接
4.1 剖面连接要解决什么问题
单条测线处理完只是半成品,工程上往往要把多条相邻剖面接成一张连续图。难点不在画图,在「对齐」:两条测线的起止桩号、道间距、高程基准可能都不一致,直接首尾相接会出现错位或重叠段重复计算。profile_join.py的职责就是把这些差异抹平。
4.2 坐标系统一与重叠段处理
常见做法是先统一到同一桩号坐标系,再按重叠区间做加权融合:
def join_profiles(profiles, overlap_tol=0.5): """profiles: [(station_array, value_array), ...] 按桩号排序""" profiles = sorted(profiles, key=lambda p: p[0][0]) merged_s, merged_v = [], [] for s, v in profiles: if not merged_s: merged_s, merged_v = list(s), list(v) continue last_s = merged_s[-1] # 找重叠段:新剖面起点落在已有剖面范围内 mask = s <= last_s + overlap_tol if mask.any(): # 重叠段取均值,避免接缝处突变 for si, vi in zip(s[mask], v[mask]): idx = min(range(len(merged_s)), key=lambda j: abs(merged_s[j]-si)) merged_v[idx] = 0.5 * (merged_v[idx] + vi) s, v = s[~mask], v[~mask] merged_s.extend(s) merged_v.extend(v) return np.array(merged_s), np.array(merged_v)overlap_tol是重叠判定容差,单位与桩号一致,设太小接缝处会留缝,设太大正常相邻段会被误判为重叠。重叠段取均值是最简单的融合策略,要求高的话可以按距离加权。这段逻辑看着短,但桩号排序、重叠判定、索引对齐三处都容易出错,建议先用两条构造数据验证再接真实测线。
4.3 拼接结果的校验与出图
拼完不能只看图顺眼,要落数值校验:检查桩号是否单调递增、接缝处相邻点差值是否超过阈值、总长度是否等于各段去重后的并集长度。校验通过再出图:
import matplotlib.pyplot as plt def plot_profile(station, value, out_path): plt.figure(figsize=(10, 4)) plt.plot(station, value, "-o", markersize=3) plt.xlabel("Station (m)") plt.ylabel("Phase velocity (m/s)") plt.grid(True, alpha=0.3) plt.tight_layout() plt.savefig(out_path, dpi=150)出图只是最后一步,真正决定质量的是前面的对齐和融合。我一般会把接缝位置在图上用竖线标出来,方便肉眼复核。
5. 避坑与常见问题排查
5.1 频散图整体偏移或速度量级不对
现象:频散曲线形状合理,但相速度整体偏大或偏小一个固定倍数。原因多半是道间距dx或采样间隔dt单位不一致,比如配置里写米、数据里按厘米存。解决:在读入后打印dt和dx实际值,与配置逐项比对,统一到 SI 单位再跑。
5.2 滤波后波形出现振铃或相位畸变
现象:带通滤波后波形两端出现振荡,频散低频段出现虚假能量。原因是用了一阶lfilter或阶数过高,前者有相位延迟,后者在截止频率附近振铃。解决:改用filtfilt零相位滤波,阶数控制在 2~4,截止频率留出过渡带。
5.3 剖面拼接处出现台阶或重复段
现象:两条测线接缝处数值突变,或某段桩号重复出现。原因:桩号未排序、重叠容差设置不当、索引对齐用了最近邻但桩号间隔不均。解决:拼接前强制按桩号排序,打印重叠区间长度人工确认,索引对齐改为插值而非最近邻。
5.4 读取二进制格式时报维度错误
现象:reshape报错或读出的道数明显不对。原因:文件头里的道数、样点数与实际数据长度不匹配,或字节序(大端/小端)不对。解决:先用np.fromfile读原始字节,按文件头字段逐项解析,确认dtype的字节序,再 reshape。
5.5 配置文件改了但程序没生效
现象:修改default.yaml后结果不变。原因:入口脚本里配置路径写死,或代码里对某些参数有硬编码默认值覆盖了配置。解决:在参数加载处打印最终生效的配置字典,确认每一项来源,硬编码项统一收敛到配置读取函数。
6. 二次开发:把处理链路改成自己的批处理工具
这套代码最值得改的地方,是把「单文件手动跑」变成「整条测线批处理」。我的做法是在main.py外面包一层调度脚本,遍历目录下所有原始文件,按配置批量跑,结果统一命名落盘:
import os, glob from core.io_reader import read_segy_like from core.preprocess import preprocess from core.dispersion import fk_dispersion from core.profile_join import join_profiles def batch_process(raw_dir, out_dir, cfg): results = [] for path in sorted(glob.glob(os.path.join(raw_dir, "*.dat"))): traces, meta = read_segy_like(path) traces = preprocess(traces, meta["dt"], cfg["fmin"], cfg["fmax"]) curve = fk_dispersion(traces, meta["dt"], cfg["dx"]) name = os.path.splitext(os.path.basename(path))[0] np.savetxt(os.path.join(out_dir, f"{name}_disp.txt"), curve) results.append((meta["station"], curve[:, 1])) return results改造时有三个点值得注意。第一,meta里要带上桩号信息,否则后面拼接无从对齐;第二,批处理要加异常捕获和日志,单条失败不能中断整批;第三,输出格式固定成「频率—相速度」两列文本,方便后续用其他工具复核。验证方法很简单:拿两条已知重叠的测线跑一遍,检查拼接后重叠段数值是否落在两条原始曲线之间,桩号是否连续。
我踩过最深的一个坑是批处理时没统一dt,不同文件道头精度不同,拼出来的剖面在接缝处速度跳变,查了两天才定位到读取环节。从那以后我每次批处理前都强制打印每个文件的dt、dx、道数三件套,确认一致再往下走。希望帮到你。
本文还有配套的精品资源,点击获取