1. 城市低空航迹规划基础环境建模

城市低空航迹规划基础环境建模 学习卡片

任务说明

本案例构建一个可供航迹规划算法直接调用的城市低空环境模型,包含三类核心要素:建筑物(三维盒体)、禁飞区(水平多边形+高度范围)、地形起伏(规则网格高程)。输出为统一的环境查询接口,支持后续航迹规划案例的空间碰撞检测与代价计算。

环境模型数据结构

采用面向对象方式定义环境要素,所有坐标使用局部笛卡尔坐标系(单位:米),高程基准为平均海平面。建筑物用轴对齐包围盒表示;禁飞区用水平多边形顶点序列加高度区间表示;地形用规则网格存储高程值。

import numpy as np
from typing import List, Tuple

class Building:
    """建筑物:轴对齐包围盒,坐标范围 [xmin,xmax]×[ymin,ymax]×[zmin,zmax]"""
    def __init__(self, xmin, xmax, ymin, ymax, zmin, zmax):
        self.xmin, self.xmax = xmin, xmax
        self.ymin, self.ymax = ymin, ymax
        self.zmin, self.zmax = zmin, zmax

    def contains_point(self, x, y, z):
        """判断点是否在建筑物内部(含边界)"""
        return (self.xmin <= x <= self.xmax and
                self.ymin <= y <= self.ymax and
                self.zmin <= z <= self.zmax)

class NoFlyZone:
    """禁飞区:水平多边形(顶点按逆时针排列)+ 高度范围 [zmin, zmax]"""
    def __init__(self, polygon_xy: List[Tuple[float, float]], zmin, zmax):
        self.polygon = polygon_xy
        self.zmin, self.zmax = zmin, zmax

    def contains_point(self, x, y, z):
        """射线法判断水平点是否在多边形内,且高度在禁飞范围内"""
        if not (self.zmin <= z <= self.zmax):
            return False
        inside = False
        n = len(self.polygon)
        j = n - 1
        for i in range(n):
            xi, yi = self.polygon[i]
            xj, yj = self.polygon[j]
            if ((yi > y) != (yj > y)) and (x < (xj - xi) * (y - yi) / (yj - yi) + xi):
                inside = not inside
            j = i
        return inside

class TerrainGrid:
    """地形网格:规则网格存储高程,原点在西南角,网格间距为 cell_size"""
    def __init__(self, origin_x, origin_y, cell_size, elevation_matrix: np.ndarray):
        self.ox, self.oy = origin_x, origin_y
        self.cell_size = cell_size
        self.elev = np.asarray(elevation_matrix, dtype=float)
        self.rows, self.cols = self.elev.shape

    def elevation_at(self, x, y):
        """双线性插值获取任意坐标处的地形高程"""
        # 网格坐标
        gx = (x - self.ox) / self.cell_size
        gy = (y - self.oy) / self.cell_size
        # 边界钳制
        gx = min(max(gx, 0), self.cols - 1.001)
        gy = min(max(gy, 0), self.rows - 1.001)
        x0, y0 = int(np.floor(gx)), int(np.floor(gy))
        x1, y1 = min(x0 + 1, self.cols - 1), min(y0 + 1, self.rows - 1)
        # 双线性权重
        wx, wy = gx - x0, gy - y0
        h00 = self.elev[y0, x0]
        h10 = self.elev[y0, x1]
        h01 = self.elev[y1, x0]
        h11 = self.elev[y1, x1]
        return (h00 * (1 - wx) * (1 - wy) +
                h10 * wx * (1 - wy) +
                h01 * (1 - wx) * wy +
                h11 * wx * wy)

class UrbanAirspace:
    """城市低空环境聚合模型:统一查询入口"""
    def __init__(self, buildings=None, noflyzones=None, terrain=None):
        self.buildings = buildings if buildings else []
        self.noflyzones = noflyzones if noflyzones else []
        self.terrain = terrain

    def is_obstacle(self, x, y, z):
        """判断点是否处于障碍状态(建筑物内部或禁飞区内)"""
        for b in self.buildings:
            if b.contains_point(x, y, z):
                return True
        for nf in self.noflyzones:
            if nf.contains_point(x, y, z):
                return True
        return False

    def ground_elevation(self, x, y):
        """获取地形高程,无地形时返回0"""
        if self.terrain is None:
            return 0.0
        return self.terrain.elevation_at(x, y)

    def is_collision_free(self, x, y, z, safety_margin=1.0):
        """航迹点安全检测:高于地面安全余量且不在障碍内"""
        if z < self.ground_elevation(x, y) + safety_margin:
            return False
        return not self.is_obstacle(x, y, z)

