4. 电子海图与航行环境建模

无人船要自主规划航路,第一步不是算路径,而是把“海在哪里、哪里能走、哪里不能走”变成计算机能处理的数据。电子海图(ENC)是这一过程的起点,而栅格地图是终点。本节围绕这条数据链展开:从 S-57 格式的 ENC 中读出要素,提取水深与障碍物,再离散化为可航行区域栅格,供后续 A*、Dijkstra 或采样类规划器使用。

一、ENC 与 S-57:数据是怎么组织的

ENC(Electronic Navigational Chart)是官方发布的矢量海图,其交换标准为 IHO S-57。S-57 采用“特征(Feature)—空间(Spatial)”分离的模型:特征对象描述“是什么”,如水深点(SOUNDG)、障碍物(OBSTRN)、航道(CHANEL)、锚地(ACHARE);空间对象描述“在哪里、什么形状”,包括点、线、面(面由边链和节点拓扑组成)。每个对象带有一组属性,例如水深值 VALSOU、障碍物类型 CATOBS、疏浚深度 DRVAL1/DRVAL2。

实际读取时,通常借助 GDAL/OGR 的 S-57 驱动或专用库(如 S-57 解析器、pyseachart 类工具),把 ENC 转成可按图层查询的要素集合。一个关键点是:S-57 的坐标是地理坐标(经纬度,WGS-84),而无人船规划通常在局部平面坐标系中进行,因此读取后必须做投影变换,例如 UTM 或本地 ENU 平面,单位统一为米。这一步若遗漏,后续栅格分辨率会失去物理意义。

# 示例:用 GDAL 读取 S-57 图层(示意,非实测)
from osgeo import ogr
ds = ogr.Open("CN5X0001.000")
for i in range(ds.GetLayerCount()):
    lyr = ds.GetLayerByIndex(i)
    print(lyr.GetName(), lyr.GetFeatureCount())
# 常见图层:DEPARE(水深面)、OBSTRN(障碍物)、LNDARE(陆地)、COALNE(海岸线)

二、水深与障碍物提取

水深信息在 ENC 中主要有两种载体:一是离散水深点 SOUNDG,二是水深面 DEPARE(由 DRVAL1 与 DRVAL2 界定深度区间)。对规划而言,更实用的是 DEPARE:它把海域划分为若干深度带,例如 0–5 m、5–10 m、10–20 m。无人船吃水为 d、安全富余为 m 时,可航深度阈值为 d+m。示例中设吃水 0.6 m、富余 0.4 m,则阈值 1.0 m,凡 DRVAL2 ≤ 1.0 m 的水深面即判为不可航。

障碍物提取需覆盖多类要素:OBSTRN(沉船、礁石等)、UWTROC(水下岩石)、WRECKS(沉船)、以及陆地 LNDARE 与海岸线 COALNE。注意 S-57 中部分障碍物带 VALSOU 属性,可据此判断其顶部深度是否构成威胁;无深度属性的障碍物应按“最保守”处理,即视为不可穿越。此外,桥梁、管线等线状要素若净空不足,也应纳入禁行集合。

提取阶段建议保留每个要素的原始属性(类型、深度、来源图层),便于后续按不同安全等级重新生成栅格,而不是一次性“烧死”在图上。

三、可航行区域栅格化

栅格化的本质是把连续几何离散为规则网格。设规划区域为矩形,分辨率 r(米/格),则网格尺寸为 ceil(W/r) × ceil(H/r)。每个格子取一个占据值:可航、不可航,或附加代价(如浅水惩罚)。常用做法是“先栅格化禁行要素,再取反”:

  1. 建立全零栅格(默认可航);
  2. 将陆地、浅水面、障碍物按多边形/点缓冲后“烧入”为禁行;
  3. 对障碍物按船体半径加安全距离做膨胀(形态学膨胀),保证路径与障碍物保持净距;
  4. 输出二值占据栅格,或叠加距离变换得到“离障碍物距离”代价层。

分辨率选择是权衡:r 越小,路径越精细但计算量与内存上升;r 过大则窄航道可能被“抹掉”。经验上 r 取船宽的 1/2~1 倍,示例船宽 0.8 m,取 r=0.5 m。膨胀半径取“船体半宽 + 安全距离”,示例 0.4+0.6=1.0 m,即膨胀 2 格。

# 示例:二值栅格 + 膨胀(示意参数)
r = 0.5          # 米/格
draft, margin = 0.6, 0.4
depth_limit = draft + margin      # 1.0 m
inflation_cells = round((0.4 + 0.6) / r)   # 2 格
# 对每个 DEPARE:若 DRVAL2 <= depth_limit,则整面烧为禁行
# 对 OBSTRN/UWTROC/WRECKS:按点或面烧入后做 2 格膨胀

四、从数据到栅格的完整链路

下图把本节的数据流串起来:ENC/S-57 经解析得到特征与空间对象,按“水深阈值”和“障碍物类型”分流提取,投影到局部平面后统一栅格化,最终输出可航行栅格与代价层,供规划器直接读取。图中标注了示例阈值与膨胀参数,便于对照实现。

ENC/S-57 → 可航行栅格 数据链 ENC / S-57 特征 + 空间对象 解析与投影 经纬度 → 局部平面(m) 水深提取 DEPARE / SOUNDG 阈值 1.0 m(吃水+富余) 障碍物提取 OBSTRN/UWTROC/WRECKS 陆地 LNDARE / 海岸线 安全膨胀 半宽0.4 + 安全0.6 = 1.0 m 栅格化 分辨率 r = 0.5 m/格 禁行要素烧入 → 取反 膨胀 2 格 二值占据 + 距离代价层 规划输入 可航行栅格 (0/1) 代价层(离障距离) 起点 / 终点坐标 → A* / Dijkstra 示例参数:吃水0.6 m、安全富余0.4 m、船宽0.8 m、安全距离0.6 m、分辨率0.5 m;实际取值需按船型与任务调整

五、实现要点与常见问题

第一,坐标系一致性。S-57 为地理坐标,栅格为平面坐标,投影参数必须记录在栅格元数据中,否则规划出的路径无法回投到海图上显示。第二,面要素的“洞”与多部件。DEPARE 可能包含岛屿形成的洞,栅格化时需按奇偶规则或使用多边形填充算法正确处理,避免把岛内水域误判为可航。第三,边界处理。规划区域边缘应留出缓冲,防止路径贴边导致实际航行越界。第四,数据时效性。ENC 有更新周期,读取时应记录版本号与更新日期,避免使用过期水深。

一个可操作的验证方法是:栅格生成后,随机抽取若干已知浅点与障碍物坐标,检查其所在格是否为禁行;再沿一条已知安全航道取若干点,检查是否为可航。若出现矛盾,优先排查投影错误、深度阈值取值与膨胀半径是否过大。完成这一步,栅格地图才具备作为规划输入的可信度。