Files
code-test-drone/utils/visual_track_control/mavsdk_gateway.py
T
2026-09-02 15:13:13 +08:00

99 lines
4.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.
"""MAVSDK offboard 网关 —— 控制意图 → MAVSDK 设定点(语义映射)。
对应 C++ 参考实现 reference-c++/visual_track_control/src/mavlink_command_gateway.cpp
(C++ 版手工编码 MAVLink ID 84 字节流;Python 版改用 MAVSDK offboard API,
按 A2 语义映射三分支):
速率式(yaw_rate_dps ≠ 0)→ set_velocity_body(机体前向, 0, vz, yaw_rate)
位置式(yaw_deg ≠ 0 且角速率为零)→ set_velocity_ned(NED 速度 + 绝对 yaw)
两者均零 → set_velocity_body 配零角速率(保持当前航向,A2 回归语义)
刹车 → set_velocity_body(0, 0, 0, 0)(MAVSDK 无刹车掩码的行为等效适配)
降级保持(DEGRADED_HOLD)不发送任何指令,由调用方(胶水层)判断模式。
MAVSDK Python API 为 asyncio 异步接口,本网关方法均为协程;传输异常
捕获后返回 False(与 C++ 传输失败传播一致),不向外抛异常。
"""
import logging
from typing import Any, Optional
from .types import ControlOutput
logger = logging.getLogger(__name__)
class MavsdkCommandGateway:
"""drone:mavsdk System(仅取 .offboard 属性,duck-typed 便于注入 mock)。"""
def __init__(self, drone: Any) -> None:
self._offboard = drone.offboard
self._started = False
async def start(self) -> bool:
"""进入 offboard:PX4 要求先流式发送首条设定点再请求启动。"""
try:
await self._offboard.set_velocity_body(
self._velocity_body(0.0, 0.0, 0.0, 0.0))
await self._offboard.start()
except Exception as exc: # OffboardError / 连接异常统一视为启动失败
logger.warning("[MavsdkGateway] offboard 启动失败: %s", exc)
return False
self._started = True
logger.info("[MavsdkGateway] offboard 已启动")
return True
async def send_velocity_heading(self, output: ControlOutput,
time_boot_ms: Optional[int] = None) -> bool:
try:
if output.yaw_rate_dps != 0.0:
# 速率式:机体系速度 + 偏航角速度(NED 分量不再使用)
await self._offboard.set_velocity_body(
self._velocity_body(output.vx_body_mps, 0.0, output.vz_mps,
output.yaw_rate_dps))
elif output.yaw_deg != 0.0:
# 位置式:NED 速度 + 绝对航向(A2:绝对航向合成在控制律完成)
await self._offboard.set_velocity_ned(
self._velocity_ned(output.vx_mps, output.vy_mps,
output.vz_mps, output.yaw_deg))
else:
# 两者均零:机体系速度 + 零角速率 = 保持当前航向(A2 回归语义)
await self._offboard.set_velocity_body(
self._velocity_body(output.vx_body_mps, 0.0, output.vz_mps,
0.0))
except Exception as exc:
logger.warning("[MavsdkGateway] 速度航向指令发送失败: %s", exc)
return False
return True
async def send_brake_hover(self, time_boot_ms: Optional[int] = None) -> bool:
try:
await self._offboard.set_velocity_body(
self._velocity_body(0.0, 0.0, 0.0, 0.0))
except Exception as exc:
logger.warning("[MavsdkGateway] 刹车指令发送失败: %s", exc)
return False
return True
async def stop(self) -> None:
try:
await self._offboard.stop()
except Exception as exc:
logger.warning("[MavsdkGateway] offboard 停止失败: %s", exc)
self._started = False
logger.info("[MavsdkGateway] offboard 已停止")
@staticmethod
def _velocity_body(forward: float, right: float, down: float,
yawspeed: float) -> Any:
from mavsdk import offboard as _mavsdk_offboard
return _mavsdk_offboard.VelocityBodyYawspeed(
forward_m_s=forward, right_m_s=right, down_m_s=down,
yawspeed_deg_s=yawspeed)
@staticmethod
def _velocity_ned(north: float, east: float, down: float,
yaw: float) -> Any:
from mavsdk import offboard as _mavsdk_offboard
return _mavsdk_offboard.VelocityNedYaw(
north_m_s=north, east_m_s=east, down_m_s=down, yaw_deg=yaw)