geopro/docs/superpowers/specs/2026-06-29-3d-radar-volume-...

14 KiB
Raw Blame History

三维雷达 DS 接入设计:规范化格式 → 体渲染 + 切片 + 异常 — 2026-06-29

负责人范围:仅三维雷达的体渲染 + 切片 + 异常标注(不含 2D 波列图详情页、不含二维雷达)。 本 spec 基于精读现有架构HEAD main+ Explore 代理交叉验证 + 用真实 Mala 数据实测确诊。 所有"数据事实"均已用 D:/Downloads/MALA南同大道_rSlicer 6 条测线核对,无未验证假设


1. 背景与锁定决策

客户新增雷达数据类型,模型层级沿用 项目 → GS → TM(TmObject) → DS列表。三维雷达 TM 携带 头信息(.head) + DS 列表 = 雷达RTK轨迹(.cor,备份+快照) + 每频率 雷达3D-频率N(打标.index + 通道1..N data.data)。 数据体 = K 切片(沿运动) × M 通道(sweep) × N 采样(深度)(与 IDS/OpenGPR 手册一致)。

四项锁定决策(用户 2026-06-29 确认):

维度 决策
职责范围 仅三维体 + 切片 + 异常;不做 2D 波列图详情、不做二维雷达
数据来源 后端未就绪 → 先读本地已转换好的目录(后端就绪后切按 DS 下载)
输入格式 客户端规范化 .head/.data/.cor/.index(新写 reader
渲染规模 单线 / 下采样稠密体打通

2. 已验证的数据事实★C++ reader 必须遵守,零假设)

实测工具:tools/radar_convert/malamira.py probeB-scan/C-scan 主序核对)。

  1. 数据体主序 = position-major:磁盘扫描顺序 = (道0:通道0..M-1)(道1:通道0..M-1)…,每 sweep 内 N 采样连续。 即 flat.reshape(K, M, N)[道][通道][采样]。probe 确诊position-major 的 B-scan 出现连续直达波 + 地层 + 双曲线绕射channel-major 为竖条乱码(已排除)。
  2. 直接对应 geopro 体轴 X=道(nx=K) / Y=通道(ny=M) / Z=采样(nz=N)无需轴置换——与现有 Gpr3dvVolumeBridge 轴映射完全一致。⚠️磁盘主序(采样最快→通道→道)与体内存布局(道最快,((k·ny+j)·nx+i) ScalarVolumeI16.hpp:58)相反,新 reader 必须逐体素散射重排填值(照 bridge 的 vol.at(to,j,s)= 严禁 memcpy/整块拷贝。"无需转置"指轴语义,不是字节布局。
  3. 维度推导M=NUMBER_OF_CHN=SAMPLESK=LAST_TRACE/NUMBER_OF_CH.headLAST_TRACE总扫描数=K×M不是道数
  4. 数据类型int16 小端(.rd3/bits=16) / int32 小端(.rd7/bits=32)。-32768 是直达波饱和真实值,非空值哨兵
  5. BITS 公式 bug:客户文档 §3.3 BITS = 文件大小/LAST_TRACE/NUMBER_OF_CH×8 漏了 SAMPLES 维、量纲错。 正确:bytes = 文件大小/(LAST_TRACE×SAMPLES),并与扩展名交叉校验。须同步后端(见 tools/radar_convert/README.md)。

3. 架构论点:只换最内层 reader下游 100% 复用

现有真实雷达体链路是分层的,只有最内层 reader 绑定厂商格式

Api3dRepository::createGprVolume          [api/Api3dRepository.cpp:128]  注册 dd_voxel/灰度色阶/预填 cachedGrid
  └ data::createGprVolumeGrid             [data/GprVolumeRepository.cpp:38]
      ├ io::gpr::buildLineVolumeFromGpr3dv  ← 仅此层绑定厂商 .iprb/.ord读 Impulse 原始)
      └ data::builtI16ToVolumeGrid        [data/GprVolumeRepository.cpp:11]  int16→float 稠密体
  → 勾选 → VtkSceneController.loadVolume → VtkSceneView.addVolume → render::buildVoxel(GPU RayCast)
  → SliceTool / InteractionManager / AnomalyDrawTool   (切片 B/C-scan + 点线面异常,全现成)