样例环境构建与验证

以下代码构建一个500m×500m范围的示例环境:包含两栋建筑物、一个多边形禁飞区以及一块带起伏的地形网格。随后对若干关键坐标点进行安全检测,验证模型查询功能。

# 构建示例环境
buildings = [
    Building(100, 150, 100, 160, 0, 80),    # 建筑A:80m高
    Building(300, 360, 250, 310, 0, 120),   # 建筑B:120m高
]

# 禁飞区:L形多边形,高度0~200m
nf_polygon = [(200, 200), (280, 200), (280, 260), (240, 260), (240, 230), (200, 230)]
nofly = NoFlyZone(nf_polygon, 0, 200)

# 地形:10m网格,5×5高程矩阵(单位m)
elev_data = np.array([
    [0, 2, 5, 8, 10],
    [1, 3, 6, 9, 12],
    [2, 5, 8, 11, 14],
    [3, 6, 10, 13, 16],
    [5, 8, 12, 15, 18],
])
terrain = TerrainGrid(origin_x=0, origin_y=0, cell_size=10, elevation_matrix=elev_data)

# 聚合模型
airspace = UrbanAirspace(buildings=buildings, noflyzones=[nofly], terrain=terrain)

# 验证点检测
test_points = [
    (120, 130, 50),    # 建筑A内部 → 障碍
    (330, 280, 100),   # 建筑B内部 → 障碍
    (250, 220, 100),   # 禁飞区内部 → 障碍
    (50, 50, 30),      # 空旷区域,高于地形 → 安全
    (50, 50, 5),       # 低于地形+安全余量 → 不安全
    (450, 450, 40),    # 远处空旷区域 → 安全
]

for pt in test_points:
    x, y, z = pt
    safe = airspace.is_collision_free(x, y, z, safety_margin=1.0)
    elev = airspace.ground_elevation(x, y)
    print(f"点({x},{y},{z}) 地面高程={elev:.1f}m → {'安全' if safe else '障碍/不安全'}")

预期输出

点(120,130,50) 地面高程=6.0m → 障碍/不安全
点(330,280,100) 地面高程=13.0m → 障碍/不安全
点(250,220,100) 地面高程=9.0m → 障碍/不安全
点(50,50,30) 地面高程=1.0m → 安全
点(50,50,5) 地面高程=1.0m → 障碍/不安全
点(450,450,40) 地面高程=18.0m → 安全

运行与验证方法

将上述代码保存为 urban_airspace.py,在安装有NumPy的Python 3.8+环境中执行 python urban_airspace.py。程序输出各测试点的安全判定结果。判定标准:前三个点因位于建筑物或禁飞区内必须判定为“障碍/不安全”;点(50,50,5)因低于地面高程1m+安全余量1m必须判定为“障碍/不安全”;其余两点应判定为“安全”。若输出与预期一致,则环境建模与查询接口验证通过。

环境要素可视化

城市低空环境模型俯视图(500m×500m范围) X (m) Y (m) 0 250 500 建筑A 高80m 建筑B 高120m 禁飞区 0~200m P1(120,130,50) P2(330,280,100) P3(250,220,100) P4(50,50,30)安全 P6(450,450,40)安全 建筑物 禁飞区

上图展示了本案例构建的示例环境布局。图中红/橙色矩形为建筑物,蓝色L形区域为禁飞区,背景渐变表示地形高程变化(西南低、东北高)。黄色标记点为障碍测试点,绿色标记点为安全测试点。航迹规划算法可通过 UrbanAirspace.is_collision_free(x, y, z) 接口对任意候选航迹点进行安全判定,并以 ground_elevation() 获取地形约束,从而在后续案例中实现基于该环境模型的航迹搜索与风险规避。