205 lines
6.4 KiB
Python
205 lines
6.4 KiB
Python
from pydantic import BaseModel, Field
|
||
from typing import Dict, Any, Optional, Literal
|
||
from datetime import datetime
|
||
from voice_drone.core.configuration import (
|
||
TAKEOFF_CONFIG,
|
||
LAND_CONFIG,
|
||
FOLLOW_CONFIG,
|
||
FORWARD_CONFIG,
|
||
BACKWARD_CONFIG,
|
||
LEFT_CONFIG,
|
||
RIGHT_CONFIG,
|
||
UP_CONFIG,
|
||
DOWN_CONFIG,
|
||
HOVER_CONFIG,
|
||
RETURN_HOME_CONFIG,
|
||
)
|
||
import warnings
|
||
warnings.filterwarnings("ignore")
|
||
|
||
|
||
class CommandParams(BaseModel):
|
||
"""
|
||
命令参数
|
||
"""
|
||
distance: Optional[float] = Field(
|
||
None,
|
||
description="飞行距离,单位:米(m),必须大于等于0(land/hover 可以为0)",
|
||
ge=0
|
||
)
|
||
speed: Optional[float] = Field(
|
||
None,
|
||
description="飞行速度,单位:米每秒(m/s),必须大于等于0(land/hover 可以为0)",
|
||
ge=0
|
||
)
|
||
duration: Optional[float] = Field(
|
||
None,
|
||
description="飞行持续时间,单位:秒(s),必须大于0",
|
||
gt=0
|
||
)
|
||
|
||
class Command(BaseModel):
|
||
"""
|
||
无人机控制命令
|
||
"""
|
||
command: Literal[
|
||
"takeoff",
|
||
"follow",
|
||
"forward",
|
||
"backward",
|
||
"left",
|
||
"right",
|
||
"up",
|
||
"down",
|
||
"hover",
|
||
"land",
|
||
"return_home",
|
||
] = Field(
|
||
...,
|
||
description="无人机控制动作: takeoff(起飞), follow(跟随), forward(向前), backward(向后), left(向左), right(向右), up(向上), down(向下), hover(悬停), land(降落), return_home(返航)",
|
||
)
|
||
params: CommandParams = Field(..., description="命令参数")
|
||
timestamp: str = Field(..., description="命令生成时间戳,ISO 8601 格式(如:2024-01-01T12:00:00.000Z)")
|
||
sequence_id: int = Field(..., description="命令序列号,用于保证命令顺序和去重")
|
||
|
||
# 命令配置映射字典
|
||
_CONFIG_MAP = {
|
||
"takeoff": TAKEOFF_CONFIG,
|
||
"follow": FOLLOW_CONFIG,
|
||
"land": LAND_CONFIG,
|
||
"forward": FORWARD_CONFIG,
|
||
"backward": BACKWARD_CONFIG,
|
||
"left": LEFT_CONFIG,
|
||
"right": RIGHT_CONFIG,
|
||
"up": UP_CONFIG,
|
||
"down": DOWN_CONFIG,
|
||
"hover": HOVER_CONFIG,
|
||
"return_home": RETURN_HOME_CONFIG,
|
||
}
|
||
|
||
# 创建命令
|
||
@classmethod
|
||
def create(
|
||
cls,
|
||
command: str,
|
||
sequence_id: int,
|
||
distance: Optional[float] = None,
|
||
speed: Optional[float] = None,
|
||
duration: Optional[float] = None
|
||
) -> "Command":
|
||
return cls(
|
||
command=command,
|
||
params=CommandParams(distance=distance, speed=speed, duration=duration),
|
||
timestamp=datetime.utcnow().isoformat() + "Z",
|
||
sequence_id=sequence_id,
|
||
)
|
||
|
||
def _get_default_config(self):
|
||
"""获取当前命令的默认配置"""
|
||
return self._CONFIG_MAP.get(self.command)
|
||
|
||
# 填充默认值
|
||
def fill_defaults(self) -> None:
|
||
"""填充缺失的参数值"""
|
||
# 如果所有参数都已提供,直接返回
|
||
if (self.params.distance is not None and
|
||
self.params.speed is not None and
|
||
self.params.duration is not None):
|
||
return
|
||
|
||
# 如果有缺失的参数,调用智能填充方法
|
||
self._fill_smart_params()
|
||
|
||
def _fill_smart_params(self):
|
||
"""智能填充缺失的参数值"""
|
||
default = self._get_default_config()
|
||
if default is None:
|
||
# 若命令未知,则直接返回不填充
|
||
return
|
||
|
||
d = self.params.distance
|
||
s = self.params.speed
|
||
t = self.params.duration
|
||
|
||
# 统计 None 个数
|
||
none_cnt = sum(x is None for x in [d, s, t])
|
||
|
||
# 三个都为None,直接填默认值
|
||
if none_cnt == 3:
|
||
self.params.distance = default["distance"]
|
||
self.params.speed = default["speed"]
|
||
self.params.duration = default["duration"]
|
||
return
|
||
|
||
# 只有一个参数有值的情况
|
||
if none_cnt == 2:
|
||
if s is not None and d is None and t is None:
|
||
# 仅速度:使用默认持续时间,计算距离
|
||
self.params.duration = default["duration"]
|
||
self.params.distance = s * self.params.duration
|
||
return
|
||
|
||
if t is not None and d is None and s is None:
|
||
# 仅持续时间:使用默认速度,计算距离
|
||
self.params.speed = default["speed"]
|
||
self.params.distance = self.params.speed * t
|
||
return
|
||
|
||
if d is not None and s is None and t is None:
|
||
# 仅距离:使用默认速度,计算持续时间
|
||
self.params.speed = default["speed"]
|
||
# 防止除以0
|
||
if self.params.speed == 0:
|
||
self.params.duration = 0
|
||
else:
|
||
self.params.duration = d / self.params.speed
|
||
return
|
||
|
||
# 两个参数有值,一个None,自动计算缺失的参数
|
||
if none_cnt == 1:
|
||
if d is None and s is not None and t is not None:
|
||
# 缺失距离:distance = speed * duration
|
||
self.params.distance = s * t
|
||
return
|
||
|
||
if s is None and d is not None and t is not None:
|
||
# 缺失速度:speed = distance / duration
|
||
if t == 0:
|
||
self.params.speed = 0
|
||
else:
|
||
self.params.speed = d / t
|
||
return
|
||
|
||
if t is None and d is not None and s is not None:
|
||
# 缺失持续时间:duration = distance / speed
|
||
if s == 0:
|
||
self.params.duration = 0
|
||
else:
|
||
self.params.duration = d / s
|
||
return
|
||
|
||
|
||
# 转换为字典
|
||
def to_dict(self) -> dict:
|
||
|
||
result = {
|
||
"command": self.command,
|
||
"params": {},
|
||
"timestamp": self.timestamp,
|
||
"sequence_id": self.sequence_id
|
||
}
|
||
|
||
if self.params.distance is None or self.params.speed is None or self.params.duration is None:
|
||
self.fill_defaults()
|
||
|
||
result["params"]["distance"] = self.params.distance
|
||
result["params"]["speed"] = self.params.speed
|
||
result["params"]["duration"] = self.params.duration
|
||
|
||
return result
|
||
|
||
|
||
|
||
if __name__ == "__main__":
|
||
command = Command.create("takeoff", 1, speed=2)
|
||
print(command.to_dict()) |