新增一个产 core::BuiltI16(同轴映射 X=道/Y=通道/Z=采样)的规范化 reader,则 builtI16ToVolumeGrid 往下的渲染/切片/异常链零改动。这正是 GprVolumeRepository.cpp:14 注释写明的"数据层方案 A"。

完全复用、不碰的部分

render::buildVoxelGPU 体绘制)、三级树 voxel 段(CategoryConfig.hpp:23 dd_voxel)、勾选→loadVolumeaddVolume、 切片(SliceTool updown 深度 C-scan / leftright·frontback B-scan + InteractionManager)、 异常(AnomalyDrawTool 点/线/面 → dd_anomaly 挂体)、跨视图色阶联动、builtI16ToVolumeGrid 适配器。


4. 插件化转换层(已落地 Mala

转换 = "按雷达型号的插件",本地 Python 工具 = 未来服务端下发插件的原型,契约不变:

plugin_id           : RADAR_TYPE_MALAMIRA            # 对齐文档 §2.1 型号列表
supports(fileset)   -> bool
convert(lineDir, prefix, outDir) -> {head, data, cor}   # 厂商原始 → 规范化
  • RADAR_TYPE_MALAMIRA(已完成)tools/radar_convert/malamira.py.rad→.head(§3.3)、 .rd3→.data(§3.5 原样拷贝)、.pos→.cor(§2.2.2)。info/convert/probe 三命令。6 条线全部转换 + 校验通过。 与文档偏差 5 处见 tools/radar_convert/README.md(含 BITS 公式修正)。
  • RADAR_TYPE_IMPLUSE(规划,暂不做):见 §7。

客户端职责(未来):设备对接/原始导入 → 按型号拉插件 → convert → 喂规范化 reader。

契约两点待补(登记)(a) convert 返回 {head,data,cor} 未含 .index(打标),有打标的型号需扩约定; (b) 规范化 .data 无扩展名C++ reader 解析字节宽(2/4) 单点依赖 .headBITS 字段 → 保证转换器 BITS 永远正确是隐性前提(malamira.pyfilesize==LAST_TRACE×SAMPLES×bytes 断言已守住,见 §2.5)。


5. 新增 / 改动清单C++ 侧,待实现)

# 文件 动作
src/io/gpr/NormalizedRadarReader.{hpp,cpp} 纯函数:parseHead(.head)→RadarHeaderreadDataCube(.data,header)→cube(按 BITS+ENDIAN_TYPE 解二进制,reshape(K,M,N) 主序已定);parseCor(.cor)→vector<TracePos>(先解析,世界配准后置)
src/io/gpr/NormalizedRadarVolumeBridge.{hpp,cpp} buildLineVolumeFromNormalized(lineDir,prefix,metrics,coarse,targetDy)→core::BuiltI16⚠️ DRYGpr3dvVolumeBridge.cpp:133-197 的值域扫描/Quant构造/逐体素填值/spacing 当前内联且耦合 Impulse 模型——动工时先把这段抽成共享 helper(签名接受抽象立方体访问器 (c,t,s)→short + 通道偏移/几何),两个 bridge 同调;planChannelInterpolation/depthOfSample 已是共享纯函数。不要复制填体循环(否则两份独立漂移)
src/data/GprVolumeRepository.{hpp,cpp} createRadarVolumeGrid(lineDir,prefix,coarse,targetDy)(调②,复用 builtI16ToVolumeGrid)。不动现有 createGprVolumeGrid
src/data/api/Api3dRepository.{cpp,hpp} createRadarVolume(...)(≈createGprVolume 复制换调③),仍注册 dd_voxel
src/app/main.cpp 本地导入入口「导入三维雷达测线目录」:选目录+前缀 → createRadarVolumerefreshAnalysis → 自动勾选渲染(后端就绪后换按 DS 下载)
测试 tests/io/gpr/test_normalized_radar_reader.cpp(喂 tests/data/radar/ 截断样例,断言维度/字节序/通道插值/主序)

