35 KiB
KL630 Host Stream 技術報告
目錄
- 系統概覽
- 硬體架構
- 軟體管線架構
- IMX662 DOL-HDR 雙曝光 ISP
- 程式碼結構詳解
- 從零開始:新裝置完整部署流程
- 常見問題排查
- 關鍵參數速查表
- STDC 語義分割邏輯詳解
- 下一階段:高爾夫球車警示與煞車控制
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)
[sensor]
sensor_cfg = "./Resource/VIC/0/imx662_1920x1080_ch0.cfg" ; 主通道(長曝光)
fusion_cfg = "./Resource/VIC/1/imx662_1920x1080_ch1.cfg" ; Fusion 通道(短曝光)
fusion_cfg 這一行不可被注解或刪除。程式碼邏輯:
// 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)
// 核心迴圈
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 影格後疊加邊界框:
// 正確的 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 分派
switch (job_id) {
case STDC_JOB_ID: // = 200
stdc_inference(job_id, ...);
break;
// 其他 job ID...
}
5.5 host_stream.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):
# 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
# 在 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 做了什麼:
- 用
arm-linux-gnueabihf-gcc編譯所有.c→.o - Link 成 ELF binary,rpath 指向
$ORIGIN/lib - 用
patchelf將 dynamic linker 從 glibc 改為 uClibc - 用
patchelf將libc.so.6→libc.so.0(uClibc soname)
步驟 2:架設 HTTP 伺服器(Host PC)
# 在 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 設定
# SSH 連入裝置(USB 網路或序列埠)
ssh root@192.168.3.10
# 在裝置上,首次只需執行一次:
sh /tmp/deploy.sh --setup
或手動執行以下指令(效果相同):
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(每次重新編譯後執行)
# 在裝置上
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:驗證執行
# 在裝置上確認程式執行
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 參數規劃
[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