gf_ai_box/docs/technical_report.md

924 lines
35 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.34LD_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] (通常 = 25616-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=FUSIONAWB 仍然只看單通道統計,無法正確收斂。
#### 層面三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=關閉 HDMI1=啟用
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 binaryrpath 指向 `$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
# 修正 1AWB 雙曝光統計模式
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 = NORMALAES 無法處理 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定義
#### 碰撞 ROICollision ROI
以影像分割圖比例定義,判斷是否有危險物體進入正前方區域:
```
左邊界: 25% 寬度 右邊界: 75% 寬度
上邊界: 25% 高度 下邊界: 70% 高度
對應 1920×1080 畫面x=480 y=270 w=960 h=486DrawBox 視覺輸出位置)
```
#### 前進 ROIForward 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 視覺疊加輸出DrawBox1920×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 fpscollision_risk 觸發到 GPIO 拉高延遲 < 80ms是否符合需求 |
| 安全冗餘 | 視覺 AI 單點失效時的 failsafe 策略例如推論超時 強制觸發警告 |
---
*報告產生日期2026-03-24*
*適用版本VMF Vienna SDK 2.5.6STDC_0520.nefFirmware Ver. 1.2.0.1203*