2026-04-14 09:54:26 +08:00

205 lines
6.4 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.

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())