diff --git a/src/GUI/CHANGES.md b/src/GUI/CHANGES.md deleted file mode 100644 index e116a49..0000000 --- a/src/GUI/CHANGES.md +++ /dev/null @@ -1,405 +0,0 @@ -=============================================================================================== -GUI 集成 fc_network Set Mode 功能 - 完整修改清單 -=============================================================================================== - -項目名稱: AirTrapMine -目標: 在 gui.py 中使用 fc_network_apps 的 change_mode 功能改變無人機飛行模式 -完成日期: 2026年4月7日 - -=============================================================================================== -1. 修改的檔案 -=============================================================================================== - -【1】communication.py - 新增 set_mode 功能 -──────────────────────────────────────────────────────────────────────────────────────────── - -位置: /home/dodo/Downloads/AirTrapMine/src/GUI/communication.py - -修改內容: - - A) 新增導入 (L18-22): - - 從 fc_network_apps 導入 change_mode 函數 - - 使用 try-except 安全導入,以支持 fc_network_apps 未安裝的情況 - - ```python - try: - from fc_network_apps import change_mode - except ImportError: - change_mode = None - ``` - - B) 新增 MODE_MAPPING 常量 (L585-610): - - 將飛行模式名稱映射到 custom_mode 值 - - 基於 ArduCopter 模式定義 - - 包含 20+ 種常用模式 - - ```python - MODE_MAPPING = { - "STABILIZE": 0, - "GUIDED": 4, - # ... 更多模式 - } - ``` - - C) 新增 set_mode() 非同步方法 (L612-685): - - 使用 fc_network_apps 的 change_mode() 函數 - - 解析 drone_id 以提取 sysid - - 查表轉換模式名稱到 custom_mode 值 - - 呼叫 ROS2 service 改變模式 - - 完整的錯誤處理和日誌記錄 - - ```python - async def set_mode(self, drone_id, mode_name): - """使用 fc_network_apps 的 change_mode 函數切換無人機飛行模式""" - # 實現細節... - ``` - -修改影響: - ✅ 無需修改 gui.py 中的使用代碼 - ✅ 自動與現有的 handle_mode_change() 和 _handle_group_mode_change() 配合 - ✅ 完全向後相容 - -=============================================================================================== -2. 新增的文件 -=============================================================================================== - -【2】example_set_mode_usage.py - 使用示例和詳細文檔 -──────────────────────────────────────────────────────────────────────────────────────────── - -位置: /home/dodo/Downloads/AirTrapMine/src/GUI/example_set_mode_usage.py - -內容: - - 詳細的實現說明和原理解釋 - - API 文檔和參數說明 - - 使用流程圖 - - fc_network_apps 集成細節 - - 支援的飛行模式列表 - - 錯誤處理方案 - - 完整的代碼示例 - - 注意事項 - -大小: ~500 行 - -【3】demo_set_mode.py - 可執行的演示腳本 -──────────────────────────────────────────────────────────────────────────────────────────── - -位置: /home/dodo/Downloads/AirTrapMine/src/GUI/demo_set_mode.py - -功能: - $ python3 demo_set_mode.py direct --sysid 1 --mode GUIDED - → 直接使用 fc_network_apps.change_mode() - - $ python3 demo_set_mode.py via-monitor --drone-id s0_1 --mode AUTO - → 通過 DroneMonitor.set_mode() 方法 - - $ python3 demo_set_mode.py group --drones s0_1 s0_2 s0_3 --mode LOITER - → 演示群組模式切換 - -【4】SET_MODE_INTEGRATION.md - 完整的集成指南 -──────────────────────────────────────────────────────────────────────────────────────────── - -位置: /home/dodo/Downloads/AirTrapMine/src/GUI/SET_MODE_INTEGRATION.md - -內容: - - 實現原理詳解 - - GUI 使用流程圖 - - 代碼示例和片段 - - fc_network_apps 實現細節 - - 等效的 ROS2 CLI 命令 - - 支援的飛行模式表 - - 使用 drone_id 的說明 - - 完整的使用示例 - - 總結和相關文件引用 - -【5】IMPLEMENTATION_SUMMARY.md - 實現總結 -──────────────────────────────────────────────────────────────────────────────────────────── - -位置: /home/dodo/Downloads/AirTrapMine/src/GUI/IMPLEMENTATION_SUMMARY.md - -內容: - - 修改詳情 - - 現有代碼的兼容性 - - 使用流程(單無人機和群組) - - 新增文件說明 - - 相關的 fc_network_apps 代碼 - - 測試檢查清單 - - 使用示例 - - 支援的飛行模式參考 - - 架構圖 - - 調試技巧 - -【6】README_SET_MODE.md - 快速參考指南 -──────────────────────────────────────────────────────────────────────────────────────────── - -位置: /home/dodo/Downloads/AirTrapMine/src/GUI/README_SET_MODE.md - -內容: - - 在 GUI 中使用 Set Mode 的最快方式 - - 現有代碼說明 - - 實現位置 - - 模式支援列表 - - API 參考 - - 相關文件索引 - - 快速開始步驟 - - 常見問題解答 - - 設計要點 - - 流程圖 - - 重要提示 - -=============================================================================================== -3. 現有代碼使用情況 -=============================================================================================== - -gui.py 中的使用代碼(無需修改): - -【位置 1】 L391-401: handle_mode_change() 方法 -──────────────────────────────────────────────────────────────────────────────────────────── - - def handle_mode_change(self, drone_id): - # 從 active group 的 mode_combo 讀取模式 - group = self._get_active_group() - if group: - panel = self.group_panels.get(group.group_id) - mode = panel.mode_combo.currentText() if panel else "GUIDED" - else: - mode = "GUIDED" - loop = asyncio.get_event_loop() - future = self.monitor.set_mode(drone_id, mode) # ✅ 使用新的 set_mode() - loop.create_task(self.handle_service_response(future, f"切換模式 {mode} {drone_id}")) - -【位置 2】 L656-664: _handle_group_mode_change() 方法 -──────────────────────────────────────────────────────────────────────────────────────────── - - def _handle_group_mode_change(self, group_id, mode): - """切換群組內所有無人機的飛行模式""" - group = self.mission_groups.get(group_id) - if not group: - return - loop = asyncio.get_event_loop() - for drone_id in group.drone_ids: - future = self.monitor.set_mode(drone_id, mode) # ✅ 使用新的 set_mode() - loop.create_task(self.handle_service_response(future, f"{drone_id} 切換 {mode}")) - -【位置 3】 L271, L501: 信號連接 -──────────────────────────────────────────────────────────────────────────────────────────── - - panel.mode_change_requested.connect(self.handle_mode_change) - panel.mode_change_requested.connect(self._handle_group_mode_change) - -狀態: ✅ 無需修改,自動與新的 set_mode() 方法配合 - -=============================================================================================== -4. 技術細節 -=============================================================================================== - -【API 簽名】 - - async def set_mode(self, drone_id: str, mode_name: str) -> bool: - """ - 使用 fc_network_apps 的 change_mode 函數切換無人機飛行模式 - - 參數: - drone_id (str): 無人機ID,格式 "s{socket_id}_{sysid}" (e.g., "s0_1") - mode_name (str): 模式名稱 (e.g., "GUIDED", "AUTO") - - 返回: - bool: 模式切換成功返回 True,失敗返回 False - """ - -【支援的模式】 - - STABILIZE (0), ACRO (1), ALT_HOLD (2), AUTO (3), GUIDED (4), LOITER (5), - RTL (6), CIRCLE (7), POSITION (8), LAND (9), OF_LOITER (10), DRIFT (11), - SPORT (13), FLIP (14), AUTOTUNE (15), POSHOLD (16), BRAKE (17), - THROW (18), AVOID_ADSB (19), GUIDED_NOGPS (20), SMART_RTL (21) - -【實現流程】 - - 1. 解析 drone_id 以提取 sysid - 2. 從 MODE_MAPPING 查表獲取 custom_mode 值 - 3. 驗證 fc_network_apps 模塊可用 - 4. 呼叫 change_mode(target_sysid, custom_mode, ...) - 5. 等待 ROS2 service 回應 - 6. 返回 result.success - -【錯誤處理】 - - ✅ 無效的 drone_id 格式 - ✅ 未知的模式名稱 - ✅ 缺少 fc_network_apps 模塊 - ✅ ROS2 service 超時 - ✅ 模式切換失敗 - -=============================================================================================== -5. 測試驗證 -=============================================================================================== - -【語法檢查】✅ 通過 - - $ python3 -m pylance communication.py - → No syntax errors found - -【導入檢查】✅ 可選(fc_network_apps 可選安裝) - - try: - from fc_network_apps import change_mode - except ImportError: - change_mode = None - - ✓ 如果 fc_network_apps 未安裝,代碼仍能運行,但 set_mode() 會返回失敗 - -【兼容性】✅ 完全向後相容 - - - 現有的 gui.py 代碼無需修改 - - 現有的調用接口保持不變 - - 自動與現有信號系統配合 - -=============================================================================================== -6. 使用示例 -=============================================================================================== - -【示例 1: 在 GUI 中單無人機切換】 - - # 用戶在 GUI 中: - # 1. 從 mode_combo 選擇 "GUIDED" - # 2. 點擊「切換」按鈕 - # 3. 系統自動調用: - - self.monitor.set_mode("s0_1", "GUIDED") - - # 結果:無人機 sysid=1 切換到 GUIDED 模式(custom_mode=4) - -【示例 2: 群組無人機切換】 - - # 用戶在 GUI 中: - # 1. 為群組 "A" 選擇模式 "AUTO" - # 2. 點擊「切換」按鈕 - # 3. 系統對群組內每個無人機調用: - - for drone_id in ["s0_1", "s0_2", "s0_3"]: - self.monitor.set_mode(drone_id, "AUTO") - - # 結果:三個無人機都切換到 AUTO 模式(custom_mode=3) - -【示例 3: 直接使用 fc_network_apps(腳本)】 - - from fc_network_apps import change_mode - - result = change_mode( - target_sysid=1, - custom_mode=4.0, # GUIDED - timeout_sec=2.0 - ) - - if result.success: - print(f"Mode change successful: {result.message}") - else: - print(f"Mode change failed: {result.message}") - -=============================================================================================== -7. 文件結構 -=============================================================================================== - -GUI/ -├── communication.py ✏️ 【修改】新增 set_mode() 方法 -├── gui.py ✓ 【無需修改】已使用 set_mode() -├── example_set_mode_usage.py ✨ 【新增】使用示例和詳細文檔 -├── demo_set_mode.py ✨ 【新增】可執行演示腳本 -├── SET_MODE_INTEGRATION.md ✨ 【新增】完整集成指南 -├── IMPLEMENTATION_SUMMARY.md ✨ 【新增】實現總結 -├── README_SET_MODE.md ✨ 【新增】快速參考 -└── ...其他文件 - -=============================================================================================== -8. 快速驗證 -=============================================================================================== - -【步驟 1: 檢查 set_mode 方法是否存在】 - - $ grep -n "async def set_mode" GUI/communication.py - 612: async def set_mode(self, drone_id, mode_name): - -【步驟 2: 檢查 MODE_MAPPING 是否完整】 - - $ grep -A 20 "MODE_MAPPING = {" GUI/communication.py - 585: MODE_MAPPING = { - 586: "STABILIZE": 0, - ... - 606: } - -【步驟 3: 檢查 fc_network_apps 導入】 - - $ grep -n "from fc_network_apps import" GUI/communication.py - 20: from fc_network_apps import change_mode - -【步驟 4: 運行演示腳本】 - - $ python3 GUI/demo_set_mode.py --help - $ python3 GUI/demo_set_mode.py direct --sysid 1 --mode GUIDED - -=============================================================================================== -9. 常見問題 -=============================================================================================== - -Q1: 為什麼要在 communication.py 中實現而不是在 gui.py 中? -A: 為了保持代碼分離和可重用性。communication.py 負責與無人機通信, - gui.py 負責用戶界面。這樣 set_mode() 可以被其他模塊使用。 - -Q2: 為什麼模式名稱要大寫? -A: 這是 MODE_MAPPING 字典中的約定,與 MAVLink 和 ArduPilot 的命名保持一致。 - -Q3: drone_id 格式為什麼是 "s{socket_id}_{sysid}"? -A: 因為同一個連接(socket)可能有多個無人機,sysid 是 MAVLink 的標準 system ID。 - -Q4: 如果 fc_network_apps 未安裝怎麼辦? -A: 代碼已使用 try-except 安全處理,set_mode() 會返回失敗,GUI 會顯示錯誤信息。 - -Q5: 支援同時為多個無人機切換模式嗎? -A: 是的,通過 _handle_group_mode_change() 方法支持群組操作。 - -=============================================================================================== -10. 總結 -=============================================================================================== - -✅ 成功在 gui.py 中集成 fc_network_apps 的 change_mode 功能 - -修改總結: - - 1 個文件修改 (communication.py) - - 4 個新增文件(示例、文檔、演示腳本) - - 所有現有代碼無需修改 - - 完全向後相容 - - 完整的錯誤處理和日誌記錄 - - 詳細的文檔和示例 - -功能特點: - ✅ 簡單易用的 API: monitor.set_mode(drone_id, mode) - ✅ 自動模式轉換: 模式名稱 → custom_mode 值 - ✅ 支援 20+ 種飛行模式 - ✅ 單無人機和群組切換 - ✅ 非同步執行不阻塞 UI - ✅ 完整錯誤處理 - ✅ 詳細日誌記錄 - -現在用戶可以通過 GUI 方便地改變無人機的飛行模式!🚁 - -=============================================================================================== -相關文件清單 -=============================================================================================== - -代碼文件: - - GUI/communication.py (修改) - - GUI/gui.py (無需修改) - - GUI/example_set_mode_usage.py (新增) - - GUI/demo_set_mode.py (新增) - -文檔文件: - - GUI/SET_MODE_INTEGRATION.md (新增) - - GUI/IMPLEMENTATION_SUMMARY.md (新增) - - GUI/README_SET_MODE.md (新增) - - 此文件 (CHANGES.md) - -原始模塊: - - fc_network_apps/changeMode.py - - fc_network_apps/__init__.py - -=============================================================================================== diff --git a/src/GUI/COMPLETION_GUIDE.md b/src/GUI/COMPLETION_GUIDE.md deleted file mode 100644 index 6f47f73..0000000 --- a/src/GUI/COMPLETION_GUIDE.md +++ /dev/null @@ -1,292 +0,0 @@ -# 🎯 在 GUI.py 中使用 fc_network 的 Set Mode 功能 - 完成指引 - -## ✅ 已完成的工作 - -已成功在 `gui.py` 中集成了 `fc_network_apps` 的 `change_mode` 功能,允许通過 GUI 改變無人機的飛行模式。 - ---- - -## 📂 生成的文件列表 - -### 核心代碼修改 -- **`communication.py`** ✏️ - - 新增 `MODE_MAPPING` 模式映射表 - - 新增 `async def set_mode()` 方法 - - 導入 `fc_network_apps.change_mode` - -### 文檔文件 -- **`README_SET_MODE.md`** ⭐ 推薦閱讀 - - 快速參考和使用指南 - - API 文檔 - - 常見問題解答 - -- **`SET_MODE_INTEGRATION.md`** - - 完整的集成指南 - - 詳細的原理解釋 - - 代碼示例 - - 流程圖 - -- **`IMPLEMENTATION_SUMMARY.md`** - - 實現總結 - - 測試檢查清單 - - 架構圖 - - 調試技巧 - -- **`CHANGES.md`** - - 詳細的修改清單 - - 文件結構 - - 技術細節 - -### 示例和演示 -- **`example_set_mode_usage.py`** 📚 - - 完整的使用示例 - - 詳細註解 - - 實現說明 - -- **`demo_set_mode.py`** 🎮 可執行 - - 實時演示腳本 - - 三種使用方式 - - 命令行接口 - ---- - -## 🚀 快速開始 - -### 1. 查看實現 -```bash -# 查看 set_mode 方法 -grep -n "async def set_mode" GUI/communication.py - -# 查看模式映射表 -grep -A 20 "MODE_MAPPING = {" GUI/communication.py -``` - -### 2. 查看文檔 -```bash -# 推薦首先閱讀快速參考 -cat GUI/README_SET_MODE.md - -# 然後是完整的集成指南 -cat GUI/SET_MODE_INTEGRATION.md -``` - -### 3. 運行演示 -```bash -cd GUI -python3 demo_set_mode.py direct --sysid 1 --mode GUIDED -python3 demo_set_mode.py via-monitor --drone-id s0_1 --mode AUTO -python3 demo_set_mode.py group --drones s0_1 s0_2 s0_3 --mode LOITER -``` - ---- - -## 💡 核心功能 - -### 簡單的 API - -```python -# 改變無人機飛行模式 -success = await monitor.set_mode("s0_1", "GUIDED") -``` - -### 支援的模式 - -| 模式 | 值 | 用途 | -|------|-----|------| -| GUIDED | 4 | 引導模式(最常用) | -| AUTO | 3 | 自動任務 | -| LOITER | 5 | 盤旋 | -| RTL | 6 | 返回起點 | -| LAND | 9 | 著陸 | -| 等等... | ... | 共20+種模式 | - -### 集成方式 - -現有代碼無需修改: - -```python -# gui.py 中已在使用 -def handle_mode_change(self, drone_id): - mode = panel.mode_combo.currentText() - future = self.monitor.set_mode(drone_id, mode) # ✅ 新方法 - loop.create_task(self.handle_service_response(future, ...)) -``` - ---- - -## 📖 文檔導航 - -``` -開始使用 - ↓ -README_SET_MODE.md (快速參考) ⭐ - ↓ - ├─ API 文檔? - │ → SET_MODE_INTEGRATION.md - │ - ├─ 想看實現細節? - │ → IMPLEMENTATION_SUMMARY.md - │ - ├─ 想看代碼? - │ → example_set_mode_usage.py - │ - ├─ 想運行演示? - │ → demo_set_mode.py - │ - └─ 想看完整改動? - → CHANGES.md -``` - ---- - -## 🔍 驗證清單 - -- ✅ communication.py 已修改 -- ✅ set_mode() 方法已實現 -- ✅ MODE_MAPPING 已定義 -- ✅ fc_network_apps 導入已添加 -- ✅ 現有代碼無需修改 -- ✅ 文檔已完成 -- ✅ 示例已提供 -- ✅ 演示腳本已創建 -- ✅ 語法檢查通過 - ---- - -## 🎓 學習路線 - -1. **初級用戶**:閱讀 `README_SET_MODE.md` -2. **中級用戶**:閱讀 `SET_MODE_INTEGRATION.md` -3. **進階用戶**:閱讀 `IMPLEMENTATION_SUMMARY.md` -4. **開發者**:查看 `communication.py` 源代碼 - ---- - -## 🛠️ 常用命令 - -```bash -# 查看 set_mode 方法 -grep -n "async def set_mode" GUI/communication.py - -# 查看所有支持的模式 -grep -o '"[A-Z_]*":' GUI/communication.py | sort | uniq - -# 檢查語法 -python3 -m py_compile GUI/communication.py - -# 查看相關日誌 -grep -i "mode\|set_mode" gui.py -``` - ---- - -## 🔗 相關文件 - -### 核心實現 -- `GUI/communication.py` - DroneMonitor 類 -- `GUI/gui.py` - ControlStationUI 類 -- `fc_network_apps/changeMode.py` - change_mode() 函數 - -### 文檔 -- `GUI/README_SET_MODE.md` - 快速參考 ⭐ -- `GUI/SET_MODE_INTEGRATION.md` - 集成指南 -- `GUI/IMPLEMENTATION_SUMMARY.md` - 實現總結 -- `GUI/CHANGES.md` - 修改清單 -- 此文件 - 完成指引 - -### 示例 -- `GUI/example_set_mode_usage.py` - 使用示例 -- `GUI/demo_set_mode.py` - 演示腳本 - ---- - -## 📋 實現概要 - -### 修改內容 -``` -communication.py -├── 導入 change_mode -├── 定義 MODE_MAPPING (20+ 種模式) -└── 實現 set_mode() 方法 - ├── 解析 drone_id - ├── 查表轉換模式 - ├── 呼叫 change_mode() - └── 返回結果 -``` - -### 工作流程 -``` -GUI 用戶操作 - ↓ -handle_mode_change() 或 _handle_group_mode_change() - ↓ -monitor.set_mode(drone_id, mode) - ↓ -change_mode(sysid, custom_mode) - ↓ -ROS2 Service Call - ↓ -無人機執行模式切換 - ↓ -返回結果並更新 UI -``` - ---- - -## ⚠️ 重要提示 - -1. **模式名稱區分大小寫** - - ✓ `"GUIDED"`, `"AUTO"`, `"LOITER"` - - ✗ `"guided"`, `"auto"`, `"loiter"` - -2. **drone_id 格式固定** - - 必須為 `"s{socket_id}_{sysid}"` 格式 - - 例如:`"s0_1"`, `"s1_11"` - -3. **支持 async/await** - - set_mode() 是非同步函數 - - 必須使用 await 或 asyncio event loop - -4. **錯誤處理** - - 超時:預設 2.0 秒 - - 缺少模塊:會返回 False - - 無效模式:會返回 False - ---- - -## 🎉 總結 - -✅ **成功集成 fc_network set mode 功能到 GUI** - -**特點:** -- 簡單易用的 API -- 自動模式轉換 -- 支援 20+ 種飛行模式 -- 單無人機和群組切換 -- 完整的錯誤處理 -- 詳細的文檔和示例 - -**現在可以:** -- 通過 GUI 改變無人機飛行模式 ✅ -- 同時為多個無人機切換模式 ✅ -- 使用簡單的 API:`monitor.set_mode(drone_id, mode)` ✅ -- 查看詳細的文檔和示例 ✅ - ---- - -## 📞 需要幫助? - -1. **快速問題**:查看 `README_SET_MODE.md` 的「常見問題」部分 -2. **詳細説明**:閱讀 `SET_MODE_INTEGRATION.md` -3. **實現細節**:查看 `IMPLEMENTATION_SUMMARY.md` -4. **代碼示例**:運行 `demo_set_mode.py` -5. **修改清單**:查看 `CHANGES.md` - ---- - -**完成日期**: 2026年4月7日 -**狀態**: ✅ 已完成 -**測試**: ✅ 通過 -**文檔**: ✅ 完整 - -祝您使用愉快!🚁✨ diff --git a/src/GUI/IMPLEMENTATION_SUMMARY.md b/src/GUI/IMPLEMENTATION_SUMMARY.md deleted file mode 100644 index d04c890..0000000 --- a/src/GUI/IMPLEMENTATION_SUMMARY.md +++ /dev/null @@ -1,414 +0,0 @@ -# GUI 集成 fc_network Set Mode 功能 - 實現總結 - -## 📋 概述 - -已在 `gui.py` 中成功集成 `fc_network_apps` 的 `change_mode` 功能,允許通過 GUI 改變無人機的飛行模式。 - ---- - -## 🔧 實現詳情 - -### 1. 修改的文件 - -#### `communication.py` (DroneMonitor 類) - -**新增內容:** - -1. **導入 fc_network_apps** - ```python - try: - from fc_network_apps import change_mode - except ImportError: - change_mode = None - ``` - - 安全地導入 change_mode,如果不可用則設為 None - - 允許代碼在 fc_network_apps 未安裝時仍能運行 - -2. **模式映射表** - ```python - MODE_MAPPING = { - "STABILIZE": 0, - "ACRO": 1, - "ALT_HOLD": 2, - "AUTO": 3, - "GUIDED": 4, # ← 最常用 - "LOITER": 5, - # ... 更多模式 - } - ``` - - 基於 ArduCopter 的模式定義 - - 將模式名稱映射到 custom_mode 值 - -3. **set_mode() 方法** - ```python - async def set_mode(self, drone_id, mode_name): - """ - 使用 fc_network_apps 的 change_mode 函數切換無人機飛行模式 - - 參數: - drone_id: 無人機ID (格式: "s{socket_id}_{sysid}") - mode_name: 模式名稱 (例如: "GUIDED", "AUTO") - - 返回: - bool: 模式切換是否成功 - """ - ``` - - **功能**: - 1. 解析 drone_id 以提取 sysid - 2. 查表獲取 custom_mode 值 - 3. 呼叫 fc_network_apps.change_mode() - 4. 記錄結果並返回成功/失敗狀態 - - - **錯誤處理**: - - 無效的 drone_id 格式 - - 未知的模式名稱 - - 缺少 fc_network_apps 模塊 - - ROS2 service 超時 - -### 2. 現有代碼的兼容性 - -**gui.py 現有的調用代碼無需修改:** - -```python -# ✅ 已在使用中,無需改動 -def handle_mode_change(self, drone_id): - mode = panel.mode_combo.currentText() - future = self.monitor.set_mode(drone_id, mode) - loop.create_task(self.handle_service_response(future, ...)) - -def _handle_group_mode_change(self, group_id, mode): - for drone_id in group.drone_ids: - future = self.monitor.set_mode(drone_id, mode) - loop.create_task(self.handle_service_response(future, ...)) -``` - ---- - -## 🎯 使用流程 - -### 單個無人機模式切換 - -``` -用戶在 GUI 中操作 - ↓ -mode_combo.currentText() → "GUIDED" - ↓ -點擊「切換」按鈕 - ↓ -handle_mode_change("s0_1") - ↓ -monitor.set_mode("s0_1", "GUIDED") - ↓ -change_mode(target_sysid=1, custom_mode=4.0) - ↓ -ROS2 service call: /fc_network/vehicle/send_command_long - ↓ -無人機切換到 GUIDED 模式 - ↓ -返回結果並更新 UI -``` - -### 群組模式切換 - -``` -用戶在群組 panel 中操作 - ↓ -選擇模式 + 點擊「切換」 - ↓ -_handle_group_mode_change("A", "AUTO") - ↓ -For each drone in group.drone_ids: - monitor.set_mode(drone_id, "AUTO") - ↓ -並行發送 ROS2 service calls - ↓ -所有無人機切換到 AUTO 模式 - ↓ -返回結果並更新 UI -``` - ---- - -## 📁 新增文件 - -### 1. `GUI/example_set_mode_usage.py` -- **目的**:詳細的使用示例和文檔 -- **包含**: - - 實現原理說明 - - API 文檔 - - 註解 - - 示例代碼片段 - -### 2. `GUI/demo_set_mode.py` -- **目的**:可執行的演示腳本 -- **功能**: - - `--direct`: 直接使用 fc_network_apps.change_mode() - - `--via-monitor`: 通過 DroneMonitor.set_mode() - - `--group`: 群組模式切換演示 -- **用法**: - ```bash - python3 demo_set_mode.py direct --sysid 1 --mode GUIDED - python3 demo_set_mode.py via-monitor --drone-id s0_1 --mode AUTO - python3 demo_set_mode.py group --drones s0_1 s0_2 s0_3 --mode LOITER - ``` - -### 3. `GUI/SET_MODE_INTEGRATION.md` -- **目的**:完整的集成文檔 -- **包含**: - - 實現原理詳解 - - 使用流程圖 - - 代碼示例 - - 錯誤處理 - - 常見問題 - - 模式參考表 - ---- - -## 🔗 相關的 fc_network_apps 代碼 - -### change_mode() 函數 - -位置:`fc_network_apps/changeMode.py` - -```python -def change_mode( - *, - target_sysid: int, - custom_mode: float, - target_compid: int = 0, - base_mode: float = 1.0, - confirmation: int = 0, - timeout_sec: float = DEFAULT_TIMEOUT_SEC, - service_name: str = DEFAULT_SERVICE_NAME, -) -> ChangeModeResult: - """ - One-shot helper for collaborators who want minimal code. - - Service call to: /fc_network/vehicle/send_command_long - Command: MAV_CMD_DO_SET_MODE (176) - """ -``` - -**參數說明:** -- `target_sysid`: 目標無人機的 system ID -- `custom_mode`: ArduCopter 的模式值 (0-21) -- `base_mode`: MAV_MODE_FLAG_CUSTOM_MODE_ENABLED = 1.0 -- `timeout_sec`: ROS2 service call 超時時間 - -**返回值:** -```python -@dataclass -class ChangeModeResult: - success: bool # 模式切換是否成功 - message: str # 詳細信息 - ack_result: int # ACK code -``` - ---- - -## ✅ 測試檢查清單 - -- [x] `communication.py` 語法檢查通過 -- [x] 導入 fc_network_apps 的 change_mode 函數 -- [x] 實現 set_mode() 方法並支持 async/await -- [x] 模式映射表涵蓋常用模式 -- [x] 錯誤處理完整(無效 drone_id、未知模式、超時) -- [x] 日誌記錄清晰 -- [x] 與現有 gui.py 代碼兼容 -- [x] 創建完整的文檔和示例 - ---- - -## 🚀 使用示例 - -### 示例 1: 直接調用(腳本中) - -```python -from fc_network_apps import change_mode - -result = change_mode( - target_sysid=1, - custom_mode=4.0, # GUIDED - timeout_sec=2.0 -) - -if result.success: - print("Mode change successful!") -else: - print(f"Mode change failed: {result.message}") -``` - -### 示例 2: 通過 GUI(在 ControlStationUI 中) - -```python -# 已在 gui.py 中使用,無需修改 -loop = asyncio.get_event_loop() -future = self.monitor.set_mode("s0_1", "GUIDED") -loop.create_task(self.handle_service_response(future, "切換模式 GUIDED s0_1")) -``` - -### 示例 3: 群組操作(在 ControlStationUI 中) - -```python -# 已在 gui.py 中使用,無需修改 -def _handle_group_mode_change(self, group_id, mode): - group = self.mission_groups.get(group_id) - for drone_id in group.drone_ids: - future = self.monitor.set_mode(drone_id, mode) - loop.create_task(self.handle_service_response(future, f"{drone_id} 切換 {mode}")) -``` - ---- - -## 🎓 支援的飛行模式 - -| 模式 | 值 | 說明 | -|------|-----|------| -| STABILIZE | 0 | 自穩定 | -| ACRO | 1 | 特技 | -| ALT_HOLD | 2 | 保持高度 | -| AUTO | 3 | 自動任務 | -| **GUIDED** | **4** | **引導模式** | -| LOITER | 5 | 盤旋 | -| RTL | 6 | 返回起點 | -| CIRCLE | 7 | 圓形飛行 | -| POSITION | 8 | 位置保持 | -| LAND | 9 | 著陸 | -| SMART_RTL | 21 | 智能返回 | - ---- - -## 📊 架構圖 - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ ControlStationUI │ -│ (gui.py) │ -├─────────────────────────────────────────────────────────────────┤ -│ handle_mode_change() │ -│ _handle_group_mode_change() │ -│ │ -│ Calls: self.monitor.set_mode(drone_id, mode) │ -└────────────────────────┬────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ DroneMonitor │ -│ (communication.py) │ -├─────────────────────────────────────────────────────────────────┤ -│ set_mode(drone_id, mode_name) │ -│ ├─ Parse drone_id → sysid │ -│ ├─ Lookup MODE_MAPPING → custom_mode │ -│ └─ Call: change_mode(sysid, custom_mode) │ -└────────────────────────┬────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ fc_network_apps │ -│ (changeMode.py) │ -├─────────────────────────────────────────────────────────────────┤ -│ change_mode() │ -│ ├─ Create ROS2 Node & Client │ -│ ├─ Prepare MavCommandLong Request │ -│ └─ Call Service & Return Result │ -└────────────────────────┬────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ ROS2 Service │ -│ /fc_network/vehicle/send_command_long │ -├─────────────────────────────────────────────────────────────────┤ -│ MavCommandLong │ -│ ├─ command: 176 (DO_SET_MODE) │ -│ ├─ param1: base_mode = 1.0 │ -│ └─ param2: custom_mode = [0-21] │ -└────────────────────────┬────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ MAVLink Protocol │ -│ to Drone │ -├─────────────────────────────────────────────────────────────────┤ -│ COMMAND_LONG message │ -│ ├─ target_system: sysid │ -│ ├─ command: 176 │ -│ ├─ param1: base_mode │ -│ └─ param2: custom_mode │ -└────────────────────────┬────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ Drone (FCU) │ -│ Changes Flight Mode │ -└─────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 🔍 調試技巧 - -### 檢查模式是否可用 - -```bash -# ROS2 CLI 直接測試 -ros2 service call /fc_network/vehicle/send_command_long \ - fc_interfaces/srv/MavCommandLong \ - "{target_sysid: 1, target_compid: 0, command: 176, confirmation: 0, \ - param1: 1, param2: 4, param3: 0, param4: 0, param5: 0, param6: 0, \ - param7: 0, timeout_sec: 2}" -``` - -### 檢查 GUI 日誌 - -```bash -# 在 GUI 終端中查看日誌 -# [INFO]: Changing mode for drone s0_1 to GUIDED (custom_mode=4) -# [INFO]: Mode change successful for s0_1: Success -``` - -### 驗證 drone_id 格式 - -```python -drone_id = "s0_1" -parts = drone_id.split('_') -sysid = int(parts[-1]) # 應該是 1 -print(f"sysid: {sysid}") # ✓ sysid: 1 -``` - ---- - -## 📝 注意事項 - -1. **模式名稱區分大小寫** - - ✓ `"GUIDED"`, `"AUTO"`, `"LOITER"` - - ✗ `"guided"`, `"auto"`, `"loiter"` - -2. **drone_id 格式** - - 必須為 `"s{socket_id}_{sysid}"` 格式 - - 例如:`"s0_1"`, `"s1_11"` - -3. **超時行為** - - 預設超時:2.0 秒 - - 如果無人機無響應,會傳回 `success=False` - -4. **非同步執行** - - `set_mode()` 是 async 函數 - - 必須使用 `await` 或透過 asyncio event loop 調用 - -5. **錯誤處理** - - 檢查 result.success 判斷是否成功 - - 查看 ROS2 日誌了解失敗原因 - ---- - -## 🎉 總結 - -✅ **成功集成 fc_network 的 set_mode 功能到 GUI 中** - -- 簡單易用的 API:`monitor.set_mode(drone_id, mode)` -- 自動模式轉換:模式名稱 → custom_mode 值 -- 完整的錯誤處理 -- 詳細的文檔和示例 -- 向後相容:現有代碼無需修改 - -現在用戶可以通過 GUI 方便地改變無人機的飛行模式!🚁 diff --git a/src/GUI/README_SET_MODE.md b/src/GUI/README_SET_MODE.md deleted file mode 100644 index 6c24f05..0000000 --- a/src/GUI/README_SET_MODE.md +++ /dev/null @@ -1,266 +0,0 @@ -# GUI Set Mode 功能 - 快速參考 - -## 📍 在 GUI 中使用 Set Mode 的最快方式 - -### 現有代碼(無需修改) - -gui.py 中已經在使用 set_mode 功能: - -```python -def handle_mode_change(self, drone_id): - """單個無人機模式切換""" - mode = panel.mode_combo.currentText() # 從下拉列表獲取模式 - future = self.monitor.set_mode(drone_id, mode) - loop.create_task(self.handle_service_response(future, ...)) - -def _handle_group_mode_change(self, group_id, mode): - """群組模式切換""" - for drone_id in group.drone_ids: - future = self.monitor.set_mode(drone_id, mode) - loop.create_task(self.handle_service_response(future, ...)) -``` - -### 實現位置 - -- **核心實現**:[`communication.py`](communication.py#L585-L685) - - `DroneMonitor.MODE_MAPPING` - 模式映射表 - - `DroneMonitor.set_mode()` - 非同步方法 - -- **使用位置**:[`gui.py`](gui.py#L391-L401) - - `handle_mode_change()` - 單個無人機 - - `_handle_group_mode_change()` - 群組無人機 - ---- - -## 🎮 模式支援列表 - -| 模式名稱 | custom_mode | 備註 | -|---------|-----------|------| -| STABILIZE | 0 | 自穩定 | -| ACRO | 1 | 特技 | -| ALT_HOLD | 2 | 保持高度 | -| AUTO | 3 | 自動任務 | -| GUIDED | 4 | 引導(常用) | -| LOITER | 5 | 盤旋 | -| RTL | 6 | 返回起點 | -| CIRCLE | 7 | 圓形飛行 | -| POSITION | 8 | 位置保持 | -| LAND | 9 | 著陸 | -| SMART_RTL | 21 | 智能返回 | - ---- - -## 🔧 API 參考 - -### DroneMonitor.set_mode() - -```python -async def set_mode(self, drone_id, mode_name) -> bool: - """ - 改變無人機飛行模式 - - 參數: - drone_id: str - 無人機ID (如: "s0_1", "s1_11") - mode_name: str - 模式名稱 (如: "GUIDED", "AUTO") - - 返回: - bool - 成功返回 True,失敗返回 False - """ -``` - -### 使用示例 - -```python -# 單個無人機 -success = await self.monitor.set_mode("s0_1", "GUIDED") - -# 或在 asyncio 中 -loop = asyncio.get_event_loop() -future = self.monitor.set_mode("s0_1", "GUIDED") -loop.create_task(handle_result(future)) -``` - ---- - -## 📂 相關文件 - -### 文檔 -- [`IMPLEMENTATION_SUMMARY.md`](IMPLEMENTATION_SUMMARY.md) - 完整實現總結 -- [`SET_MODE_INTEGRATION.md`](SET_MODE_INTEGRATION.md) - 詳細集成指南 -- [`example_set_mode_usage.py`](example_set_mode_usage.py) - 使用示例和文檔 -- 此文件:`README_SET_MODE.md` - 快速參考 - -### 代碼 -- [`communication.py`](communication.py) - DroneMonitor 實現 (L585-L685) -- [`gui.py`](gui.py) - GUI 中的使用 (L391-L401, L656-L664) -- [`demo_set_mode.py`](demo_set_mode.py) - 可執行的演示腳本 - -### 原始模塊 -- `fc_network_apps/changeMode.py` - change_mode() 函數 -- `fc_network_apps/__init__.py` - 模塊導出 - ---- - -## 🚀 快速開始 - -### 1. 檢查實現 - -查看 communication.py 中的 MODE_MAPPING 和 set_mode() 方法是否存在: - -```bash -grep -n "MODE_MAPPING\|async def set_mode" GUI/communication.py -``` - -✓ 應該能看到相關代碼 - -### 2. 驗證 fc_network_apps 可用 - -```bash -python3 -c "from fc_network_apps import change_mode; print('OK')" -``` - -✓ 輸出 "OK" 表示模塊可用 - -### 3. 在 GUI 中使用 - -直接點擊 GUI 中的模式選擇器和「切換」按鈕即可。 - -### 4. 查看日誌 - -```bash -# 在 GUI 終端查看日誌輸出 -# [INFO]: Changing mode for drone s0_1 to GUIDED (custom_mode=4) -# [INFO]: Mode change successful for s0_1: Success -``` - ---- - -## 🐛 常見問題 - -### Q1: 模式切換失敗 -**A:** 檢查以下事項: -- ✓ 無人機是否已連接到 fc_network -- ✓ 模式名稱是否正確(區分大小寫) -- ✓ drone_id 格式是否正確 (格式:`s{socket_id}_{sysid}`) -- ✓ 查看 ROS2 日誌了解詳細錯誤信息 - -### Q2: "Unknown mode" 錯誤 -**A:** -- 檢查模式名稱的大小寫 -- 確保模式在 MODE_MAPPING 中 -- 參考上面的 "模式支援列表" - -### Q3: "fc_network_apps is not available" 錯誤 -**A:** -- 確保在 ROS2 workspace 中安裝了 fc_network_apps -- 運行 `colcon build --packages-select fc_network_apps` -- 重新 source setup 文件 - -### Q4: Service call timeout -**A:** -- 檢查 fc_network 節點是否運行 -- 檢查無人機連接狀態 -- 增加 timeout 值(在 set_mode() 中修改) - ---- - -## 💡 設計要點 - -### 為什麼使用 fc_network_apps.change_mode()? - -✅ **優點**: -- 經過驗證的 MAVLink 實現 -- 統一的 ROS2 service interface -- 自動錯誤處理 -- 支持多個無人機系統 - -❌ **直接使用 MAVLink 的缺點**: -- 需要管理連接 -- 錯誤處理複雜 -- 與 fc_network 架構不一致 - -### drone_id 格式設計 - -`s{socket_id}_{sysid}` 的含義: -- `s` - 前綴,表示 socket 連接 -- `socket_id` - 連接序號(0, 1, 2...) -- `_` - 分隔符 -- `sysid` - MAVLink system ID - -例如 `s0_1`: -- socket_id = 0(第一個連接) -- sysid = 1(該連接上的第一個無人機) - ---- - -## 📊 流程圖 - -``` -GUI 用戶界面 - │ - ├─ 單無人機流程 ─────────────────────────┐ - │ │ - │ 1. 選擇模式 │ - │ 2. 點擊「切換」 │ - │ 3. handle_mode_change(drone_id) │ - │ 4. monitor.set_mode(drone_id, mode) │ - │ 5. change_mode(sysid, custom_mode) │ - │ 6. ROS2 service call │ - │ 7. 無人機執行模式切換 │ - │ 8. 返回結果並更新 UI │ - │ │ - └─────────────────────────────────────────┘ - - ├─ 群組流程 ────────────────────────────┐ - │ │ - │ 1. 為群組選擇模式 │ - │ 2. 點擊群組「切換」 │ - │ 3. _handle_group_mode_change() │ - │ 4. For each drone_id in group: │ - │ monitor.set_mode(drone_id, mode) │ - │ 5. 並行發送多個 ROS2 service calls │ - │ 6. 所有無人機執行模式切換 │ - │ 7. 返回結果並更新 UI │ - │ │ - └────────────────────────────────────────┘ -``` - ---- - -## 🔗 相關資源 - -- **ArduCopter 模式文檔**: https://ardupilot.org/copter/docs/flight-modes.html -- **MAVLink 文檔**: https://mavlink.io/en/ -- **fc_network_adapter**: 本項目中的 `fc_network_adapter/` 目錄 -- **fc_network_apps**: 本項目中的 `fc_network_apps/` 目錄 - ---- - -## 📌 重要提示 - -1. **模式名稱必須大寫** - - `"GUIDED"` ✅ - - `"guided"` ❌ - -2. **drone_id 格式固定** - - 必須包含 `_` 分隔符 - - `"s0_1"` ✅ - - `"s01"` ❌ - -3. **async/await 模式** - - `set_mode()` 是 async 函數 - - 必須通過 `await` 或 asyncio 調用 - -4. **超時設定** - - 預設 2.0 秒 - - 無響應時返回 False - -5. **日誌記錄** - - 所有操作都記錄在 ROS2 日誌中 - - 便於調試和監控 - ---- - -**最後更新**: 2026年4月7日 -**版本**: 1.0 -**作者**: GUI 團隊 diff --git a/src/GUI/SET_MODE_INTEGRATION.md b/src/GUI/SET_MODE_INTEGRATION.md deleted file mode 100644 index f9658cf..0000000 --- a/src/GUI/SET_MODE_INTEGRATION.md +++ /dev/null @@ -1,360 +0,0 @@ -# GUI 中使用 fc_network 的 Set Mode 功能 - -## 概述 - -本文檔說明如何在 `gui.py` 中使用 `fc_network_apps` 的 `change_mode` 功能來改變無人機的飛行模式。 - ---- - -## 實現原理 - -### 1. 模式映射表 - -在 `communication.py` 的 `DroneMonitor` 類中定義了模式名稱到 `custom_mode` 值的映射: - -```python -MODE_MAPPING = { - "STABILIZE": 0, - "ACRO": 1, - "ALT_HOLD": 2, - "AUTO": 3, - "GUIDED": 4, # ← 最常用 - "LOITER": 5, - "RTL": 6, - "CIRCLE": 7, - "POSITION": 8, - "LAND": 9, - "OF_LOITER": 10, - "DRIFT": 11, - "SPORT": 13, - "FLIP": 14, - "AUTOTUNE": 15, - "POSHOLD": 16, - "BRAKE": 17, - "THROW": 18, - "AVOID_ADSB": 19, - "GUIDED_NOGPS": 20, - "SMART_RTL": 21, -} -``` - -### 2. set_mode 方法 - -```python -async def set_mode(self, drone_id, mode_name): - """ - 使用 fc_network_apps 的 change_mode 函數切換無人機飛行模式 - - 參數: - drone_id: 無人機ID (格式: "s{socket_id}_{sysid}") - mode_name: 模式名稱 (例如: "GUIDED", "AUTO", "LOITER") - - 返回: - bool: 模式切換是否成功 - """ -``` - -**主要步驟:** - -1. **解析 drone_id** - - 格式: `"s{socket_id}_{sysid}"` (例如: `"s0_1"`, `"s0_11"`) - - 提取 `sysid` 部分用於 fc_network service call - -2. **查表獲取 custom_mode 值** - - 輸入: 模式名稱 (例如: `"GUIDED"`) - - 輸出: custom_mode 值 (例如: `4`) - -3. **呼叫 fc_network_apps.change_mode()** - ```python - result = change_mode( - target_sysid=sysid, - custom_mode=float(custom_mode), - target_compid=0, - base_mode=1.0, # MAV_MODE_FLAG_CUSTOM_MODE_ENABLED - confirmation=0, - timeout_sec=2.0, - ) - ``` - -4. **處理結果** - - 返回 `result.success` 指示模式切換是否成功 - - 記錄 log 信息便於調試 - ---- - -## GUI 中的使用流程 - -### 1. 用戶交互流程 - -``` -┌─────────────────────────────────────────────┐ -│ 用戶在 GUI 的 mode_combo 中選擇模式 │ -│ (例如: "GUIDED") │ -└──────────────────┬──────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────┐ -│ 用戶點擊 "切換" 按鈕 │ -└──────────────────┬──────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────┐ -│ mode_change_requested.emit(group_id, mode) │ -└──────────────────┬──────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────┐ -│ handle_mode_change() 或 │ -│ _handle_group_mode_change() │ -└──────────────────┬──────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────┐ -│ monitor.set_mode(drone_id, mode) │ -│ (async call via asyncio) │ -└──────────────────┬──────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────┐ -│ change_mode() 發送 ROS2 service request │ -│ 到 /fc_network/vehicle/send_command_long │ -└──────────────────┬──────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────┐ -│ 無人機接收並執行模式切換 │ -└──────────────────┬──────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────┐ -│ 返回 ChangeModeResult │ -│ handle_service_response() 更新 UI │ -└─────────────────────────────────────────────┘ -``` - -### 2. 代碼示例 - -#### 單個無人機模式切換 (gui.py) - -```python -def handle_mode_change(self, drone_id): - # 從 active group 的 mode_combo 讀取模式 - group = self._get_active_group() - if group: - panel = self.group_panels.get(group.group_id) - mode = panel.mode_combo.currentText() if panel else "GUIDED" - else: - mode = "GUIDED" - - # 非同步呼叫 set_mode - loop = asyncio.get_event_loop() - future = self.monitor.set_mode(drone_id, mode) - loop.create_task(self.handle_service_response(future, f"切換模式 {mode} {drone_id}")) -``` - -#### 群組無人機模式切換 (gui.py) - -```python -def _handle_group_mode_change(self, group_id, mode): - """切換群組內所有無人機的飛行模式""" - group = self.mission_groups.get(group_id) - if not group: - return - - loop = asyncio.get_event_loop() - for drone_id in group.drone_ids: - # 為每個無人機發起非同步模式切換 - future = self.monitor.set_mode(drone_id, mode) - loop.create_task(self.handle_service_response(future, f"{drone_id} 切換 {mode}")) -``` - ---- - -## fc_network_apps.change_mode() 的實現 - -`change_mode()` 是一個簡單的包裝函數,位於 `fc_network_apps/changeMode.py`: - -```python -def change_mode( - *, - target_sysid: int, - custom_mode: float, - target_compid: int = 0, - base_mode: float = 1.0, - confirmation: int = 0, - timeout_sec: float = DEFAULT_TIMEOUT_SEC, - service_name: str = DEFAULT_SERVICE_NAME, -) -> ChangeModeResult: - """One-shot helper for collaborators who want minimal code.""" - - # 1. 創建 ROS2 node 和 client - rclpy.init(args=None) - node = Node("fc_change_mode_client_once") - client = node.create_client(MavCommandLong, service_name) - - # 2. 準備 service request - req = MavCommandLong.Request() - req.target_sysid = target_sysid - req.target_compid = target_compid - req.command = COMMAND_DO_SET_MODE # 176 - req.confirmation = confirmation - req.param1 = float(base_mode) - req.param2 = float(custom_mode) - req.param3 = req.param4 = req.param5 = req.param6 = req.param7 = 0.0 - req.timeout_sec = float(timeout_sec) - - # 3. 呼叫 service - future = client.call_async(req) - rclpy.spin_until_future_complete(node, future, timeout_sec=timeout_sec + 1.0) - - # 4. 返回結果 - response = future.result() - return ChangeModeResult( - success=response.success, - message=response.message, - ack_result=response.ack_result, - ) -``` - -### 等效的 ROS2 CLI 命令 - -```bash -ros2 service call /fc_network/vehicle/send_command_long \ - fc_interfaces/srv/MavCommandLong \ - "{target_sysid: 1, target_compid: 0, command: 176, confirmation: 0, \ - param1: 1, param2: 4, param3: 0, param4: 0, param5: 0, param6: 0, \ - param7: 0, timeout_sec: 2}" -``` - -參數說明: -- `command: 176` - `COMMAND_DO_SET_MODE` -- `param1: 1.0` - `base_mode` (MAV_MODE_FLAG_CUSTOM_MODE_ENABLED) -- `param2: 4.0` - `custom_mode` (GUIDED) - ---- - -## 支援的飛行模式 - -根據無人機平台(以 ArduCopter 為例): - -| 模式名稱 | custom_mode 值 | 說明 | -|---------|----------------|------| -| STABILIZE | 0 | 自穩定模式 | -| ACRO | 1 | 特技模式 | -| ALT_HOLD | 2 | 保持高度 | -| AUTO | 3 | 自動飛行(按任務) | -| **GUIDED** | **4** | **引導模式(手動指定位置)** | -| LOITER | 5 | 盤旋模式 | -| RTL | 6 | 返回起點 | -| CIRCLE | 7 | 圓形飛行 | -| POSITION | 8 | 位置保持 | -| LAND | 9 | 著陸模式 | -| SMART_RTL | 21 | 智能返回 | - -**注意:** 模式值可能因無人機平台而異(ArduPlane, PX4 等)。 - ---- - -## 使用 drone_id 的說明 - -### drone_id 格式 - -``` -"s{socket_id}_{sysid}" -``` - -例如: -- `"s0_1"` - socket 0, sysid 1 -- `"s0_11"` - socket 0, sysid 11 -- `"s1_2"` - socket 1, sysid 2 - -### 在 set_mode 中的解析 - -```python -parts = drone_id.split('_') -sysid = int(parts[-1]) # 提取最後一個部分作為 sysid -``` - ---- - -## 錯誤處理 - -### 常見錯誤及解決方案 - -1. **Invalid drone_id format** - - 原因:drone_id 格式不正確 - - 解決:確保 drone_id 包含 `_` 分隔符 - -2. **Unknown mode** - - 原因:模式名稱不在 MODE_MAPPING 中 - - 解決:使用支援的模式名稱(區分大小寫) - -3. **fc_network_apps is not available** - - 原因:fc_network_apps 沒有正確安裝或導入 - - 解決:確保在 ROS2 workspace 中正確安裝了 fc_network_apps - -4. **Service call timeout** - - 原因:無人機無回應或 fc_network service 未啟動 - - 解決:檢查無人機連接,驗證 fc_network 節點是否執行 - ---- - -## 完整使用示例 - -### scenario_1: 單個無人機模式切換 - -```python -# 在 GUI 中調用 -drone_id = "s0_1" -mode = "GUIDED" - -loop = asyncio.get_event_loop() -future = self.monitor.set_mode(drone_id, mode) -loop.create_task(self.handle_service_response(future, f"切換 {drone_id} 到 {mode}")) - -# 預期輸出: -# [INFO]: Changing mode for drone s0_1 to GUIDED (custom_mode=4) -# [INFO]: Mode change successful for s0_1: Success -``` - -### scenario_2: 群組模式切換 - -```python -# 為群組 "A" 內的所有無人機切換到 LOITER 模式 -group_id = "A" -mode = "LOITER" - -group = self.mission_groups.get(group_id) -for drone_id in group.drone_ids: # ["s0_1", "s0_2", "s0_3"] - future = self.monitor.set_mode(drone_id, mode) - loop.create_task(self.handle_service_response(future, f"{drone_id} 切換 {mode}")) - -# 預期輸出: -# [INFO]: Changing mode for drone s0_1 to LOITER (custom_mode=5) -# [INFO]: Mode change successful for s0_1: Success -# [INFO]: Changing mode for drone s0_2 to LOITER (custom_mode=5) -# [INFO]: Mode change successful for s0_2: Success -# ... -``` - ---- - -## 總結 - -通過在 `communication.py` 中實現 `set_mode` 方法,我們將 `fc_network_apps` 的 `change_mode` 功能集成到 GUI 中,提供了: - -✅ **簡單的 API**:`monitor.set_mode(drone_id, mode_name)` -✅ **自動模式轉換**:模式名稱 → custom_mode 值 -✅ **錯誤處理**:無效輸入、超時、連接失敗 -✅ **日誌記錄**:便於調試和監控 -✅ **非同步執行**:不阻塞 UI 線程 -✅ **群組支援**:同時為多個無人機切換模式 - ---- - -## 相關文件 - -- [`communication.py`](communication.py) - DroneMonitor 類及 set_mode 實現 -- [`gui.py`](gui.py) - handle_mode_change, _handle_group_mode_change -- [`example_set_mode_usage.py`](example_set_mode_usage.py) - 使用示例 -- [`fc_network_apps/changeMode.py`](../fc_network_apps/changeMode.py) - change_mode 實現 diff --git a/src/GUI/demo_set_mode.py b/src/GUI/demo_set_mode.py deleted file mode 100644 index f5c5b9f..0000000 --- a/src/GUI/demo_set_mode.py +++ /dev/null @@ -1,302 +0,0 @@ -#!/usr/bin/env python3 -""" -演示脚本:在 GUI 中使用 fc_network 的 set_mode 功能 - -本脚本展示了如何使用 communication.py 中集成的 set_mode 方法 -來改變無人機的飛行模式。 - -==================================================================================== -前置條件: -==================================================================================== - -1. ROS2 環境已正確配置 - source /opt/ros/humble/setup.bash - source ~/AirTrapMine/install/local_setup.bash - -2. fc_network_adapter 和 fc_network_apps 已安裝 - colcon build --packages-select fc_network_apps - -3. fc_network service 節點正在運行 - ros2 launch fc_network_adapter launch.py - -4. 無人機(或 SITL 模擬器)已連接到 fc_network - -==================================================================================== -使用方式: -==================================================================================== - -方式 1: 直接使用 fc_network_apps 的 change_mode 函數 - python3 example_set_mode_usage.py --direct --sysid 1 --mode GUIDED - -方式 2: 通過 DroneMonitor 的 set_mode 方法(GUI 集成) - python3 example_set_mode_usage.py --via-monitor --drone-id s0_1 --mode GUIDED - -方式 3: 模擬 GUI 的群組模式切換 - python3 example_set_mode_usage.py --group --drones s0_1 s0_2 s0_3 --mode AUTO - -==================================================================================== -""" - -import asyncio -import argparse -import sys - -def example_direct_change_mode(target_sysid, mode_name): - """直接使用 fc_network_apps.change_mode() 的示例""" - try: - from fc_network_apps import change_mode - except ImportError: - print("ERROR: fc_network_apps 未安裝或未在 ROS2 workspace 中") - return False - - # 模式映射表(與 communication.py 中的相同) - MODE_MAPPING = { - "STABILIZE": 0, - "ACRO": 1, - "ALT_HOLD": 2, - "AUTO": 3, - "GUIDED": 4, - "LOITER": 5, - "RTL": 6, - "CIRCLE": 7, - "POSITION": 8, - "LAND": 9, - "OF_LOITER": 10, - "DRIFT": 11, - "SPORT": 13, - "FLIP": 14, - "AUTOTUNE": 15, - "POSHOLD": 16, - "BRAKE": 17, - "THROW": 18, - "AVOID_ADSB": 19, - "GUIDED_NOGPS": 20, - "SMART_RTL": 21, - } - - custom_mode = MODE_MAPPING.get(mode_name) - if custom_mode is None: - print(f"ERROR: Unknown mode '{mode_name}'") - print(f"Available modes: {list(MODE_MAPPING.keys())}") - return False - - print(f"\n" + "="*80) - print(f"【直接使用 fc_network_apps.change_mode()】") - print(f"="*80) - print(f"Target sysid: {target_sysid}") - print(f"Mode: {mode_name}") - print(f"Custom mode value: {custom_mode}") - print(f"") - - try: - result = change_mode( - target_sysid=target_sysid, - custom_mode=float(custom_mode), - target_compid=0, - base_mode=1.0, - confirmation=0, - timeout_sec=2.0, - ) - - print(f"Results:") - print(f" Success: {result.success}") - print(f" Message: {result.message}") - print(f" ACK Result: {result.ack_result}") - print(f"") - - if result.success: - print(f"✅ Mode change successful!") - return True - else: - print(f"❌ Mode change failed!") - return False - - except Exception as e: - print(f"❌ Exception: {e}") - return False - - -async def example_via_monitor(drone_id, mode_name): - """通過 DroneMonitor 的 set_mode 方法的示例""" - try: - import rclpy - from GUI.communication import DroneMonitor - except ImportError as e: - print(f"ERROR: Failed to import DroneMonitor: {e}") - return False - - print(f"\n" + "="*80) - print(f"【通過 DroneMonitor.set_mode() 方法】") - print(f"="*80) - print(f"Drone ID: {drone_id}") - print(f"Mode: {mode_name}") - print(f"") - - try: - # 初始化 ROS2 - if not rclpy.ok(): - rclpy.init() - - # 創建 DroneMonitor 實例 - monitor = DroneMonitor() - - print(f"Created DroneMonitor instance") - print(f"Available modes: {list(monitor.MODE_MAPPING.keys())}") - print(f"") - - # 呼叫 set_mode - print(f"Calling monitor.set_mode('{drone_id}', '{mode_name}')...") - result = await monitor.set_mode(drone_id, mode_name) - - print(f"Result: {result}") - print(f"") - - if result: - print(f"✅ Mode change successful!") - else: - print(f"❌ Mode change failed!") - - # 清理 - monitor.destroy_node() - - return result - - except Exception as e: - print(f"❌ Exception: {e}") - import traceback - traceback.print_exc() - return False - - -async def example_group_mode_change(drone_ids, mode_name): - """模擬 GUI 的群組模式切換示例""" - try: - import rclpy - from GUI.communication import DroneMonitor - except ImportError as e: - print(f"ERROR: Failed to import DroneMonitor: {e}") - return False - - print(f"\n" + "="*80) - print(f"【群組模式切換模擬】") - print(f"="*80) - print(f"Drone IDs: {drone_ids}") - print(f"Mode: {mode_name}") - print(f"") - - try: - # 初始化 ROS2 - if not rclpy.ok(): - rclpy.init() - - # 創建 DroneMonitor 實例 - monitor = DroneMonitor() - - print(f"Created DroneMonitor instance") - print(f"") - - # 為每個無人機發起非同步模式切換 - tasks = [] - for drone_id in drone_ids: - print(f"Starting mode change for {drone_id}...") - task = monitor.set_mode(drone_id, mode_name) - tasks.append((drone_id, task)) - - print(f"") - print(f"Waiting for all mode changes to complete...") - print(f"") - - # 等待所有任務完成 - results = {} - for drone_id, task in tasks: - try: - result = await asyncio.wait_for(task, timeout=3.0) - results[drone_id] = result - status = "✅" if result else "❌" - print(f"{status} {drone_id}: {result}") - except asyncio.TimeoutError: - results[drone_id] = False - print(f"❌ {drone_id}: Timeout") - - # 清理 - monitor.destroy_node() - - print(f"") - print(f"Summary:") - print(f" Success: {sum(1 for v in results.values() if v)}/{len(results)}") - - return all(results.values()) - - except Exception as e: - print(f"❌ Exception: {e}") - import traceback - traceback.print_exc() - return False - - -def main(): - parser = argparse.ArgumentParser( - description="演示 GUI 中使用 fc_network 的 set_mode 功能", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=""" -示例: - # 直接使用 fc_network_apps.change_mode() - python3 example_set_mode_usage.py --direct --sysid 1 --mode GUIDED - - # 通過 DroneMonitor 的 set_mode 方法 - python3 example_set_mode_usage.py --via-monitor --drone-id s0_1 --mode AUTO - - # 群組模式切換 - python3 example_set_mode_usage.py --group --drones s0_1 s0_2 s0_3 --mode LOITER - """ - ) - - subparsers = parser.add_subparsers(dest='command', help='選擇要執行的命令') - - # 直接使用 - direct_parser = subparsers.add_parser('direct', help='直接使用 fc_network_apps.change_mode()') - direct_parser.add_argument('--sysid', type=int, required=True, help='目標無人機 sysid') - direct_parser.add_argument('--mode', type=str, required=True, help='飛行模式名稱') - - # 通過 monitor - monitor_parser = subparsers.add_parser('via-monitor', help='通過 DroneMonitor.set_mode()') - monitor_parser.add_argument('--drone-id', type=str, required=True, help='無人機ID (e.g., s0_1)') - monitor_parser.add_argument('--mode', type=str, required=True, help='飛行模式名稱') - - # 群組模式切換 - group_parser = subparsers.add_parser('group', help='群組模式切換') - group_parser.add_argument('--drones', nargs='+', required=True, help='無人機ID列表') - group_parser.add_argument('--mode', type=str, required=True, help='飛行模式名稱') - - # 簡化的命令行支援(向後相容) - args = parser.parse_args() - - # 如果沒有子命令,嘗試舊格式的參數 - if not args.command: - if hasattr(args, 'direct'): - args.command = 'direct' - elif hasattr(args, 'via_monitor'): - args.command = 'via-monitor' - elif hasattr(args, 'group'): - args.command = 'group' - else: - parser.print_help() - return 1 - - # 執行選定的命令 - if args.command == 'direct': - success = example_direct_change_mode(args.sysid, args.mode) - elif args.command == 'via-monitor': - success = asyncio.run(example_via_monitor(args.drone_id, args.mode)) - elif args.command == 'group': - success = asyncio.run(example_group_mode_change(args.drones, args.mode)) - else: - parser.print_help() - return 1 - - return 0 if success else 1 - - -if __name__ == "__main__": - print(__doc__) - sys.exit(main()) diff --git a/src/GUI/example_set_mode_usage.py b/src/GUI/example_set_mode_usage.py deleted file mode 100644 index bd6c64a..0000000 --- a/src/GUI/example_set_mode_usage.py +++ /dev/null @@ -1,194 +0,0 @@ -#!/usr/bin/env python3 -""" -示例: 在 GUI 中使用 fc_network 的 set_mode 功能 - -本示例展示了如何通過 gui.py 中的 DroneMonitor 使用 fc_network_apps 的 change_mode 函數 -來改變無人機的飛行模式。 - -==================================================================================== -使用方式: -==================================================================================== - -1. 基本的模式切換流程: - - gui.py 中的 handle_mode_change(drone_id) 方法讀取 mode_combo 中選擇的模式 - - 通過 loop.create_task(self.monitor.set_mode(drone_id, mode)) 發起非同步調用 - - set_mode 方法會: - * 解析 drone_id 以提取 sysid - * 將模式名稱 (e.g., "GUIDED") 轉換為 custom_mode 值 (e.g., 4) - * 呼叫 fc_network_apps.change_mode() 函數發送 ROS2 service request - * 返回成功/失敗的結果 - -2. 支援的飛行模式 (以 ArduCopter 為例): - - STABILIZE (0) - - ACRO (1) - - ALT_HOLD (2) - - AUTO (3) - - GUIDED (4) ← 最常用 - - LOITER (5) - - RTL (6) - - CIRCLE (7) - - POSITION (8) - - LAND (9) - - SMART_RTL (21) - - 以及其他模式... - -==================================================================================== -實現細節: -==================================================================================== - -communication.py 中的 DroneMonitor 類: - - # 模式映射表 - MODE_MAPPING = { - "STABILIZE": 0, - "GUIDED": 4, - "AUTO": 3, - # ... 其他模式 - } - - async def set_mode(self, drone_id, mode_name): - ''' - 使用 fc_network_apps 的 change_mode 函數切換無人機飛行模式 - - 參數: - drone_id: 無人機ID (格式: "s{socket_id}_{sysid}") - mode_name: 模式名稱 (例如: "GUIDED", "AUTO") - - 返回: - bool: 模式切換是否成功 - ''' - # 1. 解析 drone_id 以提取 sysid - # 例如: "s0_1" -> sysid = 1 - sysid = int(drone_id.split('_')[-1]) - - # 2. 查表獲取 custom_mode 值 - # "GUIDED" -> 4 - custom_mode = self.MODE_MAPPING.get(mode_name) - - # 3. 呼叫 fc_network_apps.change_mode() - result = change_mode( - target_sysid=sysid, - custom_mode=float(custom_mode), - target_compid=0, - base_mode=1.0, # MAV_MODE_FLAG_CUSTOM_MODE_ENABLED - confirmation=0, - timeout_sec=2.0, - ) - - # 4. 返回結果 - return result.success - -==================================================================================== -GUI 中的調用流程: -==================================================================================== - -1. 用戶在 GUI 的 mode_combo 中選擇模式 (例如 "GUIDED") - -2. 用戶點擊「切換」按鈕,觸發: - button.clicked.connect(lambda: self.mode_change_requested.emit(...)) - -3. MainWindow (ControlStationUI) 的 handle_mode_change() 被呼叫: - ```python - def handle_mode_change(self, drone_id): - group = self._get_active_group() - mode = panel.mode_combo.currentText() # 例如: "GUIDED" - - loop = asyncio.get_event_loop() - future = self.monitor.set_mode(drone_id, mode) # 非同步呼叫 - loop.create_task(self.handle_service_response(future, f"切換模式 {mode}")) - ``` - -4. 在 asyncio event loop 中執行,result 被回傳給 handle_service_response() - -5. 根據結果更新 UI - -==================================================================================== -fc_network_apps.change_mode() 的實現: -==================================================================================== - -change_mode() 是一個簡單的包裝函數,用於: - -1. 創建 ROS2 node 和 client -2. 準備 MavCommandLong service request: - - command = 176 (COMMAND_DO_SET_MODE) - - param1 = base_mode (1.0) - - param2 = custom_mode (e.g., 4.0 for GUIDED) - - param3-7 = 0.0 -3. 呼叫 /fc_network/vehicle/send_command_long service -4. 等待回應並返回 ChangeModeResult (success, message, ack_result) - -等效的 ROS2 CLI 命令: - ros2 service call /fc_network/vehicle/send_command_long \\ - fc_interfaces/srv/MavCommandLong \\ - "{target_sysid: 1, target_compid: 0, command: 176, confirmation: 0, \\ - param1: 1, param2: 4, param3: 0, param4: 0, param5: 0, param6: 0, \\ - param7: 0, timeout_sec: 2}" - -==================================================================================== -注意事項: -==================================================================================== - -1. drone_id 格式: - - 形式: "s{socket_id}_{sysid}" - - 例如: "s0_1", "s0_11", "s1_2" - - set_mode() 會自動從此格式解析 sysid - -2. 模式名稱區分大小寫: - - "GUIDED" ✓ - - "guided" ✗ - - "Guided" ✗ - -3. 超時設定: - - 預設超時為 2.0 秒 - - 如果無人機無回應,會傳回 success=False - -4. 多無人機切換: - - 在 _handle_group_mode_change() 中可同時為群組內所有無人機切換模式 - - 每個無人機獨立進行 ROS2 service call - -==================================================================================== -""" - -# 示例代碼:直接使用 fc_network_apps 進行模式切換 - -def example_direct_usage(): - """直接使用 fc_network_apps 的示例""" - from fc_network_apps import change_mode - - # 切換 sysid=1 的無人機到 GUIDED 模式 - result = change_mode( - target_sysid=1, - custom_mode=4.0, # GUIDED - target_compid=0, - base_mode=1.0, - confirmation=0, - timeout_sec=2.0, - ) - - print(f"Change mode result:") - print(f" Success: {result.success}") - print(f" Message: {result.message}") - print(f" ACK Result: {result.ack_result}") - - -def example_gui_integration(): - """展示如何在 GUI 中集成 set_mode 的示例""" - import asyncio - - # 這是 gui.py 中 handle_mode_change 的典型調用模式 - async def change_drone_mode(monitor, drone_id, mode_name): - """非同步的模式切換""" - result = await monitor.set_mode(drone_id, mode_name) - return result - - # 在 event loop 中調用 - # loop = asyncio.get_event_loop() - # future = monitor.set_mode("s0_1", "GUIDED") - # loop.create_task(handle_service_response(future, "切換模式 GUIDED s0_1")) - - -if __name__ == "__main__": - print(__doc__) - print("\n" + "="*82) - print("示例代碼已準備就緒") - print("="*82)