几何映射(关键算法,纯函数易测)

  • X(沿线 spacing) = DISTANCE_INTERVAL(距离模式,本期唯一支持)。
  • Y(通道横向) = 复用 GprGeometry::planChannelInterpolation(chXOffsets, targetDy)GprGeometry.hpp:27)—— 通道偏移取自 .headCH_X_OFFSETS(不读 .ord),线内插值、绝不跨线(默认 2.5cm)。
  • Z(深度 spacing) = TIMEWINDOW/(SAMPLES-1) × 波速/2;波速由 DIELECTRIC 求,.rad 无该字段时用默认波速。
  • 量化core::BuiltI16(复用 core/algo/GprVolumeBuilder.hpp)。
  • .cor/.index 本期只解析不深用:单线放原点沿 +X切片/异常即可工作。逐道 GPS 世界配准 + 打标 → 多线阶段。

6. 分期

  • P0本期:①–⑥,单线规范化体打通。验收:导入 samples/radar/malamira_南同大道(如 000 线)→ 树出现 dd_voxel → 勾选渲染 → 切 updown/leftright/frontback → 画点/线/面异常并挂体下。
  • P1.cor 逐道 GPS 世界配准 + 高程、.index 打标、多线合一场景。
  • P2:大体量 → 把 gpr_pocChunkedVolumeStore+*VolumeSource 核外/LOD 接进 app碰 controller/view单独立项

7. 双数据集测试策略 + Impulse 转换器决策

已有完整 POC 基于另一套雷达数据 明星路Impulse,交接见 .superpowers/sdd/HANDOFF-2026-06-27-gpr-lod-slice.md。 明星路 数据在本地(D:/Downloads/明星路14G20 测线)。

关键差异:明星路 是 RADAR_TYPE_IMPLUSE——每线 14 通道为 14 个独立 .iprb 二进制_A01.._A14,各 ~74MB+ 逐通道 .iprh + .ord + .gps/.time/.cor/.mrk。Mala 则把 16 通道打进一个 position-major .rd3。 现有 C++ P1 链(buildLineVolumeFromGpr3dv能直读 .iprb

⚠️ 校正(评审 HIGH-2:明星路真正"经验证"的渲染是 gpr_poc CLI 的核外 LOD 多线路径,不是 app 内 createGprVolume→builtI16ToVolumeGrid→loadVolume→addVolume稠密单体路径——后者的 app 导入 UI 触发从未接 HANDOFF-2026-06-27 §6 第 3 条:"数据层 createGprVolume 已就绪,但 UI 触发未接")。故 app 内稠密渲染对两套数据都是首次

决策:本期不建 Impulse→规范化 转换器。 理由:

  1. Impulse .iprb 原始二进制远比 Mala "改名+字段映射"复杂14 个独立通道文件、需逆向二进制布局);且 P1 链含处理 gain/filter而规范化 .data原始——raw vs processed 语义需先厘清。高成本、低边际价值。
  2. 明星路 的 .iprb 已有现成 in-app 数据层入口(createGprVolume,只差接 UI 触发),复用成本低。

