ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ADCP数据3D图:用Python绘制流速大小与流向的立体曲面

ADCP数据3D图:用Python绘制流速大小与流向的立体曲面 简介这是一份面向水文水动力领域ADCP数据后处理的MATLAB三维可视化资源。针对声学多普勒流速剖面仪采集的多个断面数据提供从VMT软件摘要文件中提取、整理到绘制3D图形的完整流程适合需要将断面流速信息立体呈现的水文研究人员、工程师及高年级相关专业学生。资源共58个文件压缩包约20.66MB主体包括29个xlsx断面测量数据表、22个png三维效果示例图、1个MATLAB绘图脚本、2个docx操作说明和2个txt说明文档目录按原始摘要、提取文件、成图输出分工明确便于对照学习。项目覆盖14个测线断面运行脚本可自动读取数据并绘制含全部信息的3D图配合MATLAB的旋转3D功能可自由切换法向视角、XY、XZ、YZ等观察角度。目前该资源已有269人学习适合积累ADCP数据后处理和可视化经验并可直接改造用于自己的水文项目。 做海洋或河流连续站观测的人多半都有这个体验ADCP网口数据拿回来一列是时间一列是深度还有北向流速、东向流速、垂直流速、回波强度……信息很全但常规的2D云图只能显示两个维度流向要么压成几十个箭头要么干脆不画。于是很多人都动过同一个念头——能不能做一张“包含所有信息的ADCP数据的3D图”把时间、深度、流速大小、流向甚至回波强度一次摆进同一张图里。这篇文章就以“ADCP数据3D图”为主题聊聊我是怎么设计坐标、处理数据、用Python把它画出来的适合刚接触ADCP后处理的人也适合已经画烦了2D图、想换一种信息表达方式的同行参考。1. 先想清楚ADCP数据里到底藏了哪些信息1.1 一束声波带回的数据结构ADCP的全称是声学多普勒流速剖面仪工作时向水层发射声脉冲利用水中悬浮颗粒的散射信号通过多普勒频移反演出不同深度层的流速。一个连续站位的ADCP一天下来可以获得上千个时间剖面每个剖面又有几十个深度层每个格点都对应东向流速u、北向流速v、垂直流速w、误差速度、回波强度等一堆物理量。从数据结构上看这其实是一个三维时空场时间是一个坐标深度是一个坐标物理量本身又是第三维。而一旦涉及流向就需要在同一张图里同时表达“大小”和“方向”信息维度进一步上升。所谓“包含所有信息”并不是字面意义上把所有物理量都塞进去而是把最核心的时空演变和流速矢量表达完整。1.2 传统2D绘图的局限常规画法里最常用的是时间-深度云图用颜色表示流速大小。这种图看流速强弱很直观但流向完全丢失实际的潮流往复、水体剪切层位置都看不出来。另一种常见画法是某一时刻的流速剖面图横轴是流速纵轴是深度能够看清单条剖面的流速细节但无法呈现连续时间的变化。还有人尝试在时间-深度图上叠加箭头表示流向箭头一密就糊成一团能量和方向混在一起读图反而更累。3D图的优势在于多出一个物理轴可以把“流速大小”作为曲面的高度同时用颜色表达流向这样信息密度高很多也方便观察峰值流速的时间位置和深度位置。2. 3D图的坐标系怎么定才能“一图全包”2.1 哪些信息必须上动手画图之前先拆需求。一次连续站观测最需要看的是四件事流速大小的时间变化、流速大小随深度的分布、流向往复的规律、以及异常层的位置比如强剪切层或底边界层。按优先级排下来速度大小必须用高度或者颜色表现流向最好用颜色或者箭头表现时间和深度则固定作为两个轴。回波强度这类辅助量我的做法是放到第二张图里做对照而不是强行叠进同一张3D图。因为信息量太大时可读性会直线下降一张图里塞五个维度最后谁的眼睛都救不回来。2.2 坐标分配和颜色编码我采用的坐标方案是X轴放时间Y轴放深度海面方向取负深度让水面在图上侧Z轴放流速大小。这样曲面越高表示流速越强时间和深度的分布一眼就能看懂。流向怎么处理有两个方案。第一个方案是给曲面贴上HSV颜色色相表示流向角度0度表示北90度表示东亮度表示流速大小。这样一张图里同时表达了方向、大小、时间和深度。第二个方案更传统曲面颜色用viridis表示流速大小再在关键位置叠加少量流向箭头。我实际推荐先做HSV版本因为流向箭头在3D空间里画不好就会挡住曲面还容易给人错误的方向暗示。HSV版本对新手更友好颜色环解释起来也不复杂。3. 从netCDF到图数据处理和Python画图实操3.1 数据预处理不能省拿到原始ADCP数据之后不要急着画图。我的习惯是先用后处理工具把波束坐标转换成地球坐标确保u和v是真正的东向和北向分量。这一步很关键因为仪器默认输出的坐标系和船体朝向有关如果中间没有做姿态校正画出来的流向会整体偏一个角度。质量控制也必须在画图前做。ADCP在表层盲区、底边界旁瓣区经常出现明显错误的数据相关性correlation过低、信噪比过低的bin都要设为NaN。处理完之后把数据写成规矩的NetCDF文件变量名清晰后续画图就很省事。3.2 完整的3D曲面代码下面这份代码以处理好的NetCDF数据为例画一张带HSV流向着色的3D速度曲面图。需要安装的库是xarray、numpy和matplotlib安装命令如下pip install xarray netcdf4 matplotlib numpyimport xarray as xr import numpy as np import matplotlib import matplotlib.pyplot as plt # 读取处理后的数据 # 假设变量名u_earth(time, depth), v_earth(time, depth), depth(1D), time(1D) ds xr.open_dataset(adcp_timeseries.nc) u ds[u_earth].values v ds[v_earth].values depth ds[depth].values # 正值自海面向下 time_num matplotlib.dates.date2num(ds[time].values) # 构建网格 T, D np.meshgrid(time_num, depth, indexingij) # 流速大小和流向角 speed np.sqrt(u**2 v**2) theta (np.degrees(np.arctan2(v, u)) % 360) / 360.0 # 使用HSV做颜色编码色相流向亮度流速大小 vmin, vmax np.nanpercentile(speed, 2), np.nanpercentile(speed, 98) hsv np.zeros(speed.shape (3,)) hsv[..., 0] theta hsv[..., 1] 0.85 hsv[..., 2] np.clip((speed - vmin) / (vmax - vmin), 0, 1) rgb matplotlib.colors.hsv_to_rgb(hsv) rgb[np.isnan(speed)] 1.0 # 缺测区域显示为白色 # 如果只想看流速大小直接替换成 viridis 颜色即可 # rgb plt.cm.viridis((speed - vmin) / (vmax - vmin))[:, :, :3] fig plt.figure(figsize(14, 8)) ax fig.add_subplot(111, projection3d) surf ax.plot_surface(T, D, speed, facecolorsrgb, rstride2, cstride2, linewidth0, antialiasedTrue) # 观察角度 ax.view_init(elev25, azim-60) # 坐标轴标签 ax.set_xlabel(Time) ax.set_ylabel(Depth (m)) ax.set_zlabel(Speed (m/s)) # 时间轴格式化 ax.xaxis.set_major_locator(matplotlib.dates.AutoDateLocator()) ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter(%m-%d\n%H:%M)) # 流向角度色条 direction_cmap matplotlib.colors.ListedColormap([ matplotlib.colors.hsv_to_rgb((i / 360, 0.85, 1.0)) for i in range(360) ]) cbar fig.colorbar( plt.cm.ScalarMappable(normmatplotlib.colors.Normalize(0, 360), cmapdirection_cmap), axax, shrink0.6, pad0.1 ) cbar.set_label(Direction (deg from north)) plt.show()代码里有几个细节需要解释一下。date2num这步很关键它把datetime对象转成数值这样plot_surface的三维数据才能正常计算。坐标轴创建之后再传日期格式化器让刻度显示成“月-日 时:分”可读性比一串数字好太多。rstride和cstride是抽样步长如果数据网格很密设成2可以明显减少面片数量否则Notebook容易卡死。3.3 方案2流向轨迹三维图如果观测方式不是固定站位而是走航或者单点垂线流场我更推荐另一种3D图XYZ三个轴分别放东向流速u、北向流速v和深度把时间作为颜色渐变。这样的图能直接看出流速矢量随深度的旋转和大小变化很像一条在三维空间里游走的螺旋线。例如用plotly画交互版本的代码很简短import plotly.graph_objects as go fig go.Figure(data[go.Surface( xds[time].values, ydepth, zspeed, surfacecolornp.degrees(np.arctan2(v, u)) % 360, colorscaleHSV )]) fig.update_scenes(xaxis_titletime, yaxis_titledepth, zaxis_titlespeed) fig.show()plotly的好处是可以用鼠标旋转、缩放、查看具体数值适合需要和不太熟悉3D图的人沟通的场景。而matplotlib版本更适合出图投稿格式可控性更强。4. 我踩过的坑方向、坏点和画图性能4.1 流向的颜色编码容易翻车起初我用的是连续色带比如jet来映射流向角结果在0度和360度交界处颜色突然跳变明明流向很连续图里却出现了一条明显的颜色断层。后来才意识到流向是周期变量必须用HSV这类循环色带或者自己拼接色带确保首尾颜色一致。另一个和流向有关的坑是数据本身没有做磁偏角校正。仪器输出的时候默认参考地磁北极而我们要画的是相对于真北的流向。相差几度的磁偏角在时间-深度曲面上的颜色变化虽小但一旦要做流速矢量对比误差就会被放大。建议在数据预处理阶段统一校准别把问题留到画图时才发现。4.2 边界坏点带来的大坑ADCP在表层盲区和底边界附近的流速值经常是假的。如果不处理plot_surface会把这些异常值直接画成巨大的尖峰把整个曲面的Z轴拉伸到离谱的范围颜色也跟着失真。我后来养成一个习惯质量控制后的数据中把相关系数低于阈值或者信噪比过低的数值统一设为NaN画图前再用nanpercentile来计算颜色范围。这样即使存在极端异常色标也不会被个别坏点带偏。缺测区域在HSV转换成RGB之后会被赋成白色曲面会形成空洞这是正常现象说明该区域数据不可靠反而是好事。4.3 常见问题速查表问题原因处理方法曲面一团黑看不出颜色差别颜色范围过大或facecolors数值未归一化用nanpercentile限定2%~98%范围检查hsv蓝通道是否clip到0~1时间轴显示一长串数字没有用date2num或格式化函数使用matplotlib.dates.date2num转换并设置DateFormatter0度附近颜色跳变流向角用普通连续色带映射改用HSV循环色带或自建首尾一致的颜色列表曲面出现异常尖峰边界层坏数据未剔除先做质量控制将correlation/SNR低的bin设为NaN图片文件巨大Notebook卡死网格太密面片数量过多调大rstride和cstride步长或降采样数据后再画方向看着不对偏了个角度数据未经磁偏角校正在坐标转换阶段统一把磁偏角引入heading校正颜色条和曲面颜色对不上Normalize范围不一致确保surfacecolor和colorbar使用同一套vmin/vmax4.4 给新手的两点实操建议第一先做2D图做交叉验证。我画完3D图之后一定会再画一张传统的时间-深度速度云图用同样的色标对比一下确认3D曲面没有在数据插值或者颜色转换过程中引入新的问题。3D图是用来“看趋势”的2D图才是用来“读数值”的。第二颜色和视角都需要反复调整。view_init(elev25, azim-60)这个视角是我认为比较稳的起点它既能看清峰值流速的高点又不会让曲面完全遮挡住近底层的细节。实际使用时我会根据数据分布微调让曲线最高点处在视线最佳位置。最后说点个人经验。我试过把同一组数据分别画成2D云图和3D图拿给非专业的同事对比3D图在展示“流速峰值在哪里、流向怎么旋转”时说服力明显更强。但真要读取某个深度层的精确速度还是得回到2D剖面图或者直接查数据。所以我的做法是先用3D图做整体形态判断再在关键位置补2D切片图。3D图不一定是每一篇报告的主角但在你需要让一张图说话的时候它确实很能撑场面。本文还有配套的精品资源点击获取
返回列表