1.00,0.35。
这是统计表里同一行的两个数字。那天夜里脚本跑完,我打开结果表,一眼就看见了它们:在地形最陡的那块区域,同一种规划方法,先验网格从 120 米换成 900 米,回放可行率从满分一路掉到了三成多。
我盯着这两个数字坐了好一会儿。几个月来我一直在想办法让测线的综合得分更高一点,那一刻才第一次认真地去想另一个问题:一条看起来很好的测线,什么时候该被放弃?
这篇想从这里讲起,讲讲这个多波束测线规划基准仓库是怎么一点点长成现在这样的。
先说清楚它是什么
2026 年 4 月 30 日傍晚,我在终端里敲下推送命令,把攒了几个月的仓库推上 GitHub,顺手打了 v0.1.0 的版本标签。刷新页面,源码目录、派生出来的 CSV 和 JSON 表格、统计图表、环境配置清单、公开数据的引用,一样样整整齐齐地排出来,心里长舒了一口气。
仓库名里还带着 auv,那是早期起名时留下的历史称呼,后来懒得改。其实它做的事情很具体:在公开的海底地形网格上,做固定航向测线的几何与数值规划。比的是测线方向怎么选、非均匀线间距怎么排布、总路径多长、预测覆盖率多少、波束过度重叠的比例多大,以及局部漏测区域的修补策略。
船体的横摇纵摇姿态、水体声速剖面带来的折射、底层导航控制器的跟踪误差、波束级的声学散射特性、声呐换能器吐出来的原始测深点云——这些都在我这几行网格脚本够不着的地方。纸面上的航线随时可以用代码重算一百遍,而真正海面之下的风浪与声学物理,还在电脑外面。
八月有几天空闲,我把整个仓库从头到尾又捋了一遍。写这组实验时,我最怕的就是把不同层面的问题搅成一锅粥,所以这张图一直挂在 README 最上面,提醒自己现在站在哪一格。
仓库里存的是一条计算链
在底层地形数据的选取上,这套基准引入了 GEBCO 2025 Grid 与 USGS Southern Cascadia 30 m 复合海底地形产品,并在文档里老老实实为两份公开数据集都留下了官方 DOI。
由于原始栅格的体积实在太大了,我没有把它们硬塞进 Git 仓库里。如果想从最源头的数据导入开始完整重跑,依然需要顺着 DOI 链接回到官方发布页去下载对应的原始文件。仓库里真正留下的是裁剪后的小型处理缓存、算好的派生数据表,还有把整个实验串联起来的分析入口脚本。
这种做法固然牺牲了“把仓库 git clone 下来就能立刻在本地拥有全套原始大图”的省事体验,却换来了一笔极其清爽的来龙去脉:文档里每一张对比图、表格里每一个覆盖率小数,究竟是从哪一份公开地形产品里切出来的、经过了哪个脚本入口的计算、最终又落进了哪一张汇总表,全都能顺藤摸瓜找回去。哪怕日后上游发布了新版的修正网格,仓库里的实验结果也不会悄悄跟着变脸;任何想要重跑或讨论这组数据的人,都站在同一份确定的数据快照上说话。
运行环境锁在根目录的 environment.yml 里,Python、NumPy、Pandas、Matplotlib、Rasterio 这些关键依赖都明确了版本约束。
顺着提交记录往下翻,还能看到一个后来才加的小工具,check_release_readiness.py。四月底打 v0.1.0 的时候,我满脑子只想着赶紧把核心算法和数据表发出去,根本没顾上这个。到了 5 月 23 日,为了防止自己手滑漏掉必需的数据表,或者不小心把本地乱七八糟的草稿路径、夸大其词的表述混进公开目录,我写了这个自动扫描目录树的守门脚本;7 月 12 日的 a51dcf1d 提交又做了一轮更新。它本身不参与任何数值计算,就像一把尺子,每次发布前先量一量。
对这样一份数值基准来说,“可复现”这三个字落到地上,其实就是老老实实交代清楚三件事:数据从哪来,脚本从哪个入口跑,输出结果落在哪个目录。至于不同算力的机器跑起来会差几秒钟,或者能不能直接搬上真船当导航系统使,从来不是这几张静态数据表该去吹嘘的事。
得分更高的测线,也可能先漏底
只要在公开地形网格上跑完一轮规划,脚本很快就能吐出总路径长度、预测覆盖率、平均过度重叠率这几个汇总指标,一目了然。
可做实验的那阵子,我盯着屏幕看久了却发现,麻烦恰恰出在“平均”和“汇总”上:高度浓缩的单一数字,最容易把真实的空间位置信息悄悄抹平。一条在纸面上看着总航程更短的折中线路,背地里可能是牺牲了某块局部区域的覆盖换来的;一条平均重叠率很平稳的方案,到了海底峡谷侧壁、坡度陡变的褶皱地带,照样可能撕开一片集中的漏测缺口。
所以我没有只留一张光鲜的“最终最优路径图”就草草收工。主规划器之外,我把网格分辨率变化、先验地形误差、覆盖率门禁阈值、最大连续未覆盖块面积、局部单元格重叠分布以及事后修复决策,拆成了几组彼此独立的诊断实验。
优化器负责回答:在当前这套理想假设下,哪种测线布局的综合得分更高。后面跟上的这一连串诊断则在不依不饶地追问:假设一旦动摇,这条高分线路最先从哪个角落开始漏底。两类问题只有并排摆在同一张桌上,心里才算踏实。
粗先验放到细网格上回放
开头那两个数字,就出自这组实验。
评估基准固定在 300x240 的细网格上,再分别把基于 120 米、300 米、600 米和 900 米粗先验网格规划出来的测线放上去重放检验,针对混合启发式方法跑满了 20 个随机种子。
统计表交出的答卷相当诚实:在地形起伏剧烈的高复杂度裁剪区块上,混合方法的回放可行率从 120 米先验时的满分 1.00,一路跌到了 900 米先验时的 0.35;而在同一张表格里,低复杂度和中等复杂度的平缓海底,面对粗先验时则要从容得多。
要是当时偷懒只看总体平均,很容易草率地下一个“900 米先验完全不能用”或者“粗先验影响不大”的糊涂结论。海底地形本身的复杂度等级必须死死钉在结论表里——如果不分青红皂白地把平缓海盆和陡峭峡谷混在一起算,“高复杂度地形最先在粗先验下失效”这一关键现象就被彻底盖住了。先验网格切得越粗,固然能成倍减轻前期的栅格计算负担,但同时也免不了丢掉陡坡与沟壑处的局部几何褶皱。省下的算力与丢掉的细节,本来就该记在同一本账上对照着看。
往先验里掺点“坏”
顺着先验失真的思路往下深挖,结构化先验误差实验往网格里注入了三种更具针对性的合成偏差:大尺度的相关低频偏差、随坡度放大的高程偏差,以及专门附着在峡谷侧壁上的局部几何偏差。每个场景同样跑满 20 个随机种子。
实验跑完,两个来自 GEBCO 的平缓场景和那个来自 USGS 的高复杂度场景,交出的答卷脾气截然不同。面对局部结构偏差,USGS 场景明显更容易把固定线间距方案在过度重叠和覆盖可行性上的软肋暴露出来。
当然,这些扰动终归是我坐在电脑前给规划算法出的压测考题。真船在海上碰到的测量误差,比这要复杂得多。
海流偏移,是一把比较用的尺子
除了海底地形可能看走眼,船和潜器在水里走的时候,也难免被海流推偏。
“流偏移回放”模块做的,是把设定的海流速度与流向,映射成执行过程中的残余横向位置偏移、航向角微扰、低频声呐足迹变形,以及与海底坡度耦合在一起的足迹宽度缩水。为了让不同方法之间的比拼足够公允,在相同的地形场景与扰动强度下,所有方法都共享同一组共同随机数(Common Random Numbers),确保大家面对的是完全一致的扰动样本。
拿它来看不同固定测线几何在干扰下谁更扛得住,挺趁手;但它说到底依然是一套简化的几何代理,算不上真正的船舶流体动力学仿真。
“执行风险修复”这一步,是我在深夜对着满屏事后失效的图表看烦了才想通的。既然已经知道某些几何排布一遇到横向流偏就会漏测,何必等生成了最终结果再马后炮地标红?完全可以在挑选候选测线的时候,就把这些已知的脆弱条件当成一票否决的理由。
于是代码把回放里暴露出来的流偏移、航向抖动和足迹缩水风险,提前挪进了候选布局的筛选阶段。当然,哪怕把风险惩罚项塞进了目标函数,这里头依然没有引入真实的闭环反馈控制器、没有耦合六自由度流体力学方程、也没有算进水体声速剖面的动态变化。它做到的仅仅是让数值层面的过关门槛变得更严苛;至于筛出来的那条线放到真船上好不好开,还得留给真正的海试去检验。
过了及格线,不代表局部没事
在默认的平均覆盖率和重叠率门禁之外,“阈值与局部失效诊断”模块把标尺往严里拧了几格:它挨个测试了 95%、97%、98%、99% 直至 99.5% 的覆盖率硬阈值,同时搭配了 1%、2%、3% 和 5% 的平均过度重叠率上限。
一旦把审查条件收紧到极其严苛的 C99/O2(即要求覆盖率不低于 99% 且平均过度重叠不超过 2%),哪怕是在相对平缓的 GEBCO 场景里,原本表现不错的混合规划布局也没法做到百发百中地过关。而除了看整体过没过关,诊断表里单列的“最大连续未覆盖块面积”和“p99 单元格过度重叠率”,更是直接把那些被全图平均值悄悄藏起来的局部毛病一个个标了个一清二楚。
分段航向,没有一路绿灯
直觉上,允许把测区切成几块、各自采用不同的测线方向,自由度更高,结果理应更好才对。
决策审计表却不这么说。在实际测试中,自由度更高并没有换来一路绿灯:有的场景确实靠块级修复补齐了漏测;有的场景却在第一道覆盖率门禁处就直接把分段方案拒之门外;还有些场景虽然靠分段换来了一点点覆盖率提升,算上转向与衔接的转换代价后,评估逻辑依然决定退回最简单的单一航向。
这段脚本是对已有评估结果的重算与复核,并没有从头重跑底层规划器,也没碰 Dubins 曲率约束路径或者真实的轨迹跟踪控制。那些“拒绝分段”的案例,我原原本本地留在了结果表里。一个听起来更花哨的方案,究竟是在哪一道具体门槛前被拦下的,看得清清楚楚,这比一张全绿的表更让我放心。
九个全过,和那 10.42%
“足迹有效性检查(Footprint Validity Audit)”则给我上了一堂关于“别被及格线糊弄”的生动一课。
在那组实验挑选的 9 个代表性“场景—方法”布局里,我把原来粗略的“波束总宽度代理”,替换成更精细的“左右舷非对称独立足迹范围”,再跑一遍 C97/O3 的整体可行性门禁判定——9 个案例的通过与否,竟然没有发生哪怕一次翻转。
(看着终端里整整齐齐的九个通过,我差点以为这两套算法算出来的东西差不多。)
然后我低头看了一眼旁边的细粒度统计表,当场出了一身汗:在 USGS 高复杂度的峡谷场景下,两种足迹算法在局部单元格上的覆盖计数分歧,最高达到了 10.42%。
高达十个百分点的局部分歧,意思很明白:粗略的总宽度和左右舷独立足迹,在几何上根本谈不上等价。宏观上的“及格与否”恰好没被掀翻,只是因为当前的规划门禁阈值给得还算宽容;真要是到了地形陡峭的峡谷地带,这种简化的几何足迹代理绝不可能替代波束级的声学射线追踪、船体姿态补偿和声速剖面校准。
四月的标签,七月的锚点
时间走到 2026 年 7 月 12 日,主分支稳稳停在了提交 a51dcf1d5166dd798007dfd72a6b7ff30530c11b 上。那次提交把仓库元数据和当时的论文稿对齐了,更新后的 README 也在显眼位置写明:带 AUV 字样的名字只是早期留下的历史称呼,当前代码库真正围绕着打转的,是一套固定航向测线的多波束(MBES)规划数值基准。上面聊到的所有实验数字,也都定格在 a51dcf1d 这个快照里。
把春夏之交这几个月的版本节点摊开看:4 月底打下的 v0.1.0 标签(对应提交 625fb6a)里,粗先验回放还只是个早期雏形,只有 120、300、600 米三种网格,混合方法只跑了 0 到 4 号五个种子。900 米的极粗先验、扩充到 20 个种子的完整样本,连同结构化地形误差、执行风险前置修复以及局部严苛门禁审计,全都是五到七月间我一点点补进主分支的。
四月首发时做了什么、七月定稿时又补了什么,分开一五一十记清楚。以后再翻这个仓库,才不会把几个月的摸索混成同一天的事。
收拾完这一大堆表格和脚本,已经是后半夜了。
刚动手那会儿,我总在琢磨怎么让某条测线的分数再高一点。现在更常想的,是一条看起来不错的线,在什么条件下应该被果断放弃。更短的航程、更高的平均覆盖率、一次顺顺当当的及格,都省不掉对局部漏测、先验分辨率敏感性和流偏风险的逐项追问。
这个小仓库就停在调查船解缆出航之前。它存下来的,是一道测线规划题在电脑里怎么被一步步算出来、怎么被换着花样折腾。离真正的海,还有很远。
资料与版本
- 当前代码锚点:提交
a51dcf1d与该提交下的 README。 - 4 月公开节点:Release
v0.1.0,其标签指向提交625fb6a。 - 先验回放:粗先验到细网格 与 结构化先验误差。
- 执行扰动:流偏移回放 与 执行风险修复。
- 局部门禁:阈值与局部失效、分段决策 与 足迹有效性。
- 环境与仓库卫生:environment.yml 与当前的 发布准备检查(5 月首次加入,7 月更新)。