双数据集测试(校正后表述)

  • Mala16ch规范化 reader 新路径) + 明星路14ch现有 .iprb P1 路径) 在 app 内首次稠密渲染, 二者喂同一条下游 render/slice/anomaly 链 → 互证下游对两种通道数/几何无关。前置任务:明星路 in-app 导入入口 同样需接(复用现成 createGprVolume,与清单⑤同形)。
  • gpr_poc CLI 的明星路成像作为离线对照基线(那才是已验证的),不声称 in-app 已验证。
  • ⚠️ 注意:明星路是处理后数据、Mala 是原始数据 → 双测只验"管路通 + 几何/通道数无关"不验"成像一致"(二者有无增益滤波,视觉不可比)。
  • RADAR_TYPE_IMPLUSE 转换器列为规划的第二插件,待规范化管线需吞并 Impulse多半与服务端插件同步时再做 届时一并解决 raw/processed 取舍。当前"插件产规范化→reader 吃规范化"契约仅对 Mala 一家成立,属过渡态

8. 风险 / 待确认

  1. .cor 坐标语义.pos 是本地投影坐标按文档直映入经纬度列、N/E/M 占位、解状态=4。 真实 CRS 未知;单线渲染不依赖 .cor 配准,多线阶段需后端给 CRS_CODE
  2. 波速默认值Mala .radDIELECTRIC → Z 深度 spacing 用默认波速建议介电常数≈9 / v≈0.1 m·ns⁻¹ 仅影响深度标尺,不影响体结构。需确认默认值。
  3. 采集模式:本期只支持距离模式X 用 DISTANCE_INTERVAL);时间模式(SCAN_SECOND)推迟。
  4. 大体量 / 内存实算app 侧 ScalarVolumestd::vector<double>8 字节/体素,比 int16 体放大 4×,是真正天花板)。 单线最大线K=3778, ny≈49@2.5cm, N=516coarse=4 → nx=945 → 体素 23.9M → ≈182 MiB+中间 BuiltI16 48MB +VTK 副本,单线峰值 <0.5GBP0 安全 coarse=1 全分辨率 → ≈764 MB 仅体(单线勉强,非 P0 默认)。多线/全分辨率必走 P2 核外ChunkedVolumeStore,已有 gpr_poc 实现)。

9. 测试计划

  • 转换器(已验证)info 维度校验 6/6 通过;convert 产物 .data 与源 .rd3 字节一致;probe 主序确诊。
  • C++ reader 单测tests/data/radar/ 放截断样例position-major 可按字节前截 K' 道得合法小体); 断言 K/M/N、字节序、planChannelInterpolation 行数、主序填值正确。
  • bridge/repo 单测createRadarVolumeGridVolumeGrid 维度/spacing/vmin-vmax 合理NaN 空值语义。
  • 联调(人工)build.bat app → 导入样例 → 渲染 + 三向切片 + 三类异常;对照 明星路 现有路径同场景。
  • 失败排查看桌面日志 %LOCALAPPDATA%/Geomative/Geopro3/logs/geopro_*.log

10. 文件地图(现有锚点)

  • 数据体模型 src/data/repo/I3dSceneRepository.hpp:19VolumeGrid 稠密 float
  • 渲染入口 src/controller/I3dSceneView.hpp:45addVolume/ src/app/VtkSceneView.cpp:173
  • 体素工厂 src/render/actors/VoxelActor.hpp:29/47buildVoxel/buildVoxelI16
  • GPR IO src/io/gpr/Gpr3dvVolumeBridge.hpp:47(轴映射范本)/ GprGeometry.hpp:27,32(通道插值/深度)
  • 体进 app src/data/api/Api3dRepository.cpp:128createGprVolume/ src/data/GprVolumeRepository.cpp:11,38
  • 切片/异常 src/render/interact/SliceTool.hpp / InteractionManager.hpp:52,57 / AnomalyDrawTool.hpp:33
  • 三级树 src/app/panels/columns/CategoryAnalysisTab.hpp:28 / CategoryConfig.hpp:23
  • 转换器 tools/radar_convert/malamira.py + README.md;样例 samples/radar/malamira_南同大道/
  • 详情视图扩展(如未来做 2D 波列图,非本期)docs/superpowers/specs/2026-06-28-dataset-detail-view-architecture-and-extension-guide.md