base_agent/uas/route/base_waypoint.py

192 lines
9.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
路径点抽象基类
─────────────────────────────────────────────────────────────────
BaseWaypoint —— 所有路径点的公共基类dataclass + 可扩展)
子类扩展示例:
AirWaypoint 继承 BaseWaypoint增加 fly_over / turn_radius
GroundWaypoint 继承 BaseWaypoint增加 road_type / heading_constraint
WaterWaypoint 继承 BaseWaypoint增加 depth / tide_correction
─────────────────────────────────────────────────────────────────
"""
from __future__ import annotations
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Dict, Optional
# ─────────────────────────────────────────────────────────────────
# 装备域枚举
# ─────────────────────────────────────────────────────────────────
class EquipmentDomain(Enum):
AIR = "air" # 空中(飞行器)
GROUND = "ground" # 陆地(车辆/步兵)
WATER = "water" # 水面(舰艇)
SUBSEA = "subsea" # 水下(潜艇/UUV
SPACE = "space" # 太空(预留)
# ─────────────────────────────────────────────────────────────────
# 路径点约束类型
# ─────────────────────────────────────────────────────────────────
class AltConstraint(Enum):
NONE = "none"
AT = "at"
AT_OR_ABOVE = "at_or_above"
AT_OR_BELOW = "at_or_below"
BETWEEN = "between"
class SpeedConstraint(Enum):
NONE = "none"
AT = "at"
AT_OR_ABOVE = "at_or_above"
AT_OR_BELOW = "at_or_below"
MAX = "max"
# ─────────────────────────────────────────────────────────────────
# 路径点基类
# ─────────────────────────────────────────────────────────────────
@dataclass
class BaseWaypoint:
"""
路径点公共基类
所有域(空/地/水)的路径点均继承此类。
子类通过新增 dataclass 字段扩展特有属性,
无需修改基类。
坐标约定:
latitude —— 纬度WGS-84北正南负
longitude —— 经度WGS-84东正西负
altitude —— 高度/深度MSL水下为负值
"""
# ── 标识 ──────────────────────────────────────────────────────
identifier: str = "" # 路径点标识符
name: str = "" # 全称/描述
domain: EquipmentDomain = EquipmentDomain.AIR
# ── 坐标 ──────────────────────────────────────────────────────
latitude: float = 0.0 # 纬度(度)
longitude: float = 0.0 # 经度(度)
altitude: float = 0.0 # 高度/深度MSL
# ── 运动参数 ──────────────────────────────────────────────────
speed: float = 0.0 # 过点速度m/s
heading: Optional[float] = None # 指定过点航向None=自动计算)
# ── 速度约束 ──────────────────────────────────────────────────
speed_constraint: SpeedConstraint = SpeedConstraint.NONE
speed_limit: Optional[float] = None # 约束速度值m/s
# ── 高度约束 ──────────────────────────────────────────────────
alt_constraint: AltConstraint = AltConstraint.NONE
alt_value: Optional[float] = None # 约束高度(米)
alt_value2: Optional[float] = None # BETWEEN 时的上限
# ── 停留参数 ──────────────────────────────────────────────────
dwell_time: float = 0.0 # 在此点停留时间0=不停留)
# ── 扩展元数据 ────────────────────────────────────────────────
metadata: Dict[str, Any] = field(default_factory=dict)
# ── 属性 ──────────────────────────────────────────────────────
@property
def position(self) -> tuple:
"""返回 (lat, lon, alt) 三元组"""
return (self.latitude, self.longitude, self.altitude)
@property
def has_speed_constraint(self) -> bool:
return self.speed_constraint != SpeedConstraint.NONE
@property
def has_alt_constraint(self) -> bool:
return self.alt_constraint != AltConstraint.NONE
def effective_speed(self, default: float = 0.0) -> float:
"""返回有效过点速度(优先使用 speed否则用 default"""
return self.speed if self.speed > 1e-6 else default
def validate(self) -> list:
"""
校验路径点合法性
:return: 错误信息列表(空列表表示合法)
"""
errors = []
if not (-90 <= self.latitude <= 90):
errors.append(f"纬度越界: {self.latitude}")
if not (-180 <= self.longitude <= 180):
errors.append(f"经度越界: {self.longitude}")
if self.speed < 0:
errors.append(f"速度不能为负: {self.speed}")
if self.dwell_time < 0:
errors.append(f"停留时间不能为负: {self.dwell_time}")
return errors
def copy_with(self, **kwargs) -> "BaseWaypoint":
"""返回修改了指定字段的副本"""
import copy
wp = copy.copy(self)
for k, v in kwargs.items():
setattr(wp, k, v)
return wp
def __str__(self) -> str:
return (f"WP[{self.identifier}]"
f"({self.latitude:.4f}°,{self.longitude:.4f}°,"
f"{self.altitude:.0f}m) "
f"spd={self.speed:.1f}m/s")
# ─────────────────────────────────────────────────────────────────
# 各域路径点子类
# ─────────────────────────────────────────────────────────────────
@dataclass
class AirWaypoint(BaseWaypoint):
"""空中路径点(兼容 ARINC 424"""
domain: EquipmentDomain = EquipmentDomain.AIR
# 转弯参数
fly_over: bool = False # True=飞越点False=Fly-by
turn_radius: float = 0.0 # 过点转弯半径0=自动)
# ARINC 424 字段
path_terminator: str = "TF" # TF/CF/DF/IF/RF/AF
course_angle: Optional[float] = None # 指定进场航道(度)
waypoint_type: str = "FIX" # FIX/AIRPORT/VOR/NDB
@dataclass
class GroundWaypoint(BaseWaypoint):
"""陆地路径点"""
domain: EquipmentDomain = EquipmentDomain.GROUND
# 道路/地形约束
road_type: str = "any" # road/offroad/any
max_slope_deg: float = 30.0 # 最大允许坡度(度)
heading_constraint: Optional[float] = None # 强制过点航向(度)
stop_required: bool = False # 是否必须停车
@dataclass
class WaterWaypoint(BaseWaypoint):
"""水中路径点(水面/水下通用)"""
domain: EquipmentDomain = EquipmentDomain.WATER
# 水下参数(潜艇/UUV
depth: float = 0.0 # 水下深度(米,正值向下)
is_subsurface: bool = False # True=水下False=水面
# 水文约束
min_water_depth: float = 0.0 # 最小水深要求(米)
tide_correction: float = 0.0 # 潮汐修正量(米)