361 lines
18 KiB
Python
361 lines
18 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
装备参数基类 + 各域子类
|
||
─────────────────────────────────────────────────────────────────
|
||
EquipmentParams 公共参数基类(dataclass)
|
||
├── AircraftParams 飞行器专有参数
|
||
├── GroundVehicleParams 地面车辆专有参数
|
||
├── RobotDogParams 机器狗专有参数
|
||
└── RadarParams 雷达专有参数
|
||
|
||
所有参数类支持:
|
||
validate() → 校验合法性,返回错误列表
|
||
to_dict() → 序列化为字典
|
||
from_dict(d) → 从字典反序列化(classmethod)
|
||
copy_with(**kwargs) → 返回修改了指定字段的副本
|
||
─────────────────────────────────────────────────────────────────
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import copy
|
||
import dataclasses
|
||
import uuid
|
||
from dataclasses import dataclass, field
|
||
from enum import Enum
|
||
from typing import Any, Dict, List, Optional, Type, TypeVar
|
||
|
||
from uas.model.constants import WAYPOINT_CAPTURE_RADIUS
|
||
from uas.model.sensor_noise import SensorNoiseParams
|
||
from uas.model.wind import WindField
|
||
|
||
T = TypeVar("T", bound="EquipmentParams")
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────────
|
||
# 装备类型枚举
|
||
# ─────────────────────────────────────────────────────────────────
|
||
|
||
class ModelType(Enum):
|
||
AIRCRAFT = "aircraft"
|
||
MANNED_AIRCRAFT = "manned_aircraft"
|
||
UAV = "controller"
|
||
GROUND_VEHICLE = "ground_vehicle"
|
||
ROBOT_DOG = "robot_dog"
|
||
RADAR = "radar"
|
||
CUSTOM = "custom"
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────────
|
||
# 公共参数基类
|
||
# ─────────────────────────────────────────────────────────────────
|
||
|
||
@dataclass
|
||
class EquipmentParams:
|
||
"""
|
||
装备公共参数基类
|
||
|
||
所有装备类型均包含这些字段。
|
||
子类通过新增 dataclass 字段扩展特有参数,无需修改基类。
|
||
"""
|
||
|
||
# ── 标识 ──────────────────────────────────────────────────────
|
||
equipment_id: str = field(
|
||
default_factory=lambda: str(uuid.uuid4())[:8]
|
||
)
|
||
name: str = "未命名装备"
|
||
model_type: ModelType = ModelType.CUSTOM
|
||
description: str = ""
|
||
|
||
# ── 物理参数 ──────────────────────────────────────────────────
|
||
weight_kg: float = 0.0 # 重量(千克)
|
||
|
||
# ── 运动参数 ──────────────────────────────────────────────────
|
||
max_speed: float = 0.0 # 最大速度(m/s)
|
||
min_speed: float = 0.0 # 最小速度(m/s,0=可静止)
|
||
acceleration: float = 0.0 # 最大加速度(m/s²)
|
||
deceleration: float = 0.0 # 最大减速度(m/s²,正值)
|
||
|
||
# ── 扩展元数据 ────────────────────────────────────────────────
|
||
extra: Dict[str, Any] = field(default_factory=dict)
|
||
|
||
# ── 校验 ──────────────────────────────────────────────────────
|
||
|
||
def validate(self) -> List[str]:
|
||
"""校验参数合法性,返回错误信息列表(空=合法)"""
|
||
errors = []
|
||
if self.weight_kg < 0:
|
||
errors.append(f"weight_kg 不能为负: {self.weight_kg}")
|
||
if self.max_speed < 0:
|
||
errors.append(f"max_speed 不能为负: {self.max_speed}")
|
||
if self.min_speed < 0:
|
||
errors.append(f"min_speed 不能为负: {self.min_speed}")
|
||
if self.min_speed > self.max_speed and self.max_speed > 0:
|
||
errors.append(
|
||
f"min_speed({self.min_speed}) > max_speed({self.max_speed})"
|
||
)
|
||
if self.acceleration < 0:
|
||
errors.append(f"acceleration 不能为负: {self.acceleration}")
|
||
if self.deceleration < 0:
|
||
errors.append(f"deceleration 不能为负: {self.deceleration}")
|
||
return errors
|
||
|
||
# ── 序列化 ────────────────────────────────────────────────────
|
||
|
||
def to_dict(self) -> Dict[str, Any]:
|
||
"""序列化为字典(含枚举值转字符串)"""
|
||
d = dataclasses.asdict(self)
|
||
# 枚举转字符串
|
||
if "model_type" in d:
|
||
d["model_type"] = self.model_type.value
|
||
return d
|
||
|
||
@classmethod
|
||
def from_dict(cls: Type[T], d: Dict[str, Any]) -> T:
|
||
"""从字典反序列化"""
|
||
d = dict(d)
|
||
if "model_type" in d and isinstance(d["model_type"], str):
|
||
d["model_type"] = ModelType(d["model_type"])
|
||
# 过滤掉子类不认识的字段
|
||
valid_fields = {f.name for f in dataclasses.fields(cls)}
|
||
filtered = {k: v for k, v in d.items() if k in valid_fields}
|
||
return cls(**filtered)
|
||
|
||
def copy_with(self: T, **kwargs) -> T:
|
||
"""返回修改了指定字段的副本"""
|
||
obj = copy.copy(self)
|
||
for k, v in kwargs.items():
|
||
if hasattr(obj, k):
|
||
setattr(obj, k, v)
|
||
else:
|
||
raise AttributeError(
|
||
f"{self.__class__.__name__} 没有参数字段: {k}"
|
||
)
|
||
return obj
|
||
|
||
def get(self, key: str, default: Any = None) -> Any:
|
||
"""按字段名读取参数值"""
|
||
return getattr(self, key, default)
|
||
|
||
def set(self, key: str, value: Any) -> None:
|
||
"""按字段名设置参数值(运行时动态配置)"""
|
||
if not hasattr(self, key):
|
||
raise AttributeError(
|
||
f"{self.__class__.__name__} 没有参数字段: {key}"
|
||
)
|
||
setattr(self, key, value)
|
||
|
||
def __repr__(self) -> str:
|
||
return (f"{self.__class__.__name__}("
|
||
f"id={self.equipment_id}, "
|
||
f"name={self.name}, "
|
||
f"type={self.model_type.value})")
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────────
|
||
# 飞行器专有参数
|
||
# ─────────────────────────────────────────────────────────────────
|
||
|
||
@dataclass
|
||
class AircraftParams(EquipmentParams):
|
||
"""飞行器专有参数(兼容有人机 / 无人机)"""
|
||
model_type: ModelType = ModelType.AIRCRAFT
|
||
|
||
# ── 飞行包线 ──────────────────────────────────────────────────
|
||
max_altitude: float = 15000.0 # 最大飞行高度(米)
|
||
min_altitude: float = 0.0 # 最小飞行高度(米)
|
||
max_climb_rate: float = 50.0 # 最大爬升率(m/s)
|
||
max_descent_rate: float = 30.0 # 最大下降率(m/s,正值)
|
||
|
||
# ── 机动参数 ──────────────────────────────────────────────────
|
||
min_turn_radius: float = 500.0 # 最小转弯半径(米)
|
||
max_bank_angle: float = 60.0 # 最大坡度角(度)
|
||
max_pitch_angle: float = 30.0 # 最大俯仰角(度)
|
||
|
||
# ── 燃油参数 ──────────────────────────────────────────────────
|
||
fuel_capacity: float = 5000.0 # 油箱容量(升/千克)
|
||
fuel_consumption_rate: float = 10.0 # 巡航油耗(升/秒)
|
||
|
||
# ── 载荷参数 ──────────────────────────────────────────────────
|
||
payload_capacity_kg: float = 0.0 # 最大载荷(千克)
|
||
wingspan_m: float = 0.0 # 翼展(米)
|
||
|
||
# -- 风向
|
||
wind_direction: float = 0.0
|
||
wind_speed: float = 0.0
|
||
|
||
wind_field: Optional[WindField] = WindField()
|
||
sensor_noise: Optional[SensorNoiseParams] = None
|
||
|
||
waypoint_capture: float = WAYPOINT_CAPTURE_RADIUS
|
||
|
||
seed: int = 0
|
||
|
||
def validate(self) -> List[str]:
|
||
errors = super().validate()
|
||
if self.max_altitude < self.min_altitude:
|
||
errors.append(
|
||
f"max_altitude({self.max_altitude}) < "
|
||
f"min_altitude({self.min_altitude})"
|
||
)
|
||
if self.min_turn_radius <= 0:
|
||
errors.append(
|
||
f"min_turn_radius 必须为正: {self.min_turn_radius}"
|
||
)
|
||
if self.max_climb_rate <= 0:
|
||
errors.append(
|
||
f"max_climb_rate 必须为正: {self.max_climb_rate}"
|
||
)
|
||
if self.fuel_capacity < 0:
|
||
errors.append(f"fuel_capacity 不能为负: {self.fuel_capacity}")
|
||
if self.fuel_consumption_rate < 0:
|
||
errors.append(
|
||
f"fuel_consumption_rate 不能为负: {self.fuel_consumption_rate}"
|
||
)
|
||
return errors
|
||
|
||
|
||
@dataclass
|
||
class MannedAircraftParams(AircraftParams):
|
||
"""有人机专有参数(F-22 / J-20 等)"""
|
||
model_type: ModelType = ModelType.MANNED_AIRCRAFT
|
||
|
||
# ── 人员参数 ──────────────────────────────────────────────────
|
||
crew_count: int = 1 # 机组人数
|
||
max_g_force: float = 9.0 # 最大过载(g)
|
||
ejection_seat: bool = True # 是否有弹射座椅
|
||
|
||
# ── 航电参数 ──────────────────────────────────────────────────
|
||
radar_range_km: float = 200.0 # 机载雷达探测距离(km)
|
||
irst_range_km: float = 90.0 # 红外搜索跟踪距离(km)
|
||
rwr_enabled: bool = True # 雷达告警接收机
|
||
|
||
|
||
@dataclass
|
||
class UAVParams(AircraftParams):
|
||
"""无人机专有参数"""
|
||
model_type: ModelType = ModelType.UAV
|
||
|
||
# ── 任务参数 ──────────────────────────────────────────────────
|
||
endurance_hours: float = 24.0 # 续航时间(小时)
|
||
communication_range: float = 500_000.0 # 通信距离(米)
|
||
autonomous_level: int = 3 # 自主等级(1~5)
|
||
return_on_lost_link: bool = True # 失联自动返航
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────────
|
||
# 地面车辆专有参数
|
||
# ─────────────────────────────────────────────────────────────────
|
||
|
||
@dataclass
|
||
class GroundVehicleParams(EquipmentParams):
|
||
"""地面车辆专有参数"""
|
||
model_type: ModelType = ModelType.GROUND_VEHICLE
|
||
|
||
# ── 地形参数 ──────────────────────────────────────────────────
|
||
max_slope_deg: float = 30.0 # 最大爬坡角度(度)
|
||
ground_clearance_m: float = 0.3 # 最小离地间隙(米)
|
||
max_ford_depth_m: float = 0.8 # 最大涉水深度(米)
|
||
|
||
# ── 机动参数 ──────────────────────────────────────────────────
|
||
min_turn_radius: float = 5.0 # 最小转弯半径(米)
|
||
track_width_m: float = 2.5 # 车辆宽度(米)
|
||
length_m: float = 5.0 # 车辆长度(米)
|
||
|
||
# ── 动力参数 ──────────────────────────────────────────────────
|
||
fuel_capacity_l: float = 300.0 # 燃油容量(升)
|
||
fuel_consumption_lph: float = 20.0 # 油耗(升/小时)
|
||
|
||
def validate(self) -> List[str]:
|
||
errors = super().validate()
|
||
if self.max_slope_deg <= 0 or self.max_slope_deg > 90:
|
||
errors.append(
|
||
f"max_slope_deg 越界: {self.max_slope_deg}"
|
||
)
|
||
if self.min_turn_radius <= 0:
|
||
errors.append(
|
||
f"min_turn_radius 必须为正: {self.min_turn_radius}"
|
||
)
|
||
return errors
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────────
|
||
# 机器狗专有参数
|
||
# ─────────────────────────────────────────────────────────────────
|
||
|
||
@dataclass
|
||
class RobotDogParams(EquipmentParams):
|
||
"""机器狗专有参数"""
|
||
model_type: ModelType = ModelType.ROBOT_DOG
|
||
|
||
# ── 腿部参数 ──────────────────────────────────────────────────
|
||
leg_count: int = 4 # 腿数
|
||
max_step_height_m: float = 0.3 # 最大步高(米)
|
||
stride_length_m: float = 0.5 # 步幅(米)
|
||
max_slope_deg: float = 45.0 # 最大爬坡角度(度)
|
||
|
||
# ── 负载参数 ──────────────────────────────────────────────────
|
||
payload_kg: float = 20.0 # 最大负载(千克)
|
||
battery_capacity_wh: float = 1000.0 # 电池容量(Wh)
|
||
power_consumption_w: float = 500.0 # 运行功耗(W)
|
||
|
||
# ── 感知参数 ──────────────────────────────────────────────────
|
||
lidar_range_m: float = 50.0 # 激光雷达探测距离(米)
|
||
camera_fov_deg: float = 120.0 # 摄像头视场角(度)
|
||
|
||
def validate(self) -> List[str]:
|
||
errors = super().validate()
|
||
if self.leg_count not in (4, 6):
|
||
errors.append(f"leg_count 应为 4 或 6: {self.leg_count}")
|
||
if self.max_step_height_m <= 0:
|
||
errors.append(
|
||
f"max_step_height_m 必须为正: {self.max_step_height_m}"
|
||
)
|
||
if self.payload_kg < 0:
|
||
errors.append(f"payload_kg 不能为负: {self.payload_kg}")
|
||
return errors
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────────
|
||
# 雷达专有参数
|
||
# ─────────────────────────────────────────────────────────────────
|
||
|
||
@dataclass
|
||
class RadarParams(EquipmentParams):
|
||
"""雷达专有参数"""
|
||
model_type: ModelType = ModelType.RADAR
|
||
|
||
# ── 探测参数 ──────────────────────────────────────────────────
|
||
detection_range: float = 300_000.0 # 最大探测距离(米)
|
||
min_detection_range: float = 100.0 # 最小探测距离(米)
|
||
altitude_coverage: float = 30_000.0 # 高度覆盖(米)
|
||
|
||
# ── 扫描参数 ──────────────────────────────────────────────────
|
||
scan_angle_deg: float = 360.0 # 扫描角度范围(度)
|
||
scan_rate_deg_s: float = 6.0 # 扫描速率(度/秒)
|
||
beam_width_deg: float = 1.5 # 波束宽度(度)
|
||
|
||
# ── 电磁参数 ──────────────────────────────────────────────────
|
||
frequency_band: str = "X" # 频段(X/S/L/C/Ka)
|
||
peak_power_kw: float = 100.0 # 峰值功率(kW)
|
||
pulse_width_us: float = 1.0 # 脉冲宽度(μs)
|
||
|
||
# ── 跟踪参数 ──────────────────────────────────────────────────
|
||
max_track_targets: int = 100 # 最大同时跟踪目标数
|
||
track_update_rate: float = 1.0 # 跟踪更新率(Hz)
|
||
|
||
def validate(self) -> List[str]:
|
||
errors = super().validate()
|
||
if self.detection_range <= 0:
|
||
errors.append(
|
||
f"detection_range 必须为正: {self.detection_range}"
|
||
)
|
||
if self.scan_rate_deg_s <= 0:
|
||
errors.append(
|
||
f"scan_rate_deg_s 必须为正: {self.scan_rate_deg_s}"
|
||
)
|
||
if self.frequency_band not in ("X", "S", "L", "C", "Ka", "Ku"):
|
||
errors.append(
|
||
f"未知频段: {self.frequency_band}"
|
||
)
|
||
return errors |