2. Python水下数据处理环境搭建
水下多传感器融合的第一步不是算法,而是让数据“进得来、看得见、管得住”。本节围绕三件事展开:安装科学计算栈、掌握两类典型传感器数据的读取方式、建立可复用的项目目录。完成之后,你应当能在一个干净环境中读入一份CSV姿态记录和一份HDF5水声/声呐数据,画出时间序列曲线,并把它们放进结构清晰的工程目录中。
一、科学计算栈的安装与验证
推荐使用 conda 创建独立环境,避免与系统 Python 冲突。NumPy 负责数组与矩阵运算,SciPy 提供滤波、插值、信号处理函数,Pandas 处理带时间戳的表格数据,Matplotlib 负责绘图。h5py 是读取 HDF5 的关键库,它不在标准科学栈内,需要单独安装。
conda create -n usfusion python=3.11 -y
conda activate usfusion
conda install numpy scipy pandas matplotlib jupyterlab -y
pip install h5py
安装完成后不要只看“没报错”,应做一次导入与版本验证,确认各库可正常加载:
import numpy, scipy, pandas, matplotlib, h5py
print(numpy.__version__, scipy.__version__)
print(pandas.__version__, matplotlib.__version__, h5py.__version__)
若在无图形界面的服务器上运行,Matplotlib 默认后端可能报错,可在绘图前设置 matplotlib.use("Agg"),把图保存为文件而非弹窗显示。这是水下数据常在远程设备或容器中处理的常见配置。
二、两类传感器数据的读取
水下传感器数据大致分两类:一类是低频、结构化的表格记录,例如姿态传感器(IMU)输出的时间戳、横滚、俯仰、航向、深度,通常以 CSV 保存;另一类是高频、多维的二进制数据,例如水听器阵列采样、声呐图像、多波束测深,常以 HDF5 保存。两者读取方式不同,但都应统一转换为带时间索引的 DataFrame 或 NumPy 数组,便于后续对齐与融合。
读取 CSV 时,关键是显式指定时间列并解析为时间索引,而不是让 Pandas 猜:
import pandas as pd
imu = pd.read_csv("data/raw/imu_20240601.csv",
parse_dates=["timestamp"],
index_col="timestamp")
imu = imu.sort_index()
print(imu.head())
print(imu.index.inferred_freq)
示例中假设 CSV 含 timestamp, roll, pitch, yaw, depth 五列,时间戳为 ISO 格式。排序可防止设备重启导致的乱序;inferred_freq 用于快速判断采样是否均匀,若返回 None,说明存在丢帧或抖动,后续融合前需要重采样。
读取 HDF5 时,先探查结构再取数据,避免盲目索引:
import h5py
with h5py.File("data/raw/hydrophone_20240601.h5", "r") as f:
def show(name, obj):
if isinstance(obj, h5py.Dataset):
print(name, obj.shape, obj.dtype)
f.visititems(show)
fs = f.attrs["sample_rate"]
sig = f["acoustic/ch0"][:]
示例假设文件根属性含 sample_rate(如 48000 Hz),数据集中 acoustic/ch0 为一维采样序列。读取后可用 np.arange(len(sig)) / fs 构造时间轴,与 IMU 的时间索引建立对应关系。注意 HDF5 数据集可能很大,应优先读取所需通道与时间段,而不是整文件载入。
三、可视化工具与快速检查
Matplotlib 是基础绘图工具,适合画姿态时间序列、深度曲线和频谱。读取数据后先画一张“体检图”,比直接进入融合更可靠。例如把 IMU 的深度与横滚画在同一时间轴上,观察是否存在阶跃、饱和或长时间不变(传感器卡死)的区段:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig, ax = plt.subplots(2, 1, figsize=(10, 6), sharex=True)
ax[0].plot(imu.index, imu["depth"], color="#2b7a9e")
ax[0].set_ylabel("深度 / m")
ax[1].plot(imu.index, imu["roll"], color="#c9772b")
ax[1].set_ylabel("横滚 / deg")
fig.tight_layout()
fig.savefig("outputs/figures/imu_check.png", dpi=150)
示例输出为一张两行共享时间轴的 PNG。判断依据:深度曲线应连续变化,若出现水平直线段且持续数秒,需怀疑丢帧或量程饱和;横滚若频繁触及 ±180° 附近,可能是角度回绕,融合前应做解缠。对水听器信号,可先画 0.1 秒短时波形并做 FFT,确认采样率与通道映射正确,再进入后续处理。
四、可复用项目目录与配置
目录结构的目标是让“原始数据只读、处理结果可追溯、脚本与配置分离”。建议如下组织:
usfusion/
├── data/
│ ├── raw/ # 原始 CSV、HDF5,只读不改
│ └── processed/ # 清洗、重采样后的中间结果
├── src/
│ ├── io_readers.py # read_imu_csv / read_h5_channel
│ └── preprocess.py
├── configs/
│ └── sensors.yaml # 采样率、通道映射、时间列名
├── notebooks/ # 探索性分析
├── outputs/figures/ # 图表输出
└── tests/ # 读取与字段校验
把易变的参数放进配置文件,而不是散落在脚本里。例如 sensors.yaml 可记录 IMU 时间列名、HDF5 数据集路径与采样率。读取函数从配置取值,换一批数据时只改配置不改代码。再配一个最小校验:读取后断言时间索引单调递增、深度列无全空、HDF5 采样率大于零。这样每次换数据都能快速发现格式问题,而不是等到融合阶段才暴露。
完成以上四步后,环境即具备继续做时间对齐、滤波与多传感器融合的基础。判断搭建是否成功的标准很直接:能在新终端激活环境、成功读入一份 CSV 与一份 HDF5、生成一张检查图,并且目录中原始数据未被修改。