4. 栅格化海图环境建模与可航区域提取
从海图数据到可航二值栅格
本节的任务是把矢量海图或离散水深采样转换成一张“能走/不能走”的二值栅格图,并验证分辨率是否足以支撑后续航迹规划。核心处理链是:坐标统一 → 栅格划分 → 属性赋值 → 可航判定 → 分辨率校核。栅格单元的值只有两种:1 表示可航,0 表示不可航(陆地、浅水、障碍物)。
先约定坐标与判定规则,后续所有计算保持一致:采用局部平面直角坐标系(单位 m),x 向东、y 向北;栅格原点位于左下角,第 (i, j) 个单元覆盖 x∈[i·Δ, (i+1)·Δ)、y∈[j·Δ, (j+1)·Δ),Δ 为分辨率。可航判定采用保守原则:单元内只要存在任一不可航采样点,或单元中心水深小于安全水深,即判为不可航,避免“擦边可航”带来的风险。
栅格化与可航判定的实现
下面给出一个可直接运行的 Python 实现,输入为水深采样点(含陆地标记)与障碍物多边形,输出可航二值栅格,并统计不同分辨率下的可航面积与连通性变化。
import numpy as np
from matplotlib.path import Path
# ---------- 1. 示例数据(教学假设值,非实测) ----------
# 水深采样点: (x, y, depth_m),depth = -1 表示陆地
soundings = np.array([
[0, 0, -1], [200, 0, -1], [400, 0, -1],
[0, 200, 12], [200, 200, 8], [400, 200, 5],
[0, 400, 15], [200, 400, 6], [400, 400, 3],
[100,100, 10], [300,100, 4], [100,300, 9], [300,300, 2],
], dtype=float)
# 障碍物多边形(如礁石、养殖区),单位 m
obstacles = [
np.array([[150, 150], [250, 150], [250, 250], [150, 250]]),
]
SAFE_DEPTH = 5.0 # 安全水深阈值 (m),按目标船吃水+富余量设定
def build_grid(soundings, obstacles, delta, safe_depth=SAFE_DEPTH):
xs = soundings[:, 0]; ys = soundings[:, 1]
xmin, xmax = xs.min(), xs.max()
ymin, ymax = ys.min(), ys.max()
nx = int(np.ceil((xmax - xmin) / delta))
ny = int(np.ceil((ymax - ymin) / delta))
grid = np.ones((ny, nx), dtype=np.uint8) # 先全部置为可航
# 单元中心坐标
cx = xmin + (np.arange(nx) + 0.5) * delta
cy = ymin + (np.arange(ny) + 0.5) * delta
CX, CY = np.meshgrid(cx, cy)
# 最近邻水深赋值(教学简化,工程中可用 IDW/克里金插值)
for i in range(ny):
for j in range(nx):
d2 = (xs - CX[i, j])**2 + (ys - CY[i, j])**2
k = np.argmin(d2)
depth = soundings[k, 2]
if depth < 0 or depth < safe_depth:
grid[i, j] = 0
# 障碍物多边形栅格化(保守:中心落入即不可航)
pts = np.column_stack([CX.ravel(), CY.ravel()])
for poly in obstacles:
inside = Path(poly).contains_points(pts).reshape(ny, nx)
grid[inside] = 0
return grid, (xmin, ymin, delta)
def report(grid, delta):
free = int(grid.sum())
total = grid.size
print(f"分辨率 Δ={delta:>5.1f} m | 栅格 {grid.shape[1]}x{grid.shape[0]} "
f"| 可航单元 {free}/{total} = {free/total:.1%}")
return free
for d in (100, 50, 25, 10):
g, meta = build_grid(soundings, obstacles, d)
report(g, d)
运行后可以观察到:分辨率越粗,可航单元占比通常越大,因为浅水与障碍物被“平均”掉了;分辨率越细,可航区域越贴近真实边界,但栅格数量按 Δ² 增长。这正是分辨率影响航迹可行性的根源——粗栅格可能让规划器“穿过”实际不可航的窄浅区。
分辨率对可行性的判读
判断分辨率是否够用,不能只看可航面积,还要看关键通道是否连通。做法是对可航栅格做连通域标记,检查起点与终点是否在同一连通域;再对比相邻两档分辨率的连通结果,若粗分辨率连通而细分辨率不连通,说明粗栅格存在“虚假通道”,必须加密。
from scipy import ndimage
def connectivity_check(grid):
labeled, n = ndimage.label(grid, structure=np.ones((3, 3)))
return labeled, n
for d in (100, 50, 25, 10):
g, _ = build_grid(soundings, obstacles, d)
labeled, n = connectivity_check(g)
# 取左下角与右上角附近的可航单元作为起终点代表
start = tuple(np.argwhere(g)[0])
end = tuple(np.argwhere(g)[-1])
same = labeled[start] == labeled[end] and labeled[start] != 0
print(f"Δ={d:>4} m | 连通域数={n} | 起终点连通={same}")
若某档分辨率下起终点不连通,而更细分辨率下连通,说明该档分辨率丢失了必要通道;反之若粗分辨率连通、细分辨率不连通,则粗分辨率不可信。最终选定的 Δ 应满足:关键通道连通性与细分辨率一致,且栅格总量在规划算法可接受范围内(例如 A* 单次搜索不超过百万级单元)。
分辨率选择与结果校核清单
把上面的流程固化成可复用的检查项,每次更换海图区域或船型时逐项确认,避免把“看起来能走”的栅格直接交给规划器。
| 检查项 | 判据 | 不通过时的处理 |
|---|---|---|
| 安全水深阈值 | ≥ 目标船吃水 + 富余水深(如 0.5~1.0 m) | 调高阈值,重新判定浅水区 |
| 障碍物膨胀 | 不可航区外扩 1~2 个单元 | 做形态学膨胀,保证安全裕度 |
| 关键通道连通性 | 起终点在同一连通域,且与细分辨率结论一致 | 加密 Δ 或修正插值 |
| 栅格规模 | 单元总数在规划算法可接受范围 | 分区栅格化或局部加密 |
| 坐标一致性 | 栅格原点、Δ、行列方向与规划器约定一致 | 统一坐标定义后再输出 |