#!/usr/bin/env python3 """ 指令發送模組 負責將目標位置轉成具體的通訊指令發送到飛控 現階段: MavlinkSender (直接 pymavlink 發送) 未來: 替換為 ROS2Sender (發到 ROS2 topic,由 fc_network_adapter 轉發) """ from abc import ABC, abstractmethod from pymavlink import mavutil class CommandSender(ABC): """指令發送抽象介面""" @abstractmethod def send_position_global(self, sysid, lat, lon, alt): """ 發送全球座標位置指令 Args: sysid: 目標無人機的 MAVLink system ID lat: 緯度 (度) lon: 經度 (度) alt: 高度 (公尺) """ pass @abstractmethod def close(self): """關閉連線""" pass class MavlinkSender(CommandSender): """ MAVLink 直接發送 (驗證階段用) 透過 SET_POSITION_TARGET_GLOBAL_INT 發送目標位置 使用方式: sender = MavlinkSender("udpout:127.0.0.1:14550") sender.send_position_global(sysid=1, lat=24.123, lon=120.567, alt=10.0) """ # type_mask: 只使用位置 (lat, lon, alt) # 忽略速度 (bit 3,4,5)、加速度 (bit 6,7,8)、yaw (bit 10)、yaw_rate (bit 11) TYPE_MASK_POSITION_ONLY = ( 0b0000_1101_1111_1000 # = 0x0DF8 ) def __init__(self, connection_string="udpout:127.0.0.1:14550"): """ Args: connection_string: MAVLink 連線字串 SITL 範例: "udpout:127.0.0.1:14550" """ self.connection_string = connection_string self.mav = mavutil.mavlink_connection(connection_string) print(f"MavlinkSender 已建立連線: {connection_string}") def send_position_global(self, sysid, lat, lon, alt): """ 發送 SET_POSITION_TARGET_GLOBAL_INT 注意: - coordinate_frame 使用 MAV_FRAME_GLOBAL_RELATIVE_ALT_INT 高度是相對起飛點的高度 (公尺) - 如果 FormationPlanner 產出的 alt 是 AMSL 絕對高度, 需要在外部先減去起飛點高度再傳入 """ self.mav.mav.set_position_target_global_int_send( 0, # time_boot_ms (不使用) sysid, # target_system 1, # target_component (autopilot) mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT, self.TYPE_MASK_POSITION_ONLY, int(lat * 1e7), # lat_int int(lon * 1e7), # lon_int float(alt), # alt 0, 0, 0, # vx, vy, vz (忽略) 0, 0, 0, # afx, afy, afz (忽略) 0, 0 # yaw, yaw_rate (忽略) ) def close(self): """關閉 MAVLink 連線""" if self.mav: self.mav.close() self.mav = None print("MavlinkSender 已關閉")