Add TEST_ENTER/TEST_EXIT commands via BLE to enter/exit test mode. In test mode, inference results are suppressed; testers can directly trigger BLE JSON (TEST_BLE *), CAN+Buzzer actions (TEST_CAN *), or run sequenced full-coverage tests (TEST_BLE_ALL, TEST_CAN_ALL, TEST_ALL). Auto-exits after 60s idle. Routes unrecognised BLE commands through new bt_uart_set_extra_cmd_cb() hook.
KL630 Host Stream Firmware
即時影像語義分割系統,運行於 Kneron KL630 (Cortex-A7, uClibc)。 透過 IMX662 DOL-HDR 雙曝光攝影機進行 STDC 語義分割,並將結果以 RTSP 串流或 HDMI 輸出。
技術細節 → docs/technical_report.md
目錄結構
kl630_build/
├── web_serve.py # Web 控制台(主要使用)
├── build_and_serve.py # CLI 版本(不需要瀏覽器)
├── compile.sh # Docker 內 ARM 交叉編譯腳本
├── Dockerfile # kl630-dev image 定義
├── requirements.txt # Python 套件清單
│
├── src/
│ ├── host_stream/ # 主程式(初始化、推論迴圈、結果處理)
│ ├── app_flow/ # VMF pipeline 控制
│ ├── pre_post/ # YOLOv5 前後處理
│ └── stdc/ # STDC 語義分割後處理
│
├── include/
│ ├── stdc/ # stdc_post_process.h(分析結果結構)
│ └── fake/ # SDK 缺少時的 stub headers
│
├── ini/
│ └── host_stream.ini # 執行期設定(model、stream、ISP、FEC 參數)
│
│
├── lib/ # 裝置端 .so 函式庫(VMF SDK)
├── docs/
│ └── technical_report.md # 完整技術文件
└── build/ # 編譯輸出(由 compile.sh 產生)
快速開始
前置條件
- Docker Desktop 已安裝並執行中
- Python 3.8+
安裝
pip install -r requirements.txt
| 套件 | 用途 |
|---|---|
flask |
Web 控制台 + HTTP file server |
opencv-python |
RTSP 串流預覽(內建 FFmpeg,無需另外安裝) |
啟動
python web_serve.py
瀏覽器開啟 http://localhost:8080/
新機器首次設定 SOP
全新 KL630 裝置依序執行以下步驟:
Step 1 — Compile
在 Web 控制台按 Compile。
- 自動建立 Docker image(若不存在)
- 交叉編譯 ARM binary
- 複製 binary / INI / 腳本到
build/
若 binary 已是最新版本,可跳過此步驟直接從 Step 2 開始。
Step 2 — 設定 Output Mode
在 INI Settings 面板選擇輸出模式(預設 RTSP):
| 組合 | RTSP | HDMI | 啟動腳本 |
|---|---|---|---|
| RTSP 串流 | ON | OFF | demo_rtsp.sh |
| HDMI 顯示 | OFF | ON | demo_hdmi.sh |
| RTSP + HDMI 同時 | ON | ON | demo_rtsp_hdmi.sh(INI voc_enable=1) |
| 純推論(無輸出) | OFF | OFF | 直接啟動 binary |
Step 3 — Deploy to KL630
按 Deploy to KL630,自動透過 Telnet 完成:
- 停止舊 firmware
- 下載 binary、INI、NEF 模型
- 下載
demo_rtsp.sh、demo_hdmi.sh、demo_rtsp_hdmi.sh - 設定 VOC 輸出模式
- 啟動 firmware
Step 4 — First-time ISP Setup
按 First-time ISP Setup。
寫入 DOL-HDR 所需的 flash 參數(dwStatisticsSrcType=2、bGTREnable=1),
只需執行一次,設定永久保留在 flash,重新 Deploy 不會被覆蓋。
跳過此步驟影像會過暗或曝光不正確。
Step 5 — 魚眼鏡頭校正(FEC)
在 INI Settings 面板設定 Fish-Eye Correction:
| 參數 | 推薦設定 | 說明 |
|---|---|---|
| FEC | ON | 開啟魚眼校正 |
| Mode | 4 — 180° Two Direction | 天花板魚眼鏡頭推薦模式,同時展開水平與垂直方向 |
| Install Type | 0 — Ceiling | 天花板安裝 |
| EIS | 視需求 | 電子防手震 |
按 Apply to Device + Restart 套用並重啟 firmware。
Web 控制台功能說明
Network Config
設定 Host IP(這台 PC)、KL630 IP、HTTP Port、Docker Image。
按 Save 後自動更新 deploy.sh 的 HOST_URL。
HTTP Server Files
列出 build/ 目錄下所有可下載的檔案,以及裝置端手動部署指令:
wget http://<host_ip>:8080/deploy.sh -O /tmp/deploy.sh && sh /tmp/deploy.sh
RTSP Stream Preview
直接在網頁預覽 KL630 串流,不需要開 VLC。 按 ▶ Start Stream 即可,連線失敗時 6 秒內回報錯誤。
Actions
| 按鈕 | 說明 |
|---|---|
| Compile | Docker 交叉編譯 ARM binary,複製到 build/ |
| Deploy to KL630 | Telnet 部署 binary / INI / NEF / 腳本,依 Output Mode 啟動 firmware |
| First-time ISP Setup | 新機器一次性 ISP flash 設定(DOL-HDR 參數) |
| Write Autostart | 寫入開機自動啟動腳本 /etc/init.d/S99firmware |
INI Settings
Fish-Eye Correction (FEC)
| 參數 | 說明 |
|---|---|
| FEC ON/OFF | 開啟或關閉魚眼校正(fec_mode = 0 為關閉) |
| Mode 1 | Single Region |
| Mode 2 | 180° All Direction |
| Mode 3 | 180° One Direction |
| Mode 4 | 180° Two Direction(推薦) |
| Mode 5 | PT Mode |
| Install Type | Ceiling / Table / Wall(initial_fec_app_type) |
| EIS | 電子防手震(eis_enable) |
| DrawBox | H.264 burn-in 偵測框(DrawBoxEnable) |
Output Mode
| 選項 | 說明 |
|---|---|
| RTSP | H.264 RTSP 串流輸出,由 demo_rtsp.sh 管理 |
| HDMI | VOC HDMI 顯示輸出,由 demo_hdmi.sh 管理 |
- Save INI(disk only) — 更新本地
ini/host_stream.ini,下次 Deploy 時推送至裝置 - Apply to Device + Restart — 立即 Telnet 更新裝置 INI 並重啟 firmware
Model Settings
切換推論模型(NEF 檔)、ModelId、JobId,支援上傳新 NEF。 Apply to Device + Restart 會下載 NEF 到裝置並依目前 Output Mode 重啟。
Terminal
Output Log 下方有命令輸入列,可直接在網頁對 KL630 下指令:
cat /tmp/fw.log # 查看 firmware 啟動 log
cat /tmp/rtsp_demo.log # 查看 RTSP demo log
ps | grep firmware # 確認 firmware 是否在執行
killall kp_firmware_host_stream
Web Server 命令行操作指南
若不喜歡 GUI,可透過指令和 API 完全控制 web_serve.py,不需要開瀏覽器。
啟動 Web Server
# 預設 port 8080
python web_serve.py
# 指定端口
python web_serve.py --port 8080
python web_serve.py --port 9090
Server 啟動後可用 curl 調用各項功能。
Web Server API 參考
1. 查詢/保存設定
# 查看現在設定(Host IP、KL630 IP、HTTP Port、Docker Image)
curl http://localhost:8080/api/config
# 修改設定
curl -X POST http://localhost:8080/api/config \
-H "Content-Type: application/json" \
-d '{
"host_ip": "192.168.3.1",
"kl630_ip": "192.168.3.10",
"port": 8080,
"docker_image": "kl630-dev"
}'
2. 查看 build/ 目錄下載文件列表
curl http://localhost:8080/api/files
輸出示例:
[
{"name": "kp_firmware_host_stream", "size": 5242880},
{"name": "host_stream.ini", "size": 2048},
{"name": "demo_rtsp.sh", "size": 512},
{"name": "deploy.sh", "size": 1024}
]
3. 編譯(Server-Sent Events 串流輸出)
# 啟動編譯,即時輸出編譯 log
curl http://localhost:8080/api/compile/run
# 搭配 jq 只看 error
curl http://localhost:8080/api/compile/run | grep -o '"kind":"error"' | wc -l
編譯 log 格式(SSE):
{"kind": "log", "text": "Starting cross-compile..."}
{"kind": "ok", "text": "Compile SUCCESS"}
{"kind": "error", "text": "Compile FAILED (exit 1)"}
4. 部署到 KL630(Telnet)
# 執行完整部署流程(停止舊 firmware、下載 binary、啟動新 firmware)
curl http://localhost:8080/api/deploy/run
# 查看部署 log(最後 100 行)可透過網頁 Terminal 或直接用 tail
部署流程等同於 Web UI 按下 Deploy to KL630。
5. ISP / Flash 一次性設定
# 一次性 ISP setup(DOL-HDR 參數)
curl http://localhost:8080/api/spi_setup/run
# 或藍牙相關設定
curl http://localhost:8080/api/bt_setup/run
6. INI 設定讀取/寫入
# 讀取目前 INI 設定
curl http://localhost:8080/api/ini
# 修改 INI 並立即套用到裝置(等同 Web UI "Apply to Device + Restart")
curl -X POST http://localhost:8080/api/ini/apply \
-H "Content-Type: application/json" \
-d '{
"fec_mode": 4,
"DrawBoxEnable": 1,
"voc_enable": 0
}'
常見 INI 參數:
fec_mode: 0=關閉, 1-5=魚眼校正模式DrawBoxEnable: 0/1 — H.264 burn-in 偵測框voc_enable: 0/1 — HDMI VOC 輸出ModelPath,ModelId,JobId: 推論模型設定
7. RTSP 串流預覽 / 停止
# 啟動 RTSP 預覽(返回 MJPEG 影像串流)
curl http://localhost:8080/api/stream/video > stream.mjpeg
# 停止 RTSP 預覽
curl -X POST http://localhost:8080/api/stream/stop
8. STDC 推論統計
# 查看即時推論統計(幀率、語義分割百分比)
curl http://localhost:8080/api/stdc/stats
# 啟動推論
curl -X POST http://localhost:8080/api/stdc/start
# 停止推論
curl -X POST http://localhost:8080/api/stdc/stop
輸出示例:
{
"frame": 42,
"mov": 1,
"diff": 4.2,
"classes": {
"bunker": 0.0,
"car": 8.3,
"grass": 0.0,
"greenery": 12.1,
"person": 0.0,
"pond": 0.0,
"road": 71.4,
"tree": 8.2
}
}
9. 遠端執行指令(Telnet 到裝置)
# 在裝置上執行指令並取得輸出
curl -X POST http://localhost:8080/api/terminal/exec \
-H "Content-Type: application/json" \
-d '{"cmd": "ps | grep firmware"}'
# 查看 firmware log
curl -X POST http://localhost:8080/api/terminal/exec \
-H "Content-Type: application/json" \
-d '{"cmd": "cat /tmp/fw.log"}'
# 查看可用模型
curl -X POST http://localhost:8080/api/terminal/exec \
-H "Content-Type: application/json" \
-d '{"cmd": "ls -lh /mnt/flash/vienna/nef/"}'
10. 開機自動啟動設定
# 查看自動啟動腳本設定
curl http://localhost:8080/api/autostart/read
# 寫入自動啟動腳本(`/etc/init.d/S99firmware`)
curl -X POST http://localhost:8080/api/autostart/write \
-H "Content-Type: application/json" \
-d '{"autostart_mode": "rtsp"}' # or "hdmi", "rtsp_hdmi"
11. 模型管理
# 查看可用模型列表
curl http://localhost:8080/api/model/list
# 切換模型(需要指定 ModelId / JobId)
curl -X POST http://localhost:8080/api/model \
-H "Content-Type: application/json" \
-d '{
"model_id": "100001",
"job_id": 1
}'
完整工作流示例(指令行完全不用 GUI)
# 1. 設定網路(Host IP 和 KL630 IP)
curl -X POST http://localhost:8080/api/config \
-H "Content-Type: application/json" \
-d '{"host_ip": "192.168.3.1", "kl630_ip": "192.168.3.10"}'
# 2. 編譯
echo "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Compiling..."
curl http://localhost:8080/api/compile/run | grep '"kind"' | tail -1
# 3. 檢查生成的文件
echo "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Build files:"
curl http://localhost:8080/api/files | grep name
# 4. 部署到裝置
echo "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Deploying to KL630..."
curl http://localhost:8080/api/deploy/run
# 5. 查看推論統計
echo "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> STDC Stats:"
curl http://localhost:8080/api/stdc/stats | python -m json.tool
# 6. 設定魚眼校正(FEC Mode 4)
curl -X POST http://localhost:8080/api/ini/apply \
-H "Content-Type: application/json" \
-d '{"fec_mode": 4}'
CLI 指令完整參考
完全不用瀏覽器,所有操作均可透過指令完成。
1. 編譯(交叉編譯 ARM binary)
方法 A — build_and_serve.py(推薦)
# 完整流程:編譯 + 檢查 binary + 啟動 HTTP server
python build_and_serve.py
# 常用旗標
python build_and_serve.py --no-build # 跳過編譯,直接 serve(binary 已存在時)
python build_and_serve.py --no-serve # 只編譯 + 檢查,不啟動 server
python build_and_serve.py --port 9090 # 指定 HTTP server port(預設 8080)
python build_and_serve.py --image my-kl630 # 指定 Docker image 名稱
python build_and_serve.py --no-copy # 不複製到網路資料夾
python build_and_serve.py --copy-dst /tmp/out # 指定複製目的地
方法 B — 直接 Docker
# 建立 Docker image(只需一次)
docker build -t kl630-dev .
# 交叉編譯
docker run --rm \
-v "$(pwd):/workspace/kl630_build" \
kl630-dev \
bash /workspace/kl630_build/compile.sh
# 確認輸出
ls -lh build/kp_firmware_host_stream
2. 啟動 HTTP File Server(裝置 wget 用)
# 用 build_and_serve.py(推薦,自動 copy INI + deploy.sh)
python build_and_serve.py --no-build --no-check --port 8080
# 或直接 Python 內建 server
cd build && python -m http.server 8080
3. 部署到裝置(Telnet / 裝置端指令)
先確認 HTTP server 在 PC 端已啟動,再 Telnet 進裝置:
telnet 192.168.3.10
在裝置端執行:
# 一鍵下載並部署(最常用)
wget http://192.168.3.1:8080/deploy.sh -O /tmp/deploy.sh && sh /tmp/deploy.sh
# 或分步驟手動執行:
# 停止舊 firmware
killall -9 kp_firmware_host_stream 2>/dev/null; killall -9 rtsps 2>/dev/null
sleep 1; rm -f /dev/shm/*
# 下載 binary 和 INI
wget http://192.168.3.1:8080/kp_firmware_host_stream -O /mnt/flash/vienna/kp_firmware_host_stream
chmod +x /mnt/flash/vienna/kp_firmware_host_stream
wget http://192.168.3.1:8080/host_stream.ini -O /mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin/ini/host_stream.ini
# 下載並啟動 RTSP demo
wget http://192.168.3.1:8080/demo_rtsp.sh -O /mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin/ini/demo_rtsp.sh
chmod +x /mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin/ini/demo_rtsp.sh
cd /mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin
sh ./ini/demo_rtsp.sh
新機器一次性 ISP 設定(只需跑一次):
wget http://192.168.3.1:8080/deploy.sh -O /tmp/deploy.sh && sh /tmp/deploy.sh --setup
4. 裝置端常用指令
# 確認 firmware 是否在跑
ps | grep firmware
# 查看 firmware log
cat /tmp/fw.log
tail -f /tmp/fw.log # 持續監看(BusyBox 無 -f,用 watch 替代)
# 查看 RTSP demo log
cat /tmp/rtsp_demo.log
# 手動停止 firmware
killall -9 kp_firmware_host_stream
killall -9 rtsps
# 直接啟動(指定模型)
BIN_DIR=/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin
FW=/mnt/flash/vienna/kp_firmware_host_stream
LD_LIBRARY_PATH=/mnt/flash/vienna/lib $FW \
-m nef/stdc_scnn_fp.nef \
-i 100001 -j 1 &
# 切換輸出模式(不用重新 deploy)
sh $BIN_DIR/ini/demo_rtsp.sh # RTSP 串流
sh $BIN_DIR/ini/demo_hdmi.sh # HDMI 顯示
sh $BIN_DIR/ini/demo_rtsp_hdmi.sh # RTSP + HDMI 同時
5. INI 設定(指令修改)
直接用 sed 修改裝置上的 INI,不需要重新 deploy:
INI=/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin/ini/host_stream.ini
# 開啟 / 關閉魚眼校正 FEC
sed -i 's/^fec_mode.*/fec_mode = 4/' $INI # Mode 4(180° Two Direction)
sed -i 's/^fec_mode.*/fec_mode = 0/' $INI # 關閉
# 開啟 / 關閉 DrawBox(H.264 burn-in 框)
sed -i 's/^DrawBoxEnable.*/DrawBoxEnable = 1/' $INI
sed -i 's/^DrawBoxEnable.*/DrawBoxEnable = 0/' $INI
# 開啟 HDMI / VOC 輸出
sed -i 's/^voc_enable.*/voc_enable = 1/' $INI
# 查看目前設定
grep -E "fec_mode|DrawBoxEnable|voc_enable|ModelPath|ModelId" $INI
修改 INI 後需重啟 firmware 才生效:
killall -9 kp_firmware_host_stream 2>/dev/null
sh /mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin/ini/demo_rtsp.sh
6. Mock Server(事件測試)
# 啟動 mock server(port 8081)
python tools/mock_server/server.py
# 開啟監控網頁
# http://localhost:8081/
用 curl 送測試事件(不用開網頁):
# Channel A — 送即時事件(HTTP)
curl -X POST http://localhost:8081/api/event \
-H "Content-Type: application/json" \
-d '{"response_type":"violation","content":{"id":"1","date":"2025-01-01T00:00:00+08:00","type":"road","level":2}}'
# 常用事件類型:road / grass / hazard / person / bunker / pond / tree / car
# level:0=解除, 1=警告, 2=嚴重, 3=緊急
# 查詢事件列表
curl http://localhost:8081/api/events
# Channel C — 送 CAN 事件(測試 CAN bus TX)
curl -X POST http://localhost:8081/api/can/send \
-H "Content-Type: application/json" \
-d '{"type":"hazard","level":1,"can_id":256}'
# 送油門控制指令(CAN ID 0x75)
curl -X POST http://localhost:8081/api/can/send_cmd \
-H "Content-Type: application/json" \
-d '{"cmd":128,"can_id":117}' # cmd: 0=關閉, 32/64/128/255=油門檔位
# 查詢 CAN bus 狀態
curl http://localhost:8081/api/can/status
# 啟動 CAN interface(等同 UI Bring Up 按鈕)
curl -X POST http://localhost:8081/api/can/bringup \
-H "Content-Type: application/json" \
-d '{"channel":"can0","bitrate":250000}'
BLE 指令協議(藍牙通訊)
KL630 透過 DX-BT24 模組(UART /dev/ttyS1 @ 115200)與 iPad / 手機 app 溝通。
所有訊息均為 UTF-8 JSON,無需 CRLF。
Command(App → KL630)
狀態確認
{"command":"status_check"}
回傳 Notify:response_type=status_check
查詢租借狀態
{"command":"get_rent_status"}
回傳 Notify:response_type=rent_status
租借
{"command":"rent"}
若目前 status=0 則轉為 1,其他狀態忽略。回傳 Notify:response_type=rent_status
歸還
{"command":"return"}
若目前 status=1 則轉為 0,其他狀態忽略。回傳 Notify:response_type=rent_status
Notify(KL630 → App)
系統狀態(回應 status_check)
{"response_type":"status_check","content":{"ares_x_version":"1.0.0","bluetooth_peripheral_name":"BT-24","is_intervention_cart_control":false}}
| 欄位 | 說明 |
|---|---|
ares_x_version |
韌體版本,由 INI event:ares_version 設定 |
bluetooth_peripheral_name |
BLE 掃描名稱,由 INI event:bt_name 設定 |
is_intervention_cart_control |
是否正在介入車輛控制(CAN bus 活動中) |
租借狀態(回應 rent / return / get_rent_status)
{"response_type":"rent_status","content":{"status":0}}
| status | 說明 |
|---|---|
0 |
未租借 |
1 |
租借中 |
2 |
管理模式 |
違規事件(由推論結果觸發)
{"response_type":"violation","content":{"id":"abcd-1234-0000-0000","date":"2025-12-08T06:08:49Z","type":"lane","level":2}}
| level | 說明 |
|---|---|
0 |
解除 |
1 |
違規發生 |
2 |
違規持續 6 秒 |
3 |
違規持續 10 秒 |
危險區域警告
{"response_type":"alert","content":{"left_level":0,"right_level":1}}
左右各獨立,0=無危險,1=有危險。
INI 設定(ini/host_stream.ini [event] 區段)
bt_uart_dev = /dev/ttyS1 # UART 裝置路徑
bt_at_probe = 0 # 0: 正常啟動;1: 首次設定(9600→115200 升速)
ares_version = 1.0.0 # 韌體版本字串
bt_name = BT-24 # BLE 掃描名稱
測試指令
用 BLE app(nRF Connect 等)直接測試
複製以下 JSON 貼入 Write Characteristic 欄位送出:
{"command":"status_check"}
{"command":"get_rent_status"}
{"command":"rent"}
{"command":"return"}
用 Mock Server 模擬裝置回傳(curl)
# 模擬違規事件通知
curl -X POST http://localhost:8081/api/event \
-H "Content-Type: application/json" \
-d '{"response_type":"violation","content":{"id":"test-001","date":"2026-04-22T00:00:00Z","type":"lane","level":1}}'
# 模擬違規持續 6 秒
curl -X POST http://localhost:8081/api/event \
-H "Content-Type: application/json" \
-d '{"response_type":"violation","content":{"id":"test-001","date":"2026-04-22T00:00:00Z","type":"lane","level":2}}'
# 模擬違規解除
curl -X POST http://localhost:8081/api/event \
-H "Content-Type: application/json" \
-d '{"response_type":"violation","content":{"id":"test-001","date":"2026-04-22T00:00:00Z","type":"lane","level":0}}'
# 模擬危險區域警告(右側有危險)
curl -X POST http://localhost:8081/api/event \
-H "Content-Type: application/json" \
-d '{"response_type":"alert","content":{"left_level":0,"right_level":1}}'
# 模擬租借狀態更新
curl -X POST http://localhost:8081/api/event \
-H "Content-Type: application/json" \
-d '{"response_type":"rent_status","content":{"status":1}}'
# 查看目前所有事件記錄
curl http://localhost:8081/api/events
Firmware 執行結果(console log)
[STDC] frame=42 mov=1 diff=4.2 bunker=0.0% car=8.3% grass=0.0% greenery=12.1% person=0.0% pond=0.0% road=71.4% tree=8.2%
[STDC] ON ROAD
[STDC WARN] CAR 8.3%
詳細說明
IMX662 DOL-HDR 雙曝光設定、STDC 語義分割架構、新裝置部署 SOP、故障排查: