From 4d27951ea6e28e888c9a72e5a7ba26582c8ecd2b Mon Sep 17 00:00:00 2001 From: miketsai Date: Sun, 12 Apr 2026 09:42:04 +0000 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E5=82=B3=E6=AA=94=E6=A1=88=E5=88=B0?= =?UTF-8?q?=E3=80=8Cdocs=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/allxon_ota.md | 139 ++++++ docs/technical_report.md | 923 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1062 insertions(+) create mode 100644 docs/allxon_ota.md create mode 100644 docs/technical_report.md diff --git a/docs/allxon_ota.md b/docs/allxon_ota.md new file mode 100644 index 0000000..43ac05e --- /dev/null +++ b/docs/allxon_ota.md @@ -0,0 +1,139 @@ +# Allxon 奧創雲 OTA 與事件上傳架構 + +## 相關資源位置 + +| 資源 | 路徑 | +|------|------| +| kCurl 執行檔 | `/home/user/Documents/GOFACE/奧創雲/kCurl-1.1.1-release/` | +| kCurl ARMv7 binary | `kCurl-linux-armv7`(給 KL630 用)| +| golf.cgi (OOB Enabler CGI) | `/home/user/Documents/GOFACE/奧創雲/golf/content/golf.cgi` | +| golf.cgi 安裝 script | `golf/install.sh` → 複製到 OOB Enabler `/usr/local/sbin/www/api/` | +| Wrapper_mantratec | Vatics VML 建置系統(與 OTA 無關,是 VMF SDK 的 driver/lib 框架)| + +--- + +## kCurl 工具說明(v1.1.1) + +kCurl 是 Allxon 提供的輕量 HTTP 工具,有兩個模式: + +### Client 模式(KL630 → OOB Enabler) + +```sh +kCurl-linux-armv7 --data-binary @/tmp/sdcard/events/event_001.tar.gz \ + http://169.254.208.208/api/golf.cgi +``` + +- POST binary 檔案到 OOB Enabler 的 golf.cgi +- golf.cgi 計算 MD5 後回傳 `{"MD5": "..."}`,並將資料轉發至 Allxon Cloud +- golf.cgi 限制:只接受 192.168.*、169.254.*、10.* 的 REMOTE_ADDR + +### Server 模式(OOB Enabler → KL630,用於 OTA) + +```sh +kCurl-linux-armv7 -server -port 8192 -config /mnt/flash/vienna/ota_config.json & +``` + +- 在 KL630 上監聽 port 8192 +- 收到檔案後,存到 `data-store-path`,再執行 `post-command` +- `$UPLOADED_FILE_PATH` 是自動帶入的收到檔案路徑 + +**`ota_config.json` 範例:** +```json +{ + "data-store-path": "/tmp/sdcard/ota", + "post-command": "sh /mnt/flash/vienna/ota_update.sh $UPLOADED_FILE_PATH" +} +``` + +--- + +## 雙向資料流架構 + +``` +┌─────────────────────────────────────────────────────────┐ +│ Allxon Cloud │ +└──────────┬──────────────────────────┬───────────────────┘ + │ 接收事件上傳 │ 下發 OTA 韌體 + ▼ ▼ +┌─────────────────────────────────────────────────────────┐ +│ OOB Enabler Bolt (169.254.208.208) │ +│ - 獨立 4G LTE 上網 │ +│ - golf.cgi 安裝於 /usr/local/sbin/www/api/ │ +│ - 內建 NTP(提供 KL630 對時用) │ +└──────────┬──────────────────────────┬───────────────────┘ + │ golf.cgi (HTTP POST) │ kCurl client POST 韌體 + │ ← KL630 上傳事件 │ → KL630:8192 + ▼ ▼ +┌─────────────────────────────────────────────────────────┐ +│ KL630 (ARMv7, uClibc) │ +│ │ +│ [事件上傳] kCurl client → POST tar.gz to golf.cgi │ +│ [OTA接收] kCurl server (port 8192) ← 接收韌體 │ +│ └─ post-command: ota_update.sh │ +└─────────────────────────────────────────────────────────┘ +``` + +--- + +## 事件上傳流程(KL630 → Cloud) + +``` +STDC 偵測違規 + → 抓截圖 (JPEG) + → 打包 tar.gz(含 level1.jpg / level2.jpg / level3.jpg + event.json) + → 存入 /tmp/sdcard/events/ + → kCurl-linux-armv7 --data-binary @event.tar.gz http://169.254.208.208/api/golf.cgi + → OOB Enabler 轉發至 Allxon Cloud +``` + +--- + +## OTA 韌體更新流程(Cloud → KL630) + +1. 在 KL630 啟動時跑 kCurl server 常駐 +2. Allxon Cloud 下發 OTA 觸發 → OOB Enabler 用 kCurl client POST 韌體到 KL630:8192 +3. kCurl server 收到後儲存,執行 `ota_update.sh` + +**`ota_update.sh` 邏輯草稿:** +```sh +#!/bin/sh +NEW_BIN="$1" +TARGET="/mnt/flash/vienna/kp_firmware_host_stream" + +# 停止現有韌體 +killall kp_firmware_host_stream + +# 替換 binary(flash 需 remount rw) +mount -o remount,rw /mnt/flash +cp "$NEW_BIN" "$TARGET" +chmod +x "$TARGET" +mount -o remount,ro /mnt/flash + +# 重啟韌體 +/mnt/flash/vienna/start_firmware.sh & +``` + +**待確認:** +- `/mnt/flash` 是否允許 remount rw?還是 binary 放在 SD 卡? +- Allxon Cloud 下發 OTA 的具體觸發機制(是 golf.cgi 擴充還是 OOB Enabler 的另一個 API)? +- kCurl server 在 OOB Enabler 上如何被觸發去 POST 到 KL630(需看 Allxon Platform 的 OTA flow) + +--- + +## golf.cgi 說明 + +- 編譯環境:GCC 9.4.0 Buildroot allxon-bolt-standard,目標 ARMv7 uClibc +- 安裝位置(OOB Enabler 上):`/usr/local/sbin/www/api/golf.cgi` +- 功能:接收 POST binary → 計算 MD5 → 回傳 JSON → 轉發至 Cloud +- IP 白名單:僅接受 `192.168.*`、`169.254.*`、`10.*`(本地網路保護) + +--- + +## Wrapper_mantratec 說明(非 OTA 相關) + +這是 Vatics VML(Vatics Middleware Library)Package 的建置系統框架: +- `drivers/` — Linux kernel modules(vpl_dmac_v2.0.0.3) +- `include/` — 共用 headers(dbgdefs.h, typedef.h, vivo_codec.h 等) +- `scripts/` — Makefile 建置系統 + +與 Allxon OTA 無關。是 VMF Vienna SDK 的 driver/library 開發框架,若未來需要自訂 KL630 上的 kernel module 或 VMF library 時才會用到。 diff --git a/docs/technical_report.md b/docs/technical_report.md new file mode 100644 index 0000000..b023214 --- /dev/null +++ b/docs/technical_report.md @@ -0,0 +1,923 @@ +# KL630 Host Stream 技術報告 + +## 目錄 + +1. [系統概覽](#1-系統概覽) +2. [硬體架構](#2-硬體架構) +3. [軟體管線架構](#3-軟體管線架構) +4. [IMX662 DOL-HDR 雙曝光 ISP](#4-imx662-dol-hdr-雙曝光-isp) +5. [程式碼結構詳解](#5-程式碼結構詳解) +6. [從零開始:新裝置完整部署流程](#6-從零開始新裝置完整部署流程) +7. [常見問題排查](#7-常見問題排查) +8. [關鍵參數速查表](#8-關鍵參數速查表) +9. [STDC 語義分割邏輯詳解](#9-stdc-語義分割邏輯詳解) +10. [下一階段:高爾夫球車警示與煞車控制](#10-下一階段高爾夫球車警示與煞車控制) + +--- + +## 1. 系統概覽 + +本專案在 Kneron KL630 AI SoC 上實現一個完整的即時視覺 AI 推論管線: + +``` +IMX662 魚眼攝影機 (DOL-HDR 雙曝光) + │ + ▼ + KL630 ISP / IFP + (去馬賽克、AWB、AE、色調映射) + │ + ▼ + VMF 影像管線 (YM12 planar YUV420) + ├── Stream 0: 1920×1080 → H.264 → RTSP + └── Stream 1: 724×362 → NPU 推論 (STDC 語義分割) + │ + ▼ + KL630 NPU (STDC_0520.nef) + 語義分割:道路 / 車輛 / 行人 + │ + ▼ + 結果疊加 → HDMI (VOC) 或 RTSP 輸出 +``` + +**主要元件版本:** + +| 元件 | 版本 / 規格 | +|------|------------| +| SoC | Kneron KL630 (ARMv7-A Cortex-A7) | +| SDK | VMF Vienna SDK 2.5.6 | +| 感測器 | Sony IMX662 1920×1080 DOL-HDR | +| AI 模型 | STDC (Short-Term Dense Concatenate) 語義分割,ModelId=32769 | +| 執行環境 | uClibc 1.0.34,LD_LIBRARY_PATH=/mnt/flash/vienna/lib | + +--- + +## 2. 硬體架構 + +### KL630 SoC 功能方塊 + +``` +┌──────────────────────────────────────────────────────────┐ +│ KL630 │ +│ │ +│ MIPI CSI-2 ──► IFP (Image Front-end Processor) │ +│ │ Bayer RAW 處理 │ +│ │ DOL-HDR 合成 (長短曝光 Bayer → HDR) │ +│ ▼ │ +│ ISP (Image Signal Processor) │ +│ │ AWB / AE / GTR (Global Tone Remap) │ +│ │ 輸出:YM12 (planar YUV420) │ +│ ▼ │ +│ ┌─────────────────────────────────────┐ │ +│ │ VMF (Vatics Media Framework) │ │ +│ │ SSM 共享記憶體環形緩衝區 │ │ +│ │ Video Encoder (H.264/H.265/MJPEG) │ │ +│ │ VOC (Video Output Component/HDMI) │ │ +│ └─────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ NPU (Neural Processing Unit) │ +│ │ 執行 .nef 模型 │ +│ │ FifoQ 非同步推論佇列 │ +│ ▼ │ +│ 推論結果回傳 → 主程式後處理 │ +│ │ +│ /mnt/flash/ ── eMMC flash,重開機資料保留 │ +│ /dev/shm/ ── 揮發性 RAM shared memory │ +└──────────────────────────────────────────────────────────┘ +``` + +### 儲存空間配置 + +``` +/mnt/flash/vienna/ +├── kp_firmware_host_stream ← 主程式 binary +├── lib/ ← VMF 動態函式庫 +├── demo_rtsp.sh ← RTSP 模式啟動腳本 +├── demo_hdmi.sh ← HDMI 模式啟動腳本 +├── demo_rtsp_hdmi.sh ← RTSP+HDMI 同時輸出啟動腳本 +└── drivers/ ← 硬體 kernel module + ├── driver.sh ← 一鍵載入所有驅動腳本 + ├── vpl_edmc_v6.3.0.2.ko ← EDMC DMA 控制器(必須第一個載入) + ├── vpl_dmac_v2.0.0.3.ko + ├── vma_ifpe_v1.1.0.0.ko ← IFP 前端處理 + ├── vma_ispe_v2.0.0.5.ko ← ISP 影像處理 + ├── vpl_voc_v2.2.0.1.ko ← VOC HDMI 輸出 + ├── it66121enc_v1.0.0.3.ko ← HDMI encoder + ├── IMX662_v1.0.0.1.ko ← 感測器驅動 + └── ...(其他 VMA/VPL 模組) + +/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin/ +├── ini/ +│ ├── host_stream.ini ← 主設定檔 +│ ├── demo_rtsp.sh ← 腳本副本(從 vienna/ 複製) +│ ├── demo_hdmi.sh +│ ├── demo_rtsp_hdmi.sh +│ └── fec_calibrate.ini / fec_conf.ini +├── nef/ +│ └── STDC_0520.nef ← NPU 模型 +└── Resource/ + ├── VIC/0/imx662_1920x1080_ch0.cfg ← 主感測器設定 + ├── VIC/1/imx662_1920x1080_ch1.cfg ← Fusion (短曝光通道) + ├── AWB/AutoWhiteBalance.ini + └── ISP/0/ 及 ISP/1/ + └── pqtable_ispe_Config.cfg +``` + +> **重要:** `/mnt/flash/etc/` 雖然是 UBIFS 分區的一部分,但寫入的變更在斷電後不會持久(UBIFS journal 未 flush)。寫入後必須執行 `sync` 才能確保資料寫入 flash。`/mnt/flash/vienna/` 和 `/mnt/flash/plus/` 不受此限制。 + +--- + +## 3. 軟體管線架構 + +### 執行緒模型 + +``` +主執行緒 (kp_firmware.c: main) +│ +├── loadConfig() 讀取 host_stream.ini +├── init_video_source() 初始化 VMF_VSRC (IFP + ISP) +├── 建立 FifoQ NPU 推論佇列 +│ +├── Thread A: kdp2_host_stream_image_thread +│ │ SSM_Reader → 取得 YUV 影格 +│ │ app_hdr_send_inf_cb → 放入 FifoQ +│ └► 餵圖給 NPU +│ +├── Thread B: kdp2_host_update_result_thread +│ └► NPU 結果回傳 → 解析 STDC 輸出 → g_atDrawInfo[] +│ +├── Thread C: kdp2_host_stream_draw_box (DrawBoxEnable=1 時) +│ │ SSM_Reader → DMA 複製 YUV → 在 YUV 上畫框 +│ └► SSM_Writer → 送給 H.264 Encoder +│ +├── Thread D: kdp2_host_video_thread +│ └► H.264 Encoder → SRB → rtsps (RTSP server) +│ +└── Thread E: kdp2_host_voc_thread (VOC=1 時) + └► SSM_Reader → VOC (HDMI 輸出) +``` + +### SSM (Sync Shared Memory) 緩衝區格式 + +ISP 輸出的每個影格存放在 SSM 緩衝區中,格式為 **YM12 planar YUV420**: + +``` +SSM 緩衝區佈局 (1920×1080 為例) +┌─────────────────────────────────┐ ← buffer 起始 +│ VMF_FRAME_INFO_T header │ 偏移 0,大小 = VMF_MAX_SSM_HEADER_SIZE (256 bytes) +│ 包含:dwOffset[0/1/2] │ +│ dwYStride, dwYSize │ +│ dwUVSize, dwType... │ +├─────────────────────────────────┤ ← dwOffset[0] (通常 = 256,16-byte aligned) +│ Y 平面 │ dwYSize = dwYStride × H = 1920 × 1080 +│ 1920 × 1080 = 2,073,600 bytes │ +│ stride = 1920 (無 padding) │ +├─────────────────────────────────┤ ← dwOffset[1] (≠ 256+YSize!需 stride 對齊) +│ Cb 平面 │ dwUVSize = dwYStride/2 × H/2 = 960 × 540 +│ 960 × 540 = 518,400 bytes │ +├─────────────────────────────────┤ ← dwOffset[2] +│ Cr 平面 │ 同 Cb 大小 +│ 960 × 540 = 518,400 bytes │ +└─────────────────────────────────┘ + +重要:dwOffset[1] 由 ISP 計算含對齊 padding, + 不等於 VMF_MAX_SSM_HEADER_SIZE + dwYSize。 + 所有 DMA 操作必須使用 dwOffset[0/1/2],不可自行計算。 +``` + +--- + +## 4. IMX662 DOL-HDR 雙曝光 ISP + +### 4.1 為什麼 IMX662 需要特殊設定 + +IMX662 是 Sony 的 StarVis 2 感測器,支援 **DOL-HDR (Digital Overlap High Dynamic Range)** 模式。在此模式下,感測器在同一條 MIPI 資料流中交錯輸出兩次曝光的 Bayer 資料: + +``` +MIPI 資料流內容 (DOL-HDR) +┌──────────────────────────────────────────┐ +│ 列 0 (短曝光 Bayer) ← 亮部細節 │ +│ 列 1 (長曝光 Bayer) ← 暗部細節 │ +│ 列 2 (短曝光 Bayer) │ +│ 列 3 (長曝光 Bayer) │ +│ ... │ +└──────────────────────────────────────────┘ + +IFP 接收後格式標識:VMF_FRAME_FORMAT_FUSION_BAY = 31 +``` + +這個格式讓 ISP 能夠合成高動態範圍影像——長曝光捕捉暗部,短曝光捕捉亮部,合成後得到超過單次曝光能表現的動態範圍。 + +### 4.2 雙曝光模式下 AWB 的問題 + +AWB (Auto White Balance) 需要分析影像的色彩統計數據才能決定色溫增益。對於 DOL-HDR 感測器,ISP 的 AES (Auto Exposure Statistics) 模組必須同時處理兩個曝光通道的統計數據。 + +**錯誤模式(使用 NORMAL 模式):** + +``` +程式碼:vsrc_initopt.eAppMode = VMF_VSRC_APP_MODE_NORMAL + +→ IFP 嘗試用單通道統計模式處理雙通道 Bayer 資料 +→ VSRC_AES_GetViBuffer() 持續失敗,錯誤碼 0x801C000B +→ AWB 無法取得有效統計數據 +→ AWB 卡在冷啟動初始增益 (Cr 增益偏高) +→ 輸出影像持續偏粉紅色 +``` + +**正確模式(使用 FUSION 模式):** + +``` +程式碼:vsrc_initopt.eAppMode = VMF_VSRC_APP_MODE_FUSION + +→ IFP 使用雙曝光統計模式 (dwStatisticsSrcType = 2) +→ AES 成功取得兩個通道的統計數據 +→ AWB 正常收斂,色溫 Cr/Cb 趨近 128 (中性) +→ 輸出色彩正常 +``` + +### 4.3 啟用 Fusion 模式的完整條件 + +Fusion 模式由三個層面共同決定,**三個必須同時正確**: + +#### 層面一:INI 設定 (`host_stream.ini`) + +```ini +[sensor] +sensor_cfg = "./Resource/VIC/0/imx662_1920x1080_ch0.cfg" ; 主通道(長曝光) +fusion_cfg = "./Resource/VIC/1/imx662_1920x1080_ch1.cfg" ; Fusion 通道(短曝光) +``` + +`fusion_cfg` 這一行**不可被注解或刪除**。程式碼邏輯: + +```c +// kp_firmware.c → loadConfig() +tmp = iniparser_getstring(ini, "sensor:fusion_cfg", NULL); +if (tmp) + pHostStreamInit->pszFusionConfigPath = strdup(tmp); + +// kdp2_host_stream.c → init_video_source() +if (pHostStreamInit->pszFusionConfigPath) { + tFrontConfig.dwSensorConfigCount = 2; + tFrontConfig.apszSensorConfig[0] = pHostStreamInit->pszSensorConfigPath; + tFrontConfig.apszSensorConfig[1] = pHostStreamInit->pszFusionConfigPath; + vsrc_initopt.eAppMode = VMF_VSRC_APP_MODE_FUSION; // ← 關鍵 +} +``` + +#### 層面二:ISP AWB 統計設定 (裝置端 flash) + +``` +檔案:$BIN_DIR/Resource/AWB/AutoWhiteBalance.ini + +必須設定:dwStatisticsSrcType = 2 + (0 = 單曝光,2 = 雙曝光 DOL-HDR) +``` + +此設定告訴 AWB 演算法從哪個統計來源取色彩數據。設為 0 時,即使 eAppMode=FUSION,AWB 仍然只看單通道統計,無法正確收斂。 + +#### 層面三:ISP 色調映射設定 (裝置端 flash) + +``` +檔案:$BIN_DIR/Resource/ISP/0/pqtable_ispe_Config.cfg + $BIN_DIR/Resource/ISP/1/pqtable_ispe_Config.cfg + +必須設定:bGTREnable = 1 + (GTR = Global Tone Remapping,全域色調重映射) +``` + +GTR 是 HDR 到 SDR 的色調壓縮演算法。DOL-HDR 合成的高動態範圍影像若不經過 GTR 壓縮,在標準顯示器上亮部會過飽和,色彩失真。 + +### 4.4 三個層面的關係圖 + +``` + ┌──────────────────────────────┐ +INI: fusion_cfg ───►│ eAppMode = FUSION │ + │ dwSensorConfigCount = 2 │ + │ 提供長短曝光兩組 .cfg 給 IFP │ + └──────────────┬───────────────┘ + │ + ▼ + ┌──────────────────────────────┐ +Resource: AWB ─────►│ dwStatisticsSrcType = 2 │ + │ AES 模組使用雙通道統計 │ + │ AWB 收斂 → 色溫正確 │ + └──────────────┬───────────────┘ + │ + ▼ + ┌──────────────────────────────┐ +Resource: ISP ─────►│ bGTREnable = 1 │ + │ GTR 壓縮 HDR→SDR │ + │ 亮度/色彩還原正常 │ + └──────────────┬───────────────┘ + │ + ▼ + ✅ 色彩正確的 YM12 影像 +``` + +--- + +## 5. 程式碼結構詳解 + +### 5.1 原始碼目錄 + +``` +kl630_build/ +├── compile.sh ← 交叉編譯腳本 (ARM armv7-a) +├── ini/ +│ └── host_stream.ini ← 主設定檔(部署時下載至裝置) +├── src/ +│ ├── host_stream/ +│ │ ├── kp_firmware.c ← main(),loadConfig(),執行緒管理 +│ │ ├── kdp2_host_stream.c ← 核心管線:影像執行緒、畫框、VOC +│ │ ├── application_init.c ← NPU job dispatch (STDC_JOB_ID=200) +│ │ └── app_header_init.c ← FifoQ 封包封裝 +│ ├── app_flow/ +│ │ └── demo_customize_inf_single_model.c ← 推論回呼 +│ ├── pre_post/ +│ │ └── stdc_post_process.c ← STDC 後處理(分割圖解析) +│ └── stdc/ +│ ├── fec_api.c ← FEC 魚眼校正 API +│ ├── stat_shim.c ← stat64 shim(交叉編譯相容) +│ └── glibc_shim.c ← glibc ABI 相容層 +├── lib/ ← 裝置端 .so 函式庫(從裝置複製) +├── tools/device/ +│ ├── deploy.sh ← 一鍵部署腳本 +│ ├── demo_rtsp.sh ← 裝置端 RTSP 啟動腳本 +│ └── demo_hdmi.sh ← 裝置端 HDMI 啟動腳本 +└── SDK/ ← Kneron VMF SDK 原始碼(參考用) +``` + +### 5.2 kp_firmware.c — 入口點 + +``` +main() +├── loadConfig(HOST_STREAM_CONFIG_PATH) +│ ├── 讀取 [sensor] sensor_cfg, fusion_cfg, fec_mode ... +│ ├── 讀取 [nnm] ModelPath, ModelId, JobId, InferenceStream ... +│ ├── 讀取 [streamer] StreamCount ... +│ └── 讀取 [voc] → g_dwVocPixFmt = YM12 (hardcoded) +│ +├── VMF_NNM_Fifoq_Manager_Init() ← NPU 推論佇列初始化 +├── VMF_NNM_Inference_App_Init() ← 載入 .nef 模型 +│ +├── kdp2_host_stream_init(pHostStreamInit) +│ ├── init_video_source() +│ │ ├── loadFECConfig() ← 魚眼校正參數 +│ │ ├── tFrontConfig.apszSensorConfig[0/1] +│ │ ├── eAppMode = FUSION / NORMAL +│ │ └── VMF_VSRC_Init() + VMF_VSRC_Start() +│ │ +│ └── 啟動各執行緒 +│ +└── 等待 SIGINT/SIGTERM 清理退出 +``` + +### 5.3 kdp2_host_stream.c — 核心執行緒 + +#### 影像採集執行緒 (`kdp2_host_stream_image_thread`) + +```c +// 核心迴圈 +SSM_Reader_ReturnReceiveNewestBuff(ptSsmHandle, &ssm_buf, eImageBufMode); +VMF_VSRC_SSM_GetInfo(ssm_buf.buffer, &vsrc_ssm_info); +// 取得 YUV 影格,交給 NPU FifoQ +app_hdr_send_inf_cb(buf_addr, ..., ssm_buf.buffer, &vsrc_ssm_info); +``` + +#### 畫框執行緒 (`kdp2_host_stream_draw_box`) + +此執行緒在 DrawBoxEnable=1 時啟動,用 DMA 複製 YUV 影格後疊加邊界框: + +```c +// 正確的 DMA 目標位址(使用 SSM header 中的對齊偏移) +dma_addr.pbyDstYPhysAddr = buffer_phys + vsrc_ssm_reader_info.dwOffset[0]; +dma_addr.pbyDstCbPhysAddr = buffer_phys + vsrc_ssm_reader_info.dwOffset[1]; +dma_addr.pbyDstCrPhysAddr = buffer_phys + vsrc_ssm_reader_info.dwOffset[2]; +// ↑ 關鍵:不可用 VMF_MAX_SSM_HEADER_SIZE + dwYSize, +// 因為 dwOffset[1] 含有 stride 對齊的 padding,數值不同。 +``` + +### 5.4 application_init.c — NPU Job 分派 + +```c +switch (job_id) { + case STDC_JOB_ID: // = 200 + stdc_inference(job_id, ...); + break; + // 其他 job ID... +} +``` + +### 5.5 host_stream.ini — 關鍵設定說明 + +```ini +[sensor] +sensor_cfg = "./Resource/VIC/0/imx662_1920x1080_ch0.cfg" +fusion_cfg = "./Resource/VIC/1/imx662_1920x1080_ch1.cfg" ; ← 必須!啟用 DOL-HDR +fec_mode = 0 ; 0=直通(不做魚眼校正),其他值啟用 FEC + +[nnm] +ModelPath = "nef/SDTC_0520.nef" +ModelId = 32800 ; STDC segmentation model id +JobId = 212 ; STDC_JOB_ID,對應 application_init.c +InferenceStream = 1 ; 使用 stream1 (724×362) 做推論 + ; RTSP+HDMI 同時模式必須設為 0(否則 SIGSEGV) +Fps = 25 +DrawBoxEnable = 1 ; 必須為 1,才會啟動畫框執行緒與語義分割疊加 +DrawOnResize = 0 ; InferenceStream=0 時必須設為 1(在 resize stream 畫框) + +[streamer] +StreamCount = 2 ; 開啟 stream0 (1920×1080) 和 stream1 (724×362) + ; HDMI 單獨模式設為 1 + +[stream0] +Width = 1920 Height = 1080 FPS = 25 Bitrate = 2000000 + +[stream1] +Width = 724 Height = 362 FPS = 25 Bitrate = 2000000 + +[voc] +voc_enable = 1 ; 0=關閉 HDMI,1=啟用 +VocWidth = 1920 VocHeight = 1080 +PixFmt = NV12 ; 注意:此值被程式碼忽略,實際固定為 YM12 +``` + +### 5.6 三種輸出模式的 INI 差異 + +| 模式 | voc_enable | StreamCount | InferenceStream | DrawOnResize | +|------|-----------|-------------|----------------|--------------| +| RTSP only | 0 | 2 | 1 | 0 | +| HDMI only | 1 | 1 | 0 | 1 | +| RTSP + HDMI | 1 | 2 | **0** (必須!) | 1 | + +> `voc_enable=1 + StreamCount=2 + InferenceStream=1` 會造成 **SIGSEGV** crash,因此 RTSP+HDMI 模式必須將 InferenceStream 設為 0,並用 DrawOnResize=1 讓畫框作用在 resize stream 上。 + +--- + +## 6. 從零開始:新裝置完整部署流程 + +### 步驟 0:前置準備 — 準備開發環境 + +**在 Host PC 上(Windows + Docker):** + +```bash +# 1. 確認 Docker 安裝並有 ARM 交叉編譯環境 +docker run --rm arm-compiler:latest arm-linux-gnueabihf-gcc --version + +# 2. 確認 lib/ 目錄有裝置端函式庫(從裝置複製或 SDK 提供) +ls kl630_build/lib/ +# 應包含:libvmf.so, libmembroker.so, libmsgbroker.so, +# libsyncringbuffer.so, libiniparser.so, +# libvmf_nnm.so, libkutils.so, libapp_yolo.so, +# libuClibc-1.0.34.so 等 + +# 3. 確認 SDK headers 路徑 +ls kl630_build/SDK/sdk/vtcs_root_vienna/include/vmf/ +# 應包含:video_source.h, video_encoder.h, ssm_info.h 等 +``` + +### 步驟 1:編譯 Binary + +```bash +# 在 Host PC 上,於 Docker 容器內執行 +docker run --rm \ + -v "C:/Users/hipe5/Desktop/kl630_build:/workspace/kl630_build" \ + kl630-dev \ + bash /workspace/kl630_build/compile.sh + +# 編譯成功後輸出: +# === SUCCESS: /workspace/kl630_build/build/kp_firmware_host_stream === +# 並顯示 patchelf 修補後的 NEEDED 清單(應無 ld-linux-armhf.so.3) +``` + +**compile.sh 做了什麼:** + +1. 用 `arm-linux-gnueabihf-gcc` 編譯所有 `.c` → `.o` +2. Link 成 ELF binary,rpath 指向 `$ORIGIN/lib` +3. 用 `patchelf` 將 dynamic linker 從 glibc 改為 uClibc +4. 用 `patchelf` 將 `libc.so.6` → `libc.so.0`(uClibc soname) + +### 步驟 2:架設 HTTP 伺服器(Host PC) + +```bash +# 在 kl630_build/build/ 目錄下架設 HTTP server +cd kl630_build/build/ +cp ../ini/host_stream.ini . # 複製最新 INI +python -m http.server 8080 + +# 或 Windows 版 +python -m http.server 8080 +``` + +確認裝置能連到 Host PC: +- Host PC IP:`192.168.3.1`(透過 USB 網路介面) +- Port:`8080` +- 提供檔案:`kp_firmware_host_stream`、`host_stream.ini` + +### 步驟 3:連線裝置並執行一次性 ISP 設定 + +```bash +# SSH 連入裝置(USB 網路或序列埠) +ssh root@192.168.3.10 + +# 在裝置上,首次只需執行一次: +sh /tmp/deploy.sh --setup +``` + +或手動執行以下指令(效果相同): + +```sh +BIN_DIR=/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin + +# 修正 1:AWB 雙曝光統計模式 +sed -i 's/dwStatisticsSrcType = 0/dwStatisticsSrcType = 2/' \ + $BIN_DIR/Resource/AWB/AutoWhiteBalance.ini + +# 修正 2:啟用 GTR 色調映射(兩個 ISP 通道) +sed -i 's/bGTREnable = 0/bGTREnable = 1/' \ + $BIN_DIR/Resource/ISP/0/pqtable_ispe_Config.cfg +sed -i 's/bGTREnable = 0/bGTREnable = 1/' \ + $BIN_DIR/Resource/ISP/1/pqtable_ispe_Config.cfg + +# 確認 +grep dwStatisticsSrcType $BIN_DIR/Resource/AWB/AutoWhiteBalance.ini +grep bGTREnable $BIN_DIR/Resource/ISP/0/pqtable_ispe_Config.cfg +``` + +> **這個步驟只需要在新裝置上執行一次。** +> 所有變更寫入 `/mnt/flash/`(eMMC),重開機後永久保留。 + +### 步驟 4:部署新 Binary(每次重新編譯後執行) + +```bash +# 在裝置上 +wget -q http://192.168.3.1:8080/deploy.sh -O /tmp/deploy.sh +sh /tmp/deploy.sh +``` + +`deploy.sh` 自動執行: + +``` +1. killall -9 kp_firmware_host_stream +2. killall -9 rtsps +3. rm -f /dev/shm/* +4. wget kp_firmware_host_stream → /mnt/flash/vienna/kp_firmware_host_stream +5. wget host_stream.ini → $BIN_DIR/ini/host_stream.ini +6. cd $BIN_DIR && sh ./ini/demo_rtsp.sh +``` + +### 步驟 5:驗證執行 + +```bash +# 在裝置上確認程式執行 +ps | grep kp_firmware +ps | grep rtsps + +# 查看啟動 log +# 應看到: +# [NNM] fusion_cfg: ./Resource/VIC/1/imx662_1920x1080_ch1.cfg +# [init_video_source] Fusion Mode +# [VOC] PixFmt: YM12 (0x324D5930) + +# 用 VLC 或 ffplay 播放 RTSP +# rtsp://192.168.3.10/live1.sdp +``` + +### 步驟 6:確認色彩正常 + +| 現象 | 可能原因 | 解法 | +|------|---------|------| +| 粉紅色影像 | fusion_cfg 沒設定 | 檢查 host_stream.ini 第二行有無 `fusion_cfg` | +| 粉紅色影像 | dwStatisticsSrcType = 0 | 重新執行 `deploy.sh --setup` | +| 粉紅色影像 | bGTREnable = 0 | 重新執行 `deploy.sh --setup` | +| 色彩正常但有條紋 | DMA 畫框位址錯誤 | 已修正,重新編譯部署即可 | + +--- + +## 7. 常見問題排查 + +### 7.1 `VSRC_AES_GetViBuffer() failed: 801C000B` + +``` +問題:log 出現此錯誤且持續出現(每幀) +原因:eAppMode = NORMAL,AES 無法處理 FUSION_BAY (fmt=31) 格式 +解法:確認 host_stream.ini 有 fusion_cfg 這一行且未被注解 +``` + +### 7.2 Stack Smashing 崩潰 + +``` +問題:*** stack smashing detected *** : Aborted +原因:stat64/fstat64 符號在 uClibc 上與 glibc 不相容 +解法:已在 src/stdc/stat_shim.c 加入 shim,確認編譯時包含此檔案 +``` + +### 7.3 程式啟動後立即退出 + +``` +問題:VMF_VSRC_Init() 回傳 NULL +檢查清單: +1. LD_LIBRARY_PATH=/mnt/flash/vienna/lib 是否設定 +2. /dev/shm/ 是否清空(執行 rm -f /dev/shm/*) +3. 舊的 kp_firmware_host_stream 是否已 kill +4. Resource/ 目錄下的設定檔是否存在 +``` + +### 7.4 RTSP 連不上 + +``` +問題:VLC 打開 rtsp://192.168.3.10/live1.sdp 失敗 +檢查: +1. demo_rtsp.sh 中 sleep 4 後 rtsps 是否已啟動 + → ps | grep rtsps +2. stream_server_config.ini 是否存在於 $BIN_DIR +3. venc_srb_* 共享記憶體是否存在 + → ls /dev/shm/ +``` + +### 7.5 HDMI 無輸出 + +``` +問題:voc_enable = 1 但 HDMI 無畫面 +檢查: +1. VocWidth/VocHeight 是否正確設定 +2. ISP 輸出格式:固定為 YM12,程式碼已 hardcode +3. HDMI 顯示器是否支援 1920×1080 +``` + +### 7.6 `[MemMgr][Error]: Open EDMC device fail !!` + +``` +問題:firmware 啟動後立即出現此錯誤並退出 +原因:硬體 kernel module 尚未載入,/dev/vpl_edmc 不存在 + (EDMC = Enhanced DMA Controller,由 vpl_edmc_v6.3.0.2.ko 提供) + +排查: + strace 確認: + open("/dev/vpl_edmc", O_RDWR) = -1 ENOENT (No such file or directory) + +解法: + cd /mnt/flash/vienna/drivers && sh driver.sh 2>/dev/null + 然後重新執行 firmware + +注意: +- /lib/modules/ 中有兩個版本:Godshand.ko 和 Godshand_v2.1.0.1.ko + 系統預設載入的是 Godshand.ko,兩者皆不建立 /dev/vpl_edmc + vpl_edmc 必須由 driver.sh 明確載入 vpl_edmc_v6.3.0.2.ko 才會建立 +- driver.sh 使用相對路徑 insmod,必須從 drivers/ 目錄執行 +- demo_rtsp.sh / demo_hdmi.sh / demo_rtsp_hdmi.sh 已內建自動檢查: + [ -e /dev/vpl_edmc ] || (cd /mnt/flash/vienna/drivers && sh driver.sh 2>/dev/null && sleep 1) +``` + +### 7.7 Write Autostart 後重開機無效 + +``` +問題:透過 web UI Write Autostart 後,重開機 firmware 沒有自動啟動 +原因:/mnt/flash/etc/ 的寫入在斷電重開後會消失 + UBIFS journal 未 flush 到 flash + +解法: + web_serve.py 的 api_autostart_write 在寫完 rc.local 後自動執行 sync + 手動寫入時必須在斷電前執行:sync + +開機自動啟動原理: + /etc/init.d/rcS → S95done → insmod /lib/modules/Godshand.ko + → sh /mnt/flash/etc/rc.local(若存在) + rc.local 內容: + cd /mnt/flash/vienna/drivers && sh driver.sh 2>/dev/null ← 載入硬體驅動 + sleep 2 + cd /mnt/flash/vienna + sleep 3 + nohup sh /mnt/flash/vienna/demo_*.sh > /tmp/fw.log 2>&1 & +``` + +--- + +## 8. 關鍵參數速查表 + +### 每次部署需確認的 INI 設定 + +| 參數 | 位置 | 必要值 | 說明 | +|------|------|--------|------| +| `fusion_cfg` | `[sensor]` | `./Resource/VIC/1/...` | DOL-HDR 必須,不可注解 | +| `ModelPath` | `[nnm]` | `nef/SDTC_0520.nef` | STDC 模型路徑 | +| `ModelId` | `[nnm]` | `32800` | STDC segmentation model id | +| `JobId` | `[nnm]` | `212` | 對應 `STDC_JOB_ID` | +| `InferenceStream` | `[nnm]` | `1`(RTSP)/ `0`(HDMI/RTSP+HDMI) | 推論用 stream 索引 | +| `DrawBoxEnable` | `[nnm]` | `1` | 必須,否則語義分割疊加不會執行 | +| `DrawOnResize` | `[nnm]` | `1`(InferenceStream=0 時) | 在 resize stream 上畫框 | +| `StreamCount` | `[streamer]` | `2`(RTSP)/ `1`(HDMI only) | stream 數量 | + +### 裝置端 flash 一次性設定 + +| 參數 | 檔案 | 必要值 | 說明 | +|------|------|--------|------| +| `dwStatisticsSrcType` | `AWB/AutoWhiteBalance.ini` | `2` | 雙曝光統計模式 | +| `bGTREnable` | `ISP/0/pqtable_ispe_Config.cfg` | `1` | 色調映射開啟 | +| `bGTREnable` | `ISP/1/pqtable_ispe_Config.cfg` | `1` | 色調映射開啟 | + +### 編譯關鍵 Flags + +| Flag | 說明 | +|------|------| +| `-DVATICS_PLATFORM` | VMF SDK 平台識別 | +| `-DKL630` | KL630 特定程式碼路徑 | +| `-march=armv7-a -mfpu=neon -mfloat-abi=hard` | Cortex-A7 + NEON | +| `-Wl,--dynamic-linker,/lib/ld-uClibc.so.1` | 指定 uClibc dynamic linker | +| patchelf: `libc.so.6` → `libc.so.0` | glibc soname → uClibc soname | + +--- + +## 9. STDC 語義分割邏輯詳解 + +本節說明目前 `stdc_post_process.c` 和 `app_header_init.c` 實作的完整判斷邏輯,作為下一階段警示/煞車控制的輸入基礎。 + +### 9.1 分割類別定義 + +STDC 模型輸出 128×64 的像素分類圖,每個像素屬於以下 8 個類別之一: + +| 索引 | 類別 | 說明 | +|------|------|------| +| 0 | `BUNKER` | 沙坑 | +| 1 | `CAR` | 車輛 | +| 2 | `GRASS` | 草地 | +| 3 | `GREENERY` | 植被(灌木、雜草等) | +| 4 | `PERSON` | 行人 | +| 5 | `POND` | 水塘 | +| 6 | `ROAD` | 道路/路徑 | +| 7 | `TREE` | 樹木 | + +每個類別都計算全域佔比 `class_ratio[ci]`(該類像素數 ÷ 總像素數)。 + +### 9.2 感興趣區域(ROI)定義 + +#### 碰撞 ROI(Collision ROI) + +以影像分割圖比例定義,判斷是否有危險物體進入正前方區域: + +``` +左邊界: 25% 寬度 右邊界: 75% 寬度 +上邊界: 25% 高度 下邊界: 70% 高度 + +對應 1920×1080 畫面:x=480 y=270 w=960 h=486(DrawBox 視覺輸出位置) +``` + +#### 前進 ROI(Forward ROI)— 梯形 + +模擬球車前方路面的透視梯形,用於草地/樹木判斷: + +``` +頂邊:左 45% ─────────── 右 55% (影像 55% 高度處) +底邊:左 30% ─────────────────── 右 70% (影像 95% 高度處) +``` + +### 9.3 警告旗標與閥值 + +`stdc_analysis_t` 中的 boolean 旗標由以下邏輯設定: + +| 旗標 | 條件 | 閥值來源 | +|------|------|---------| +| `on_grass` | 前進 ROI 內草地佔比 > 30% | `THR_GRASS_ROI = 0.30` | +| `grass_warning` | `on_grass` 持續時間 > 2 秒 | `STDC_WARNING_SECONDS = 2.0` | +| `person_warning` | 全域行人佔比 > 1% | `THR_PERSON_GLOBAL = 0.01` | +| `car_warning` | 全域車輛佔比 > 5% | `THR_CAR_GLOBAL = 0.05` | +| `greenery_warning` | 全域植被佔比 > 20% | `THR_GREENERY_GLOBAL = 0.20` | +| `bunker_warning` | 全域沙坑佔比 > 1% | `THR_BUNKER_GLOBAL = 0.01` | +| `pond_warning` | 全域水塘佔比 > 1% | `THR_POND_GLOBAL = 0.01` | +| `tree_dense` | 全域樹木佔比 > 30% | `THR_TREE_DENSE = 0.30` | +| `tree_approaching` | 前進 ROI 內樹木佔比相較前幀成長 > 5% | `STDC_TREE_GROWTH_THR = 0.05` | +| `collision_risk` | 碰撞 ROI 內任一危險類別超過各自閥值(見下表) | — | +| `is_moving` | 前進 ROI 內 luma 平均差 > 3.0 | `STDC_MOTION_THRESHOLD = 3.0` | + +#### `collision_risk` 詳細條件(碰撞 ROI 內) + +| 類別 | 閥值 | +|------|------| +| 行人 (`col_person_ratio`) | > 1% | +| 車輛 (`col_car_ratio`) | > 5% | +| 樹木 (`col_tree_ratio`) | > 20% | +| 沙坑 (`col_bunker_ratio`) | > 1% | +| 水塘 (`col_pond_ratio`) | > 1% | + +任一條件成立即觸發 `collision_risk = 1`。 + +### 9.4 警告優先層級 + +目前的邏輯是平坦的 bitmask(無優先順序),下一階段需要整合成層級: + +``` +Level 2(緊急) ← collision_risk = 1 + OR person_warning = 1 + +Level 1(警告) ← car_warning = 1 + OR pond_warning = 1 + OR bunker_warning = 1 + OR tree_approaching = 1 + +Level 0(正常) ← 其餘情況 +``` + +`grass_warning`、`greenery_warning`、`tree_dense` 在球場環境中幾乎常時觸發,**不納入煞車邏輯**,僅保留為 HUD 顯示資訊。 + +### 9.5 視覺疊加輸出(DrawBox,1920×1080) + +``` +畫面區域 內容 +───────────────────────────────────────────────────── +中央大框 碰撞 ROI 輪廓(480,270 960×486) + collision_risk 時加畫第二層內框(492,282 936×462) +中央下框 前進 ROI 輪廓(576,594 768×432) +左側警告欄 (200×55) y=10 collision_risk + y=73 person_warning + y=136 car_warning + y=199 grass_warning + y=262 tree_dense + y=325 tree_approaching +右側類別欄 (40×55) 每個 class 當比例超過各自閥值時亮起 + bunker/car/grass/greenery/person/pond/road/tree(由上到下) +``` + +--- + +## 10. 下一階段:高爾夫球車警示與煞車控制 + +### 10.1 目標 + +在現有 KL630 AI 視覺管線上疊加一套安全警示與動力介入系統,適用於高爾夫球場電動球車: + +- **行人/障礙物偵測** → 即時警告 +- **緊急煞車** → GPIO 訊號觸發 relay,截斷馬達動力或拉起電磁煞車 +- **未來擴充** → 油門 PWM 控制(依障礙物距離梯度減速) + +### 10.2 系統架構變化 + +``` +現有管線(第一階段) + STDC 分割 → stdc_analysis_t flags → printf 警告 + DrawBox 視覺疊加 + +新增(第二階段) + stdc_analysis_t flags + │ + ▼ + warn_level 整合(0/1/2) ← app_header_init.c 新增 + │ + ┌────┴─────────┐ + ▼ ▼ +GPIO 煞車訊號 GPIO 警告燈 ← gpio_ctrl.c 新增 +/sys/class/gpio/ relay/buzzer + │ + ▼ + 馬達控制器 / 電磁煞車 +``` + +### 10.3 需新增/修改的檔案 + +| 檔案 | 動作 | 說明 | +|------|------|------| +| `src/host_stream/gpio_ctrl.c` | **新增** | GPIO 初始化、`gpio_set_warning(level)` | +| `src/host_stream/gpio_ctrl.h` | **新增** | 函式宣告、pin 定義 | +| `src/host_stream/app_header_init.c` | **修改** | STDC 結果區塊加 warn_level 計算 + 呼叫 `gpio_set_warning()` | +| `src/host_stream/kp_firmware.c` | **修改** | `main()` 加入 `gpio_ctrl_init()` | +| `ini/host_stream.ini` | **修改** | 新增 `[golf_cart]` 區段,放置 GPIO pin 號與閥值 | +| `src/host_stream/kp_firmware.c` | **修改** | `loadConfig()` 讀取 `[golf_cart]` 參數 | + +### 10.4 警告等級 → GPIO 輸出對應 + +``` +warn_level 0(正常) → GPIO_WARN=0 GPIO_BRAKE=0 +warn_level 1(警告) → GPIO_WARN=1 GPIO_BRAKE=0 (警示燈亮) +warn_level 2(緊急) → GPIO_WARN=1 GPIO_BRAKE=1 (警示燈 + 煞車 relay 觸發) +``` + +觸發條件: +- Level 2:`collision_risk == 1` 或 `person_warning == 1` +- Level 1:`car_warning == 1` 或 `pond_warning == 1` 或 `bunker_warning == 1` 或 `tree_approaching == 1` + +### 10.5 新增 INI 參數規劃 + +```ini +[golf_cart] +BrakeOnCollision = 1 ; collision_risk 觸發煞車(0=停用) +BrakeOnPerson = 1 ; person_warning 觸發煞車(0=停用) +WarnOnCar = 1 ; car_warning 觸發警告燈 +WarnOnPond = 1 ; pond_warning 觸發警告燈 +WarnOnBunker = 1 ; bunker_warning 觸發警告燈 +GpioPinWarn = 5 ; 警告燈/蜂鳴器 GPIO pin 編號 +GpioPinBrake = 6 ; 煞車 relay GPIO pin 編號 +BrakePulseMs = 0 ; 0=持續拉高;>0=觸發後維持 N ms 再釋放(pulse 模式) +``` + +### 10.6 待確認事項 + +| 項目 | 說明 | +|------|------| +| KL630 GPIO pin mapping | 確認板上哪些 pin 拉出,對應 `/sys/class/gpio/gpioX` 編號(需查 BSP / schematic) | +| 煞車介面 | relay DO(直接 GPIO)或需要 PWM 訊號給馬達控制器 | +| 草地閥值調整 | 球場全是草,`THR_GRASS_ROI=0.30` 會讓 `on_grass` 幾乎常時觸發;考慮提高閥值或直接排除 grass 警告出煞車邏輯 | +| 反應延遲 | 目前 STDC 推論速率約 25 fps,collision_risk 觸發到 GPIO 拉高延遲 < 80ms,是否符合需求 | +| 安全冗餘 | 視覺 AI 單點失效時的 failsafe 策略(例如:推論超時 → 強制觸發警告) | + +--- + +*報告產生日期:2026-03-24* +*適用版本:VMF Vienna SDK 2.5.6,STDC_0520.nef,Firmware Ver. 1.2.0.1203*