docs: add KL720 hardware support and GUI installer design
PRD v2.6: - F17: complete GUI installer spec (Wails v2, 6-step wizard, auto deps) - F18: expand from KL520-only to KL520+KL720 dual-chip support - KDP→KDP2 firmware update, cross-chip model path resolution TDD v1.5: - Section 5.2: rewrite Kneron driver with chip-aware architecture - Section 8.5.13: dual-chip communication diagram + KL720 verification - Section 8.5.14: new GUI installer technical design (Wails, Go API, install/uninstall flow, platform-specific handling, ASCII mockups) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3f02435414
commit
f322557af3
@ -8,8 +8,8 @@
|
||||
|------|------|
|
||||
| 文件名稱 | 邊緣 AI 開發平台 PRD |
|
||||
| 產品名稱 | (暫未定名,以下稱「本平台」) |
|
||||
| 版本 | v2.5 |
|
||||
| 日期 | 2026-02-28 |
|
||||
| 版本 | v2.6 |
|
||||
| 日期 | 2026-03-01 |
|
||||
| 狀態 | 更新中 |
|
||||
|
||||
---
|
||||
@ -1211,35 +1211,49 @@ Kneron Dongle Arduino 開發板 非 Kneron 晶片
|
||||
| **解除安裝** | macOS: `rm -rf ~/.edge-ai-platform && sudo rm -f /usr/local/bin/edge-ai-server`、Windows: 刪除目錄 + 移除 PATH |
|
||||
| **啟動依賴檢查** | Server 啟動時自動檢查 ffmpeg、yt-dlp、python3,缺少時顯示對應平台的安裝指引 |
|
||||
| **GoReleaser 打包** | 跨平台 archive 自動產出(darwin amd64/arm64 tar.gz、windows amd64 zip),含 binary + data + scripts |
|
||||
| **Kneron 硬體偵測** | 安裝完成時自動偵測 USB Kneron 裝置(KL520/KL720/KL730),使用 pyusb |
|
||||
| **Kneron 硬體偵測** | 安裝完成時自動偵測 USB Kneron 裝置(KL520/KL720/KL730),使用 pyusb;KL720 KDP legacy 裝置提示韌體更新 |
|
||||
|
||||
#### F17 — 圖形化安裝與解除安裝程式(規劃中)
|
||||
#### F17 — 圖形化安裝與解除安裝程式
|
||||
|
||||
| 項目 | 規格 |
|
||||
|------|------|
|
||||
| **概述** | 提供非技術人員可使用的桌面 GUI 安裝精靈,雙擊即可安裝或解除安裝 |
|
||||
| **目標平台** | macOS (.dmg) + Windows (.exe installer) |
|
||||
| **技術方案** | macOS: DMG + .app bundle 包裝、Windows: NSIS 安裝精靈 |
|
||||
| **安裝精靈畫面** | 歡迎頁 → 安裝路徑選擇 → 可選元件(Python venv / Kneron driver)→ 安裝進度 → 完成 |
|
||||
| **解除安裝** | macOS: .app 內含解除安裝選項、Windows: 控制台「新增或移除程式」標準流程 |
|
||||
| **狀態** | 規劃中,待 CLI 安裝穩定後實作 |
|
||||
| **概述** | 提供非技術人員可使用的桌面 GUI 安裝精靈,雙擊即可完成所有安裝步驟(server binary、Python 環境、系統依賴、硬體驅動),無需開終端機或輸入任何指令 |
|
||||
| **目標平台** | macOS (.dmg → .app) + Windows (.exe installer) |
|
||||
| **技術方案** | Go + Wails v2(WebView-based GUI),前端以 HTML/CSS/JS 實作安裝畫面,後端 Go 執行實際安裝邏輯。共用 Go 工具鏈,無需額外 runtime |
|
||||
| **安裝精靈流程** | 6 步驟:(1) 歡迎頁 + 授權協議 → (2) 安裝路徑選擇 → (3) 元件選擇(必要/可選)→ (4) 自動安裝 + 即時進度 → (5) 硬體偵測結果 → (6) 完成 + 啟動選項 |
|
||||
| **必要元件(自動安裝)** | edge-ai-server binary(含嵌入式前端)、Python 3 venv + numpy + opencv-python-headless + pyusb、Kneron 韌體檔案(KL520 + KL720)、NEF 預訓練模型、libusb(USB 裝置通訊) |
|
||||
| **可選元件** | ffmpeg(攝影機/影片串流)、yt-dlp(YouTube 影片下載) |
|
||||
| **自動依賴解析** | macOS: 自動安裝 Homebrew(若未安裝)→ `brew install libusb python3 ffmpeg`;Windows: 自動下載 Python embedded + libusb DLL,免管理員權限 |
|
||||
| **即時進度顯示** | 每個安裝步驟獨立顯示進度條 + 狀態文字(下載中 → 解壓中 → 設定中 → 完成),失敗時顯示錯誤訊息 + 重試按鈕 |
|
||||
| **硬體偵測** | 安裝完成後自動掃描 USB Kneron 裝置,顯示偵測到的晶片型號(KL520/KL720)、韌體版本、連線狀態;KL720 KDP legacy 裝置提示一鍵韌體更新 |
|
||||
| **解除安裝** | 內建解除安裝功能:刪除 server binary + Python venv + 資料檔案 + symlink/PATH,macOS 提供拖曳到垃圾桶 + 深度清理選項,Windows 整合「新增或移除程式」 |
|
||||
| **安裝目錄** | macOS: `~/.edge-ai-platform/`(不需 sudo)、Windows: `%LOCALAPPDATA%\EdgeAIPlatform\`(不需管理員) |
|
||||
| **安裝完成動作** | 可選擇立即啟動 server + 自動開啟瀏覽器(`http://localhost:3721`)、建立桌面捷徑、設定開機自動啟動 |
|
||||
| **更新支援** | 偵測現有安裝版本,僅更新 binary + 新模型,保留使用者資料(custom-models、設定檔)與 Python venv |
|
||||
| **打包產出** | macOS: `EdgeAI-Installer.dmg`(含 .app + 背景圖 + Applications 捷徑)、Windows: `EdgeAI-Setup.exe`(NSIS + Wails 包裝) |
|
||||
| **安裝體積** | 完整安裝約 300-400 MB(binary ~10MB + 模型 ~73MB + Python venv ~250MB + firmware ~2MB) |
|
||||
|
||||
#### F18 — Kneron KL520 硬體通訊整合
|
||||
#### F18 — Kneron 硬體通訊整合(KL520 + KL720)
|
||||
|
||||
| 項目 | 規格 |
|
||||
|------|------|
|
||||
| **概述** | KL520 USB Dongle 的完整通訊管線已驗證,涵蓋 USB 偵測、韌體載入、模型載入、即時推論與結果後處理 |
|
||||
| **支援裝置** | Kneron KL520 (VID 0x3231, PID 0x0100),USB 2.0 High-Speed |
|
||||
| **USB Boot 模式** | KL520 無板載 Flash 韌體,每次連線時 host 自動上傳 fw_scpu.bin + fw_ncpu.bin |
|
||||
| **概述** | 雙晶片 Kneron USB Dongle 完整通訊管線已驗證,涵蓋 USB 偵測、晶片感知連線、韌體載入/更新、模型載入、即時推論與多模型後處理 |
|
||||
| **支援裝置** | Kneron KL520 (VID 0x3231, PID 0x0100, USB 2.0) + KL720 (PID 0x0720 KDP2 / PID 0x0200 KDP legacy, USB 3.0) |
|
||||
| **晶片感知架構** | Driver 從 `device_type` 或 `product_id` 自動推導晶片類型(KL520/KL720),所有行為依晶片動態調整 |
|
||||
| **KL520 USB Boot** | 無板載 Flash 韌體,每次連線自動上傳 fw_scpu.bin + fw_ncpu.bin;每 USB session 僅能載入一個模型(Error 40 限制),換模型需 reset + bridge restart |
|
||||
| **KL720 Flash-based** | KDP2 韌體預燒在 SPI Flash,連線免上傳韌體;無 Error 40 限制,可自由切換模型 |
|
||||
| **KL720 KDP→KDP2 更新** | KL720 出廠可能為舊版 KDP 韌體(PID=0x0200),透過 DFUT magic bypass (`0x1FF55B4F`) 連線後以 `kp_update_kdp_firmware_from_files()` 永久更新為 KDP2;更新後 PID 變為 0x0720 |
|
||||
| **SDK** | Kneron PLUS SDK v3.1.2,從 C 原始碼編譯為 macOS dylib(Apple Silicon 透過 Rosetta 2 執行) |
|
||||
| **通訊架構** | Go Server → JSON-RPC (stdin/stdout) → Python Bridge (`kneron_bridge.py`) → Kneron PLUS SDK (kp) → USB → KL520 |
|
||||
| **支援模型** | Kneron NEF 格式,已驗證 Tiny YOLO v3 (model_id=19, 輸入 224×224, COCO 80 類) |
|
||||
| **推論延遲** | ~25ms(KL520 NPU 硬體推論,不含圖片前處理與網路傳輸) |
|
||||
| **後處理** | YOLO v3 雙尺度偵測 (7×7 + 14×14)、Sigmoid 解碼、信心門檻過濾 (≥0.25)、NMS (IoU 0.45) |
|
||||
| **輸出格式** | 統一 `InferenceResult` JSON:taskType、latencyMs、detections[{label, confidence, bbox{x,y,width,height}}] |
|
||||
| **Python Bridge 指令** | `scan`(裝置掃描)、`connect`(連線+自動韌體載入)、`load_model`(NEF 模型載入)、`inference`(base64 圖片推論)、`disconnect` |
|
||||
| **圖片前處理** | OpenCV 解碼 → BGR565 轉換 → KP_IMAGE_FORMAT_RGB565,支援 JPEG/PNG/BMP |
|
||||
| **驗證結果** | 街道場景圖片偵測到 8 個物件(person ×2、car ×5、bicycle ×1),結果正確 |
|
||||
| **通訊架構** | Go Server → JSON-RPC (stdin/stdout) → Python Bridge (`kneron_bridge.py`) → Kneron PLUS SDK (kp) → USB → KL520/KL720 |
|
||||
| **支援模型** | Kneron NEF 格式,10+ 預訓練模型。KL520: Tiny YOLO v3、YOLOv5s、FCOS、ResNet18、SSD 人臉偵測。KL720: YOLOv5s、FCOS、ResNet18 |
|
||||
| **跨晶片模型路徑** | `resolveModelPath()` 自動將 `data/nef/kl520/kl520_20001_...` 替換為 `data/nef/kl720/kl720_20001_...`(依連線晶片),跨平台模型自動選用正確的 NEF 檔案 |
|
||||
| **推論延遲** | KL520: ~25ms(Tiny YOLO v3, 224×224)、KL720: 更快(USB 3.0 + 更強 NPU) |
|
||||
| **後處理引擎** | 多模型自動偵測:Tiny YOLO v3(雙尺度 7×7+14×14)、YOLOv5s(三尺度 P3/P4/P5)、FCOS(5 層特徵金字塔)、SSD 人臉偵測、ResNet18 分類(Top-5 softmax) |
|
||||
| **Letterbox 校正** | 自動讀取 `hw_pre_proc_info` 的 padding 參數,修正 bbox 座標以對應原始圖片比例 |
|
||||
| **輸出格式** | 統一 `InferenceResult` JSON:taskType (detection/classification)、latencyMs、detections[{label, confidence, bbox}]、classifications[{label, confidence}] |
|
||||
| **Python Bridge 指令** | `scan`、`connect`(含 `device_type` 參數)、`load_model`、`inference`、`reset`(KP_RESET_REBOOT)、`disconnect` |
|
||||
| **Error 40 處理** | KL520: `restartBridge()` → 重建整個 Python 子進程 → 重新連線 + 載入模型。KL720: 先直接 retry → 若失敗才 fallback 到 bridge restart |
|
||||
| **驗證結果** | KL520: 街道場景 8 物件偵測正確;KL720: KDP→KDP2 韌體更新成功(0x0200→0x0720)、connect/flash/inference 流程驗證 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
466
docs/TDD.md
466
docs/TDD.md
@ -7,9 +7,9 @@
|
||||
| 項目 | 內容 |
|
||||
|------|------|
|
||||
| 文件名稱 | 邊緣 AI 開發平台 TDD |
|
||||
| 對應 PRD | PRD-Integrated.md v2.5 |
|
||||
| 版本 | v1.4 |
|
||||
| 日期 | 2026-02-28 |
|
||||
| 對應 PRD | PRD-Integrated.md v2.6 |
|
||||
| 版本 | v1.5 |
|
||||
| 日期 | 2026-03-01 |
|
||||
| 狀態 | 更新中 |
|
||||
|
||||
---
|
||||
@ -1200,20 +1200,21 @@ type BBox struct {
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 Kneron Driver 實作策略(已實作)
|
||||
### 5.2 Kneron Driver 實作策略(已實作,KL520 + KL720)
|
||||
|
||||
Go Driver 透過 Python subprocess(`kneron_bridge.py`)與 Kneron PLUS SDK 通訊,採用 JSON-RPC over stdin/stdout 模式。
|
||||
Go Driver 透過 Python subprocess(`kneron_bridge.py`)與 Kneron PLUS SDK 通訊,採用 JSON-RPC over stdin/stdout 模式。支援 KL520(USB Boot)和 KL720(Flash-based)兩種晶片。
|
||||
|
||||
```
|
||||
Go Server ──stdin/stdout──► kneron_bridge.py ──ctypes──► libkplus.dylib ──USB──► KL520
|
||||
Go Server ──stdin/stdout──► kneron_bridge.py ──ctypes──► libkplus.dylib ──USB──► KL520/KL720
|
||||
JSON-RPC kp module BULK EP
|
||||
```
|
||||
|
||||
```go
|
||||
// internal/driver/kneron/kl720_driver.go
|
||||
|
||||
type KL720Driver struct {
|
||||
type KneronDriver struct {
|
||||
info driver.DeviceInfo
|
||||
chipType string // "KL520" or "KL720" — 從 info.Type 推導
|
||||
connected bool
|
||||
inferring bool
|
||||
modelLoaded string
|
||||
@ -1225,29 +1226,34 @@ type KL720Driver struct {
|
||||
pythonReady bool
|
||||
}
|
||||
|
||||
// USB 裝置識別(已確認實際值)
|
||||
const KneronVendorID uint16 = 0x3231
|
||||
// KL520 PID = 0x0100, KL720 PID = 0x0200
|
||||
|
||||
// Python venv 自動偵測:scripts/venv/bin/python3 → 系統 python3
|
||||
func (d *KL720Driver) resolvePython() string { ... }
|
||||
|
||||
// 啟動 Python bridge,設定 DYLD_LIBRARY_PATH,等待 {"status":"ready"}
|
||||
func (d *KL720Driver) startPython() error { ... }
|
||||
|
||||
// JSON-RPC 通訊:Marshal → stdin → stdout → Unmarshal
|
||||
func (d *KL720Driver) sendCommand(cmd map[string]interface{}) (map[string]interface{}, error) { ... }
|
||||
// chipType 推導邏輯
|
||||
func NewKneronDriver(info driver.DeviceInfo) *KneronDriver {
|
||||
chip := "KL520"
|
||||
if strings.Contains(strings.ToLower(info.Type), "kl720") {
|
||||
chip = "KL720"
|
||||
}
|
||||
return &KneronDriver{info: info, chipType: chip, ...}
|
||||
}
|
||||
```
|
||||
|
||||
**USB 裝置識別:**
|
||||
|
||||
| 晶片 | Vendor ID | Product ID | USB | 韌體模式 |
|
||||
|------|-----------|-----------|-----|---------|
|
||||
| KL520 | 0x3231 | 0x0100 | 2.0 High-Speed | USB Boot(每次上傳) |
|
||||
| KL720 (KDP2) | 0x3231 | 0x0720 | 3.0 Super-Speed | Flash-based(預燒) |
|
||||
| KL720 (KDP legacy) | 0x3231 | 0x0200 | 3.0 Super-Speed | 舊版韌體,需更新 |
|
||||
|
||||
**Python Bridge 指令協定:**
|
||||
|
||||
| 指令 | 參數 | 回應 | 說明 |
|
||||
|------|------|------|------|
|
||||
| `scan` | — | `{devices: [{port, firmware, kn_number, product_id}]}` | USB 裝置掃描 |
|
||||
| `connect` | `port`, `index` | `{status, firmware, kn_number}` | 連線 + 自動韌體載入 |
|
||||
| `load_model` | `path` | `{status, model_id, target_chip}` | NEF 模型載入 |
|
||||
| `inference` | `image_base64` | `{taskType, latencyMs, detections[...]}` | 圖片推論 |
|
||||
| `disconnect` | — | `{status}` | 斷線 |
|
||||
| `scan` | — | `{devices: [{port, firmware, kn_number, product_id, connectable}]}` | USB 裝置掃描 |
|
||||
| `connect` | `port`, `device_type` | `{status, firmware, kn_number, chip, kdp_legacy?}` | 晶片感知連線 + 自動韌體處理 |
|
||||
| `load_model` | `path` | `{status, model_id, model_type, input_size, target_chip}` | NEF 模型載入 + 自動類型偵測 |
|
||||
| `inference` | `image_base64` | `{taskType, latencyMs, detections[], classifications[]}` | 圖片推論(偵測或分類) |
|
||||
| `reset` | — | `{status}` | 裝置重置(KP_RESET_REBOOT) |
|
||||
| `disconnect` | — | `{status}` | 斷線 + 狀態清理 |
|
||||
|
||||
**KL520 連線流程(USB Boot 模式):**
|
||||
|
||||
@ -1261,20 +1267,95 @@ connect_devices(port_id)
|
||||
→ 回傳 firmware 版本
|
||||
```
|
||||
|
||||
**KL720 連線流程(Flash-based):**
|
||||
|
||||
```
|
||||
KL720 KDP2 (pid=0x0720):
|
||||
→ connect_devices(port_id) — 正常連線
|
||||
→ firmware 已在 flash,跳過上傳
|
||||
→ 直接就緒
|
||||
|
||||
KL720 KDP legacy (pid=0x0200):
|
||||
→ connect_devices_without_check(port_id) — 繞過韌體版本檢查
|
||||
→ load_firmware_from_file(KL720/fw_scpu.bin, fw_ncpu.bin) — 載入 KDP2 到 RAM
|
||||
→ sleep(5s) → reconnect
|
||||
→ 提示使用者執行 update_kl720_firmware.py 永久更新
|
||||
```
|
||||
|
||||
**KL720 KDP→KDP2 韌體更新流程(`update_kl720_firmware.py`):**
|
||||
|
||||
```
|
||||
1. scan_devices() → 找到 pid=0x0200 的 KL720
|
||||
2. kp_connect_devices(port, &KDP_MAGIC_CONNECTION_PASS) — DFUT magic bypass
|
||||
3. kp_update_kdp_firmware_from_files(dg, scpu, NULL) — flash SCPU
|
||||
4. kp_reset_device() → sleep(3s) → reconnect
|
||||
5. kp_update_kdp_firmware_from_files(dg, NULL, ncpu) — flash NCPU
|
||||
6. kp_reset_device() → verify pid=0x0720
|
||||
```
|
||||
|
||||
**晶片感知 Error 40 處理(`Flash()` 方法):**
|
||||
|
||||
```
|
||||
KL520 — Error 40 (KP_ERROR_USB_BOOT_LOAD_SECOND_MODEL_40):
|
||||
→ restartBridge() — 完全重啟 Python 子進程
|
||||
→ 重新 connect + load_firmware
|
||||
→ retry load_model
|
||||
|
||||
KL720 — Error 40(理論上不會發生):
|
||||
→ 先直接 retry load_model(不需 restart)
|
||||
→ 若仍失敗才 fallback 到 restartBridge()
|
||||
```
|
||||
|
||||
**跨晶片模型路徑解析(`flash/service.go`):**
|
||||
|
||||
```go
|
||||
// resolveModelPath 自動替換晶片特定 NEF 路徑
|
||||
// 例:data/nef/kl520/kl520_20001_... → data/nef/kl720/kl720_20001_...
|
||||
func resolveModelPath(filePath string, deviceType string) string {
|
||||
targetChip := chipFromDeviceType(deviceType) // "kl520" or "kl720"
|
||||
if alreadyTargetChip(filePath, targetChip) { return filePath }
|
||||
candidate := swapChipPrefix(filePath, sourceChip, targetChip)
|
||||
if fileExists(candidate) { return candidate }
|
||||
return filePath // fallback 原路徑
|
||||
}
|
||||
```
|
||||
|
||||
**多模型後處理引擎(`kneron_bridge.py`):**
|
||||
|
||||
| 模型類型 | Model ID | 輸入尺寸 | 後處理 | 適用晶片 |
|
||||
|---------|----------|---------|--------|---------|
|
||||
| Tiny YOLO v3 | 0/19 | 224×224 | 雙尺度 YOLO decode + NMS | KL520 |
|
||||
| YOLOv5s | 20005 | 640×640 | 三尺度 YOLO decode + NMS | KL520, KL720 |
|
||||
| FCOS | 20004 | 512×512 | 5 層 FPN decode + NMS | KL520, KL720 |
|
||||
| SSD (人臉) | — | 320×320 | SSD bbox decode + NMS | KL520 |
|
||||
| ResNet18 | 20001 | 224×224 | Softmax + Top-5 分類 | KL520, KL720 |
|
||||
|
||||
### 5.3 Driver 註冊流程
|
||||
|
||||
```go
|
||||
// main.go or cmd/root.go
|
||||
|
||||
func setupDrivers(registry *device.DriverRegistry) {
|
||||
// 註冊 Kneron KL720 Driver
|
||||
// 註冊 Kneron Driver(KL520 + KL720)
|
||||
// detector.go 的 chipFromProductID() 自動判斷晶片型號
|
||||
// KL520: PID=0x0100, KL720: PID=0x0200(KDP)/0x0720(KDP2)
|
||||
registry.Register(device.DriverFactory{
|
||||
Name: "kneron_kl520",
|
||||
Match: func(info USBDeviceInfo) bool {
|
||||
return info.VendorID == 0x3231 && info.ProductID == 0x0100
|
||||
},
|
||||
Create: func(info USBDeviceInfo) (DeviceDriver, error) {
|
||||
return kneron.NewKneronDriver(info)
|
||||
},
|
||||
})
|
||||
registry.Register(device.DriverFactory{
|
||||
Name: "kneron_kl720",
|
||||
Match: func(info USBDeviceInfo) bool {
|
||||
return info.VendorID == 0x3231 && info.ProductID == 0x0200
|
||||
return info.VendorID == 0x3231 &&
|
||||
(info.ProductID == 0x0200 || info.ProductID == 0x0720)
|
||||
},
|
||||
Create: func(info USBDeviceInfo) (DeviceDriver, error) {
|
||||
return kneron.NewKL720Driver(info)
|
||||
return kneron.NewKneronDriver(info)
|
||||
},
|
||||
})
|
||||
|
||||
@ -1658,23 +1739,28 @@ Server 啟動 → deps.PrintStartupReport(logger)
|
||||
| macOS Apple Silicon | tar.gz | `edge-ai-platform_v0.1.0_darwin_arm64.tar.gz` |
|
||||
| Windows x64 | zip | `edge-ai-platform_v0.1.0_windows_amd64.zip` |
|
||||
|
||||
每個 archive 含:`edge-ai-server` binary + `data/models.json` + `scripts/kneron_bridge.py` + `scripts/requirements.txt` + `scripts/firmware/KL520/`
|
||||
每個 archive 含:`edge-ai-server` binary + `data/models.json` + `scripts/kneron_bridge.py` + `scripts/requirements.txt` + `scripts/firmware/KL520/` + `scripts/firmware/KL720/`
|
||||
|
||||
#### 8.5.13 Kneron KL520 硬體通訊整合(F18)
|
||||
#### 8.5.13 Kneron 硬體通訊整合(F18 — KL520 + KL720)
|
||||
|
||||
| 前端元件 | 後端模組 | 腳本/資源 |
|
||||
|---------|---------|----------|
|
||||
| 既有 inference panel | `internal/driver/kneron/kl720_driver.go` | `scripts/kneron_bridge.py` |
|
||||
| 既有 device panel | `internal/driver/kneron/detector.go` | `scripts/firmware/KL520/fw_scpu.bin` |
|
||||
| — | `internal/driver/interface.go` | `scripts/firmware/KL520/fw_ncpu.bin` |
|
||||
| 既有 device panel | `internal/driver/kneron/detector.go` | `scripts/firmware/KL520/fw_{scpu,ncpu}.bin` |
|
||||
| — | `internal/flash/service.go` (resolveModelPath) | `scripts/firmware/KL720/fw_{scpu,ncpu}.bin` |
|
||||
| — | `internal/driver/interface.go` | `scripts/update_kl720_firmware.py` |
|
||||
| — | — | `data/nef/kl720/kl720_2000{1,4,5}_*.nef` |
|
||||
|
||||
**通訊架構:**
|
||||
**通訊架構(晶片感知):**
|
||||
|
||||
```
|
||||
┌──────────┐ JSON-RPC ┌──────────────────┐ ctypes ┌──────────────┐ USB BULK ┌───────┐
|
||||
│ Go Server│──stdin/out──►│ kneron_bridge.py │──────────►│libkplus.dylib│──────────►│ KL520 │
|
||||
│ (Gin) │◄────────────│ (Python 3.9) │◄──────────│(Kneron PLUS) │◄──────────│ (NPU) │
|
||||
└──────────┘ └──────────────────┘ └──────────────┘ └───────┘
|
||||
┌──────────┐ JSON-RPC ┌──────────────────┐ ctypes ┌──────────────┐ USB BULK ┌─────────────┐
|
||||
│ Go Server│──stdin/out──►│ kneron_bridge.py │──────────►│libkplus.dylib│──────────►│ KL520 (2.0) │
|
||||
│ (Gin) │◄────────────│ (Python 3.9) │◄──────────│(Kneron PLUS) │◄──────────│ KL720 (3.0) │
|
||||
└──────────┘ └──────────────────┘ └──────────────┘ └─────────────┘
|
||||
│ │
|
||||
│ chipType="KL520"/"KL720" │ _device_chip 全域變數
|
||||
│ device_type 參數傳遞 │ 依晶片調整連線/韌體邏輯
|
||||
```
|
||||
|
||||
**kneron_bridge.py 推論流程:**
|
||||
@ -1700,19 +1786,27 @@ inf_config = kp.GenericImageInferenceDescriptor(
|
||||
kp.inference.generic_image_inference_send(device_group, inf_config)
|
||||
result = kp.inference.generic_image_inference_receive(device_group)
|
||||
|
||||
# 5. YOLO v3 後處理:decode → sigmoid → NMS → detections[]
|
||||
# 5. 依 model_type 自動選擇後處理
|
||||
# tiny_yolov3 → _parse_yolo_output(ANCHORS_TINY_YOLOV3)
|
||||
# yolov5s → _parse_yolo_output(ANCHORS_YOLOV5S)
|
||||
# fcos → _parse_fcos_output()
|
||||
# ssd → _parse_ssd_output()
|
||||
# resnet18 → _parse_classification_output()
|
||||
```
|
||||
|
||||
**YOLO v3 後處理細節:**
|
||||
**後處理引擎細節:**
|
||||
|
||||
| 項目 | 規格 |
|
||||
|------|------|
|
||||
| 偵測頭 | 雙尺度:7×7(大物件)+ 14×14(小物件) |
|
||||
| Anchor boxes | 7×7: (81,82), (135,169), (344,319);14×14: (10,14), (23,27), (37,58) |
|
||||
| 輸出通道 | 255 = 3 anchors × (5 + 80 classes) |
|
||||
| 信心門檻 | ≥ 0.25 |
|
||||
| NMS IoU 門檻 | 0.45 |
|
||||
| 類別 | COCO 80 類(person, bicycle, car, ... toothbrush) |
|
||||
| 模型 | 偵測頭 / 輸出 | Anchor / 架構 | 信心門檻 | NMS IoU |
|
||||
|------|-------------|--------------|---------|---------|
|
||||
| Tiny YOLO v3 | 雙尺度 7×7 + 14×14 | (81,82)(135,169)(344,319) / (10,14)(23,27)(37,58) | ≥ 0.25 | 0.45 |
|
||||
| YOLOv5s | 三尺度 P5/P4/P3 | (116,90)(156,198)(373,326) / (30,61)(62,45)(59,119) / (10,13)(16,30)(33,23) | ≥ 0.25 | 0.45 |
|
||||
| FCOS | 5 層 FPN (stride 8~128) | Anchor-free (centerness × cls) | ≥ 0.25 | 0.45 |
|
||||
| SSD 人臉 | 2 類(bg + face) | SSD prior boxes | ≥ 0.25 | 0.45 |
|
||||
| ResNet18 | 1000 類 softmax | Top-5 排序 | — | — |
|
||||
|
||||
**Letterbox 校正(`_correct_bbox_for_letterbox`):**
|
||||
|
||||
SDK 的 `hw_pre_proc_info` 提供 `pad_left`, `pad_top`, `resized_w`, `resized_h` 等參數,後處理自動從模型空間轉換到原始圖片空間,消除 padding 偏移。
|
||||
|
||||
**macOS Apple Silicon 相容性:**
|
||||
|
||||
@ -1725,7 +1819,7 @@ pendian.h 修正:#if defined(__APPLE__) #include <machine/endian.h>
|
||||
CMakeLists.txt 修正:移除 -Werror,改用 -Wno-unused-but-set-variable
|
||||
```
|
||||
|
||||
**驗證結果(bike_cars_street_224x224.bmp):**
|
||||
**KL520 驗證結果(bike_cars_street_224x224.bmp):**
|
||||
|
||||
| 物件 | 信心值 | BBox (x, y, w, h) |
|
||||
|------|--------|-------------------|
|
||||
@ -1738,7 +1832,283 @@ CMakeLists.txt 修正:移除 -Werror,改用 -Wno-unused-but-set-variable
|
||||
| car | 0.368 | (0.16, 0.37, 0.05, 0.05) |
|
||||
| car | 0.294 | (0.45, 0.43, 0.06, 0.09) |
|
||||
|
||||
推論延遲:~25ms(NPU 硬體推論)
|
||||
推論延遲:KL520 ~25ms(NPU 硬體推論)
|
||||
|
||||
**KL720 驗證結果:**
|
||||
|
||||
| 項目 | 結果 |
|
||||
|------|------|
|
||||
| 韌體更新 | KDP→KDP2 成功(PID 0x0200→0x0720,firmware: KDP2 Comp/F) |
|
||||
| 連線 | `connect_devices()` 正常連線 |
|
||||
| 模型載入 | NEF 模型載入成功(跨晶片路徑解析正確) |
|
||||
| USB 速度 | USB 3.0 Super-Speed |
|
||||
|
||||
#### 8.5.14 圖形化安裝與解除安裝程式(F17)
|
||||
|
||||
**技術選型:Wails v2**
|
||||
|
||||
| 項目 | 規格 |
|
||||
|------|------|
|
||||
| 框架 | [Wails v2](https://wails.io/) — Go + WebView GUI 框架 |
|
||||
| 選型理由 | 共用 Go 工具鏈(不需 Node.js runtime)、單一 binary 產出、WebView 體積小(~5MB vs Electron ~150MB)、macOS/Windows 原生 WebView 支援 |
|
||||
| 前端 | 輕量 HTML/CSS/JS(vanilla 或 Preact),不使用 React 以減少安裝程式體積 |
|
||||
| 後端 | Go — 執行實際安裝邏輯(檔案操作、下載、venv 建立、硬體偵測) |
|
||||
| 產出 | macOS: `.app` → `.dmg`、Windows: `.exe`(Wails 內建 NSIS 包裝) |
|
||||
| 架構分離 | Installer 是獨立 Go module(`installer/`),與 server 分開編譯,但共用 GoReleaser 發布 |
|
||||
|
||||
**安裝程式目錄結構:**
|
||||
|
||||
```
|
||||
installer/
|
||||
├── main.go # Wails 應用程式入口
|
||||
├── app.go # Go 後端邏輯(安裝/解除安裝/偵測)
|
||||
├── wails.json # Wails 專案設定
|
||||
├── frontend/
|
||||
│ ├── index.html # 安裝精靈 UI
|
||||
│ ├── style.css # 樣式(含深色模式)
|
||||
│ └── main.js # 前端邏輯(步驟導航、進度更新)
|
||||
└── build/
|
||||
├── darwin/
|
||||
│ └── Info.plist # macOS app metadata
|
||||
└── windows/
|
||||
└── installer.nsi # NSIS 安裝腳本
|
||||
```
|
||||
|
||||
**Go 後端 API(Wails binding → 前端可呼叫):**
|
||||
|
||||
```go
|
||||
// installer/app.go
|
||||
|
||||
type Installer struct {
|
||||
ctx context.Context
|
||||
installDir string
|
||||
platform string // "darwin" / "windows"
|
||||
arch string // "amd64" / "arm64"
|
||||
progress chan StepProgress
|
||||
}
|
||||
|
||||
// 安裝流程 API
|
||||
func (a *Installer) GetSystemInfo() SystemInfo // 偵測 OS、已安裝版本、磁碟空間
|
||||
func (a *Installer) SetInstallDir(dir string) error // 設定安裝路徑
|
||||
func (a *Installer) GetComponents() []Component // 取得可安裝元件清單
|
||||
func (a *Installer) Install(components []string) error // 執行安裝(串流進度)
|
||||
func (a *Installer) DetectHardware() []HardwareInfo // 掃描 Kneron USB 裝置
|
||||
func (a *Installer) UpdateKL720Firmware() error // KDP→KDP2 韌體更新
|
||||
func (a *Installer) LaunchServer() error // 啟動 edge-ai-server
|
||||
func (a *Installer) OpenBrowser() error // 開啟瀏覽器
|
||||
|
||||
// 解除安裝 API
|
||||
func (a *Installer) GetInstalledInfo() InstalledInfo // 讀取已安裝版本 + 大小
|
||||
func (a *Installer) Uninstall(keepData bool) error // 解除安裝(可選保留資料)
|
||||
|
||||
// 進度回報(透過 Wails Events 推送到前端)
|
||||
type StepProgress struct {
|
||||
Step string `json:"step"` // "download", "extract", "python", "libusb", ...
|
||||
Percent int `json:"percent"` // 0-100
|
||||
Message string `json:"message"` // 人類可讀狀態
|
||||
Error string `json:"error"` // 錯誤訊息(空=正常)
|
||||
}
|
||||
```
|
||||
|
||||
**安裝精靈 UI 流程(6 步驟):**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Step 1: 歡迎頁 │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────┐ │
|
||||
│ │ 🔧 Edge AI Platform Installer │ │
|
||||
│ │ │ │
|
||||
│ │ 版本: v0.2.0 │ │
|
||||
│ │ 安裝大小: ~350 MB │ │
|
||||
│ │ │ │
|
||||
│ │ ☐ 我同意授權條款 │ │
|
||||
│ │ │ │
|
||||
│ │ [下一步 →] │ │
|
||||
│ └─────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Step 2: 安裝路徑 │
|
||||
│ │
|
||||
│ 安裝位置: │
|
||||
│ ┌──────────────────────────────┐ [瀏覽...] │
|
||||
│ │ ~/.edge-ai-platform │ │
|
||||
│ └──────────────────────────────┘ │
|
||||
│ 可用空間: 58.3 GB │
|
||||
│ 需要空間: ~350 MB │
|
||||
│ │
|
||||
│ [← 上一步] [下一步 →] │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Step 3: 元件選擇 │
|
||||
│ │
|
||||
│ 必要元件: │
|
||||
│ ☑ Edge AI Server (10 MB) ── 核心伺服器 │
|
||||
│ ☑ Python 環境 (250 MB) ── Kneron SDK 執行 │
|
||||
│ ☑ 預訓練模型 (73 MB) ── KL520/KL720 NEF │
|
||||
│ ☑ 韌體檔案 (2 MB) ── KL520/KL720 FW │
|
||||
│ ☑ USB 驅動 (libusb) (1 MB) ── 裝置通訊 │
|
||||
│ │
|
||||
│ 可選元件: │
|
||||
│ ☐ ffmpeg (80 MB) ── 攝影機/影片串流 │
|
||||
│ ☐ yt-dlp (15 MB) ── YouTube 影片 │
|
||||
│ │
|
||||
│ [← 上一步] [安裝 →] │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Step 4: 安裝進度 │
|
||||
│ │
|
||||
│ ✅ 下載 Edge AI Server 完成 │
|
||||
│ ✅ 解壓縮檔案 完成 │
|
||||
│ ✅ 安裝 libusb 完成 │
|
||||
│ 🔄 建立 Python 環境 62% │
|
||||
│ ┌──────────────████████░░░░░░──────┐ │
|
||||
│ │ 安裝 numpy... │ │
|
||||
│ └──────────────────────────────────┘ │
|
||||
│ ○ 安裝模型檔案 等待中 │
|
||||
│ ○ 偵測硬體 等待中 │
|
||||
│ │
|
||||
│ [取消] │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Step 5: 硬體偵測 │
|
||||
│ │
|
||||
│ 偵測到的 Kneron 裝置: │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────┐ │
|
||||
│ │ ✅ KL720 (port 136) │ │
|
||||
│ │ Firmware: KDP2 Comp/F │ │
|
||||
│ │ PID: 0x0720 │ │
|
||||
│ ├──────────────────────────────────┤ │
|
||||
│ │ ✅ KL520 (port 33) │ │
|
||||
│ │ Firmware: Loader │ │
|
||||
│ │ PID: 0x0100 │ │
|
||||
│ └──────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ⚠️ 未偵測到裝置?請確認 USB 連接 │
|
||||
│ │
|
||||
│ [← 上一步] [下一步 →] │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Step 6: 完成 │
|
||||
│ │
|
||||
│ ✅ 安裝成功! │
|
||||
│ │
|
||||
│ 安裝位置: ~/.edge-ai-platform │
|
||||
│ 版本: v0.2.0 │
|
||||
│ 安裝大小: 342 MB │
|
||||
│ │
|
||||
│ ☑ 立即啟動 Edge AI Server │
|
||||
│ ☑ 開啟瀏覽器 (http://localhost:3721) │
|
||||
│ ☐ 建立桌面捷徑 │
|
||||
│ ☐ 開機自動啟動 │
|
||||
│ │
|
||||
│ [完成] │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**安裝步驟實作細節:**
|
||||
|
||||
| 步驟 | macOS 實作 | Windows 實作 |
|
||||
|------|-----------|-------------|
|
||||
| 下載 binary | `net/http` GET → Gitea release tar.gz | `net/http` GET → Gitea release zip |
|
||||
| 解壓 | `archive/tar` + `compress/gzip` | `archive/zip` |
|
||||
| symlink | `os.Symlink()` → `/usr/local/bin/edge-ai-server` (需確認權限) | 加入 User PATH (`os.Setenv` + registry) |
|
||||
| libusb | `exec.Command("brew", "install", "libusb")` 或內嵌 dylib | 內嵌 `libusb-1.0.dll` 到安裝目錄 |
|
||||
| Python venv | `exec.Command("python3", "-m", "venv", ...)` | `exec.Command("python", "-m", "venv", ...)` 或內嵌 Python embedded |
|
||||
| pip install | `venv/bin/pip install numpy opencv-python-headless pyusb` | `venv\Scripts\pip install ...` |
|
||||
| ffmpeg | `brew install ffmpeg` (可選) | `winget install Gyan.FFmpeg` 或內嵌 (可選) |
|
||||
| 硬體偵測 | `exec.Command(python, "kneron_detect.py")` | 同左 |
|
||||
| 桌面捷徑 | `~/Desktop/EdgeAI.command` | `shell:desktop\EdgeAI.lnk` (COM API) |
|
||||
| 開機啟動 | `~/Library/LaunchAgents/com.innovedus.edge-ai.plist` | `HKCU\...\Run` registry |
|
||||
|
||||
**macOS 無 Homebrew 情境處理:**
|
||||
|
||||
```
|
||||
if !commandExists("brew") {
|
||||
// 方案 A: 提示安裝 Homebrew(推薦)
|
||||
// 方案 B: 直接下載 libusb dylib 放入安裝目錄
|
||||
// 設定 DYLD_LIBRARY_PATH 環境變數
|
||||
}
|
||||
```
|
||||
|
||||
**Windows 無 Python 情境處理:**
|
||||
|
||||
```
|
||||
if !commandExists("python") && !commandExists("python3") {
|
||||
// 方案 A: 自動下載 Python embeddable package (無需安裝)
|
||||
// 解壓到 installDir/python/,直接使用
|
||||
// 方案 B: 提示使用者安裝 Python (winget install Python.Python.3.12)
|
||||
}
|
||||
```
|
||||
|
||||
**解除安裝流程:**
|
||||
|
||||
```go
|
||||
func (a *Installer) Uninstall(keepData bool) error {
|
||||
// 1. 停止正在運行的 edge-ai-server 進程
|
||||
killProcess("edge-ai-server")
|
||||
|
||||
// 2. 移除 symlink / PATH
|
||||
// macOS: os.Remove("/usr/local/bin/edge-ai-server")
|
||||
// Windows: 從 User PATH 移除安裝目錄
|
||||
|
||||
// 3. 移除安裝目錄
|
||||
if keepData {
|
||||
// 保留 data/custom-models/(使用者自訂模型)
|
||||
removeAllExcept(installDir, "data/custom-models")
|
||||
} else {
|
||||
os.RemoveAll(installDir)
|
||||
}
|
||||
|
||||
// 4. 移除桌面捷徑 / 開機啟動
|
||||
removeDesktopShortcut()
|
||||
removeAutoStart()
|
||||
|
||||
// 5. macOS: 不需要額外清理(brew 套件保留)
|
||||
// Windows: 不移除 Python(可能被其他程式使用)
|
||||
}
|
||||
```
|
||||
|
||||
**更新流程(覆蓋安裝):**
|
||||
|
||||
```go
|
||||
func (a *Installer) Install(components []string) error {
|
||||
existing := a.GetInstalledInfo()
|
||||
if existing.Version != "" {
|
||||
// 升級模式:保留 venv + custom-models + 設定
|
||||
// 只更新 binary + 預訓練模型 + firmware + scripts
|
||||
backupUserData()
|
||||
installNewVersion()
|
||||
restoreUserData()
|
||||
} else {
|
||||
// 全新安裝
|
||||
fullInstall()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**打包與發布:**
|
||||
|
||||
```yaml
|
||||
# .goreleaser.yaml — Installer 額外產出
|
||||
builds:
|
||||
- id: installer
|
||||
dir: installer
|
||||
binary: EdgeAI-Installer
|
||||
goos: [darwin, windows]
|
||||
goarch: [amd64, arm64]
|
||||
hooks:
|
||||
pre: wails build -platform {{ .Os }}/{{ .Arch }}
|
||||
|
||||
# macOS: create-dmg 產出 .dmg
|
||||
# Windows: NSIS 包裝成 Setup.exe
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user