base_agent/uas/route/base_segment.py

268 lines
12 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 -*-
"""
航段抽象基类
─────────────────────────────────────────────────────────────────
BaseSegment —— 所有直线/曲线航段的公共抽象基类
BaseTransition —— 两段之间过渡段(转弯/机动)的抽象基类
继承体系:
BaseSegment
├── AirSegment 空中直线段(适配 RouteSegment
├── GroundSegment 陆地直线段
└── WaterSegment 水中直线段
BaseTransition
├── AirTurnSegment 空中转弯圆弧段(适配 TurnSegment
├── GroundTurnSegment 陆地转弯段
└── WaterTurnSegment 水中机动段
─────────────────────────────────────────────────────────────────
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Dict, TYPE_CHECKING
from uas.route.base_params import BaseParams
from uas.route.base_waypoint import BaseWaypoint, EquipmentDomain
if TYPE_CHECKING:
pass
# ─────────────────────────────────────────────────────────────────
# 航段类型枚举
# ─────────────────────────────────────────────────────────────────
class SegmentKind(Enum):
STRAIGHT = "straight" # 直线段
TURN = "turn" # 转弯/圆弧段
HOLD = "hold" # 等待/盘旋段
APPROACH = "approach" # 进近段
MANEUVER = "maneuver" # 机动段(水下/地面)
CUSTOM = "custom" # 自定义
# ─────────────────────────────────────────────────────────────────
# 航段抽象基类
# ─────────────────────────────────────────────────────────────────
@dataclass
class BaseSegment(ABC):
"""
航段公共抽象基类
每段代表航线上一个连续的运动片段,
包含起止坐标、时间区间、速度/方位等核心参数。
子类必须实现:
query(t) —— 查询段内 t 时刻的装备状态
interpolate(ratio)—— 按比例插值0.0=起点1.0=终点)
validate() —— 校验航段合法性
"""
# ── 逻辑路径点引用 ────────────────────────────────────────────
wp_from: BaseWaypoint # 起始逻辑路径点
wp_to: BaseWaypoint # 终止逻辑路径点
# ── 实际飞行起终点坐标 ────────────────────────────────────────
start_lat: float # 实际起点纬度(度)
start_lon: float # 实际起点经度(度)
start_alt: float # 实际起点高度(米)
end_lat: float # 实际终点纬度(度)
end_lon: float # 实际终点经度(度)
end_alt: float # 实际终点高度(米)
# ── 几何参数 ──────────────────────────────────────────────────
distance: float # 水平距离(米)
bearing: float # 初始方位角(度,真北顺时针)
# ── 时间参数 ──────────────────────────────────────────────────
t_start: float # 航段起始时刻(秒)
t_end: float # 航段结束时刻(秒)
duration: float # 飞行时长(秒)
# ── 速度参数 ──────────────────────────────────────────────────
v_start: float # 起点速度m/s
v_end: float # 终点速度m/s
acceleration: float = 0.0 # 匀加速度m/s²
# ── 域与类型 ──────────────────────────────────────────────────
domain: EquipmentDomain = EquipmentDomain.AIR
kind: SegmentKind = SegmentKind.STRAIGHT
# ── 扩展元数据 ────────────────────────────────────────────────
metadata: Dict[str, Any] = field(default_factory=dict)
# ── 衍生属性 ──────────────────────────────────────────────────
@property
def avg_speed(self) -> float:
return (self.v_start + self.v_end) / 2.0
@property
def vertical_distance(self) -> float:
return self.end_alt - self.start_alt
@property
def vertical_speed(self) -> float:
if self.duration < 1e-6:
return 0.0
return self.vertical_distance / self.duration
@property
def is_climb(self) -> bool:
return self.vertical_distance > 10
@property
def is_descent(self) -> bool:
return self.vertical_distance < -10
@property
def is_level(self) -> bool:
return abs(self.vertical_distance) <= 10
@property
def start_position(self) -> tuple:
return (self.start_lat, self.start_lon, self.start_alt)
@property
def end_position(self) -> tuple:
return (self.end_lat, self.end_lon, self.end_alt)
def contains_time(self, t: float) -> bool:
"""判断时刻 t 是否落在本段时间范围内"""
return self.t_start <= t <= self.t_end
# ── 抽象接口 ──────────────────────────────────────────────────
@abstractmethod
def query(self, t: float) -> BaseParams:
"""
查询段内 t 时刻的装备状态
:param t: 查询时刻(秒)
:return: BaseParams 或其子类实例
"""
...
@abstractmethod
def interpolate(self, ratio: float) -> BaseParams:
"""
按比例插值
:param ratio: 0.0=起点1.0=终点
:return: BaseParams 或其子类实例
"""
...
@abstractmethod
def validate(self) -> list:
"""
校验航段合法性
:return: 错误信息列表(空列表=合法)
"""
...
def __str__(self) -> str:
phase = "爬升" if self.is_climb else ("下降" if self.is_descent else "平飞")
return (f"{self.__class__.__name__}["
f"{self.wp_from.identifier}{self.wp_to.identifier}] "
f"{self.distance/1000:.2f}km/{self.bearing:.1f}°/"
f"{self.duration:.1f}s/{phase}]")
# ─────────────────────────────────────────────────────────────────
# 过渡段抽象基类(转弯/机动)
# ─────────────────────────────────────────────────────────────────
@dataclass
class BaseTransition(ABC):
"""
两段之间过渡段的公共抽象基类
过渡段连接两个直线段,描述转弯、机动等非直线运动。
典型实例:
AirTurnSegment —— 飞机 Fly-by 圆弧转弯
GroundTurnSegment—— 车辆转弯段
WaterTurnSegment —— 舰艇回转机动
子类必须实现 query(t) 和 validate()。
"""
# ── 入口/出口参数 ─────────────────────────────────────────────
entry_lat: float # 入口纬度(度)
entry_lon: float # 入口经度(度)
entry_alt: float # 入口高度(米)
entry_heading: float # 入口航向(度)
exit_heading: float # 出口航向(度)
# ── 时间参数 ──────────────────────────────────────────────────
t_start: float # 过渡段起始时刻(秒)
duration: float = 0.0 # 过渡段时长(秒,子类计算后填充)
# ── 运动参数 ──────────────────────────────────────────────────
speed: float = 0.0 # 过渡段速度m/s
altitude: float = 0.0 # 过渡段高度(米)
# ── 域与类型 ──────────────────────────────────────────────────
domain: EquipmentDomain = EquipmentDomain.AIR
kind: SegmentKind = SegmentKind.TURN
# ── 扩展元数据 ────────────────────────────────────────────────
metadata: Dict[str, Any] = field(default_factory=dict)
# ── 衍生属性 ──────────────────────────────────────────────────
@property
def t_end(self) -> float:
return self.t_start + self.duration
@property
def turn_angle(self) -> float:
"""转弯角(度,右转为正,左转为负)"""
diff = self.exit_heading - self.entry_heading
while diff > 180: diff -= 360
while diff < -180: diff += 360
return diff
@property
def turn_direction(self) -> int:
"""转弯方向:+1=右转,-1=左转0=直线"""
a = self.turn_angle
if a > 0.5: return 1
elif a < -0.5: return -1
return 0
def contains_time(self, t: float) -> bool:
return self.t_start <= t <= self.t_end
# ── 抽象接口 ──────────────────────────────────────────────────
@abstractmethod
def query(self, t: float) -> BaseParams:
"""查询过渡段内 t 时刻的装备状态"""
...
@abstractmethod
def validate(self) -> list:
"""校验过渡段合法性"""
...
@property
@abstractmethod
def exit_lat(self) -> float:
"""出口纬度(子类计算)"""
...
@property
@abstractmethod
def exit_lon(self) -> float:
"""出口经度(子类计算)"""
...
def __str__(self) -> str:
dir_str = "右转" if self.turn_direction > 0 else "左转"
return (f"{self.__class__.__name__}["
f"{dir_str}{abs(self.turn_angle):.1f}° "
f"t={self.t_start:.1f}~{self.t_end:.1f}s]")