2 Commits
Author SHA1 Message Date
ken910606 fc2c642d6c Force sync local master to remote ken 2026-04-14 14:37:56 +08:00
ken910606 2c38376f9c Update master with local 2.0.5 2026-04-14 14:10:57 +08:00
7 changed files with 136 additions and 27 deletions
-6
View File
@@ -26,9 +26,3 @@ Makefile
**/*.pyc **/*.pyc
**/*.pyo **/*.pyo
**/.cursor/ **/.cursor/
CLAUDE.md
# 本地筆記與硬體 dump
my_env_notes.md
notes/
**/eeprom.bin
+5
View File
@@ -97,3 +97,8 @@ python gui.py
N. logs 是執行時期的記錄檔 N. logs 是執行時期的記錄檔
=== ===
簡述0328
主要是透過test_transform這隻程式執行fdm_switch_example_one_with_remote_forwarding這個副函式,透過UDP將封包轉接到Matlab,並在Matlab進行演算法迭代後,打包JSON檔,並再次透過port口傳回封包,傳回來的封包會在close_loop這隻程式被解析,並提取其中有關velocity的資料儲存成變數,以mavlink的方式打入Ardupilot,以實現封閉迴路的構思。
Binary file not shown.
+14 -20
View File
@@ -602,7 +602,7 @@ class DroneMonitor(Node):
# ================================================================================ # ================================================================================
async def set_mode(self, drone_id, mode_name): async def set_mode(self, drone_id, mode_name):
"""使用 CommandLongClient 切換無人機飛行模式""" """使用 CommandLongClient 切換無人機飛行模式(使用非阻塞的 async 方法)"""
try: try:
# 解析 drone_id 提取 sysid # 解析 drone_id 提取 sysid
parts = drone_id.split('_') parts = drone_id.split('_')
@@ -619,30 +619,24 @@ class DroneMonitor(Node):
print(f"\n📢 [SET_MODE] {drone_id} → {mode_name} (custom_mode={custom_mode})") print(f"\n📢 [SET_MODE] {drone_id} → {mode_name} (custom_mode={custom_mode})")
# 在線程池中調用 CommandLongClient if not self.command_long_client:
loop = asyncio.get_event_loop() print(f"❌ [SET_MODE] CommandLongClient 未初始化")
executor = ThreadPoolExecutor(max_workers=1) return False
def _call_set_mode(): # 直接調用 async 方法,無需線程池(避免嵌套執行器衝突)
client = CommandLongClient() if CommandLongClient else None result = await self.command_long_client.change_mode_async(
if not client: target_sysid=sysid,
return False custom_mode=float(custom_mode),
result = client.change_mode( target_compid=0,
target_sysid=sysid, base_mode=1.0,
custom_mode=float(custom_mode), timeout_sec=2.0,
target_compid=0, )
base_mode=1.0,
timeout_sec=2.0,
)
client.destroy_node()
return result.success if result else False
result = await loop.run_in_executor(executor, _call_set_mode) if result and result.success:
if result:
print(f"✅ [SET_MODE] 模式切換成功") print(f"✅ [SET_MODE] 模式切換成功")
return True return True
else: else:
print(f"❌ [SET_MODE] 模式切換失敗") print(f"❌ [SET_MODE] 模式切換失敗 (message={result.message if result else 'None'})")
return False return False
except Exception as e: except Exception as e:
print(f"❌ [SET_MODE] 例外錯誤: {e}") print(f"❌ [SET_MODE] 例外錯誤: {e}")
+1 -1
View File
@@ -33,7 +33,7 @@ from mission_group import (
# ================================================================================ # ================================================================================
class ControlStationUI(QMainWindow): class ControlStationUI(QMainWindow):
VERSION = '2.0.4' VERSION = '2.0.6'
def __init__(self): def __init__(self):
super().__init__() super().__init__()
+116
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional from typing import Optional
import asyncio
import rclpy import rclpy
from rclpy.node import Node from rclpy.node import Node
@@ -185,3 +186,118 @@ class CommandLongClient(Node):
param7=0.0, param7=0.0,
timeout_sec=timeout_sec, timeout_sec=timeout_sec,
) )
# ============================================================================
# 【新增】非阻塞 async 包裝方法(用於 GUI 的非阻塞調用)
# 這些方法在 ThreadPoolExecutor 中運行同步版本,以避免阻塞事件循環
# ============================================================================
async def change_mode_async(
self,
*,
target_sysid: int,
custom_mode: float,
target_compid: int = 0,
base_mode: float = 1.0,
confirmation: int = 0,
timeout_sec: float = DEFAULT_TIMEOUT_SEC,
) -> CommandLongResult:
"""非阻塞 async 版本的 change_mode(在 ThreadPoolExecutor 中運行)"""
from concurrent.futures import ThreadPoolExecutor
loop = asyncio.get_event_loop()
executor = ThreadPoolExecutor(max_workers=1)
return await loop.run_in_executor(
executor,
lambda: self.change_mode(
target_sysid=target_sysid,
custom_mode=custom_mode,
target_compid=target_compid,
base_mode=base_mode,
confirmation=confirmation,
timeout_sec=timeout_sec,
)
)
async def arm_disarm_async(
self,
*,
target_sysid: int,
arm: bool,
target_compid: int = 0,
confirmation: int = 0,
param2: float = 0.0,
timeout_sec: float = DEFAULT_TIMEOUT_SEC,
) -> CommandLongResult:
"""非阻塞 async 版本的 arm_disarm(在 ThreadPoolExecutor 中運行)"""
from concurrent.futures import ThreadPoolExecutor
loop = asyncio.get_event_loop()
executor = ThreadPoolExecutor(max_workers=1)
return await loop.run_in_executor(
executor,
lambda: self.arm_disarm(
target_sysid=target_sysid,
arm=arm,
target_compid=target_compid,
confirmation=confirmation,
param2=param2,
timeout_sec=timeout_sec,
)
)
async def takeoff_async(
self,
*,
target_sysid: int,
altitude_m: float,
target_compid: int = 0,
min_pitch_deg: float = 0.0,
yaw_deg: float = 0.0,
latitude: Optional[float] = None,
longitude: Optional[float] = None,
timeout_sec: float = DEFAULT_TIMEOUT_SEC,
) -> CommandLongResult:
"""非阻塞 async 版本的 takeoff(在 ThreadPoolExecutor 中運行)"""
from concurrent.futures import ThreadPoolExecutor
loop = asyncio.get_event_loop()
executor = ThreadPoolExecutor(max_workers=1)
return await loop.run_in_executor(
executor,
lambda: self.takeoff(
target_sysid=target_sysid,
altitude_m=altitude_m,
target_compid=target_compid,
min_pitch_deg=min_pitch_deg,
yaw_deg=yaw_deg,
latitude=latitude,
longitude=longitude,
timeout_sec=timeout_sec,
)
)
async def land_async(
self,
*,
target_sysid: int,
target_compid: int = 0,
yaw_deg: float = 0.0,
latitude: Optional[float] = None,
longitude: Optional[float] = None,
altitude_m: float = 0.0,
timeout_sec: float = DEFAULT_TIMEOUT_SEC,
) -> CommandLongResult:
"""非阻塞 async 版本的 land(在 ThreadPoolExecutor 中運行)"""
from concurrent.futures import ThreadPoolExecutor
loop = asyncio.get_event_loop()
executor = ThreadPoolExecutor(max_workers=1)
return await loop.run_in_executor(
executor,
lambda: self.land(
target_sysid=target_sysid,
target_compid=target_compid,
yaw_deg=yaw_deg,
latitude=latitude,
longitude=longitude,
altitude_m=altitude_m,
timeout_sec=timeout_sec,
)
)
Binary file not shown.