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 数据集可能很大,应优先读取所需通道与时间段,而不是整文件载入。

水下数据读取链路与项目目录 IMU 姿态记录 CSV · 时间戳/横滚/俯仰 航向/深度 · 约 100 Hz 水听器阵列 HDF5 · acoustic/ch0 采样率 48000 Hz pandas.read_csv parse_dates 解析时间 sort_index 防乱序 h5py.File 读取 visititems 探查结构 按通道/时段取数据 统一时间基准 DataFrame 时间索引 NumPy 数组 + 采样时间轴 供后续对齐与融合 项目目录(可复用) usfusion/ ├─ data/raw(原始只读) ├─ data/processed(清洗后) ├─ src/(读取与融合脚本) ├─ notebooks/(探索分析) ├─ configs/(采样率、通道映射) ├─ outputs/figures(图) └─ tests/(读取校验)

三、可视化工具与快速检查

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 采样率大于零。这样每次换数据都能快速发现格式问题,而不是等到融合阶段才暴露。

建议把原始数据目录设为只读,所有清洗结果写入 processed。这样任何一次处理都可从原始数据重放,避免“改坏了原始文件却无法回溯”。

完成以上四步后,环境即具备继续做时间对齐、滤波与多传感器融合的基础。判断搭建是否成功的标准很直接:能在新终端激活环境、成功读入一份 CSV 与一份 HDF5、生成一张检查图,并且目录中原始数据未被修改。