增加检测模型,增加模拟告警服务,增加压测方案和配置

This commit is contained in:
haotian 2026-02-26 16:51:25 +08:00
parent a69463bd51
commit ad13ad0f88
19 changed files with 3313 additions and 0 deletions

BIN
best_cxn.onnx Normal file

Binary file not shown.

BIN
check0_base_optimize.onnx Normal file

Binary file not shown.

BIN
check1_fold_constant.onnx Normal file

Binary file not shown.

BIN
check2_correct_ops.onnx Normal file

Binary file not shown.

BIN
check3_fuse_ops.onnx Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

281
configs/sample_cam3.json Normal file
View File

@ -0,0 +1,281 @@
{
"queue": { "size": 8, "strategy": "drop_oldest" },
"graphs": [
{
"name": "cam1_sample_full_pipeline",
"nodes": [
{
"id": "in_cam1",
"type": "input_rtsp",
"role": "source",
"enable": true,
"url": "rtsp://10.0.0.49:8554/cam",
"fps": 30,
"width": 1280,
"height": 720,
"use_mpp": true,
"use_ffmpeg": false,
"force_tcp": true,
"reconnect_sec": 5,
"reconnect_backoff_max_sec": 30
},
{
"id": "pre_cam1",
"type": "preprocess",
"role": "filter",
"enable": true,
"dst_w": 640,
"dst_h": 640,
"dst_format": "rgb",
"dst_packed": true,
"keep_ratio": false,
"rga_gate": "cam1_sample_full_pipeline",
"use_rga": true
},
{
"id": "yolo_cam1",
"type": "ai_yolo",
"role": "filter",
"enable": true,
"infer_fps": 10,
"model_path": "./models/yolov5s-640-640.rknn",
"model_version": "v5",
"num_classes": 80,
"conf": 0.35,
"nms": 0.45,
"class_filter": []
},
{
"id": "face_det_cam1",
"type": "ai_face_det",
"role": "filter",
"enable": true,
"model_path": "./models/RetinaFace_mobile320.rknn",
"conf": 0.7,
"nms": 0.4,
"max_faces": 10,
"output_landmarks": true,
"input_format": "rgb"
},
{
"id": "face_recog_cam1",
"type": "ai_face_recog",
"role": "filter",
"enable": true,
"model_path": "./models/mobilefacenet_arcface.rknn",
"align": true,
"emit_embedding": false,
"max_faces": 10,
"input_format": "rgb",
"input_dtype": "uint8",
"threshold": { "accept": 0.45, "margin": 0.05 },
"gallery": {
"backend": "sqlite",
"path": "./models/face_gallery.db",
"load_on_start": true,
"expected_dim": 512,
"dtype": "auto"
}
},
{
"id": "trk_cam1",
"type": "tracker",
"role": "filter",
"enable": true,
"mode": "bytetrack_lite",
"per_class": true,
"state_key": "cam1_sample_full_pipeline",
"track_classes": [0],
"ignore_classes": [],
"allowed_models": ["yolov5", "yolov8"],
"high_th": 0.5,
"low_th": 0.1,
"iou_th": 0.3,
"max_age_ms": 1500,
"min_hits": 2,
"max_tracks": 128,
"debug": { "stats": false, "stats_interval": 200 }
},
{
"id": "pre_face_cam1",
"type": "preprocess",
"role": "filter",
"enable": true,
"dst_w": 0,
"dst_h": 0,
"dst_format": "rgb",
"dst_packed": true,
"keep_ratio": false,
"rga_gate": "cam1_sample_full_pipeline",
"use_rga": true
},
{
"id": "osd_cam1",
"type": "osd",
"role": "filter",
"enable": true,
"draw_bbox": true,
"draw_text": true,
"draw_face_det": false,
"draw_face_bbox": false,
"line_width": 2,
"font_scale": 1,
"use_rga_bbox": false,
"labels": []
},
{
"id": "post_cam1",
"type": "preprocess",
"role": "filter",
"enable": true,
"dst_w": 1280,
"dst_h": 720,
"dst_format": "nv12",
"keep_ratio": false,
"rga_gate": "cam1_sample_full_pipeline",
"use_rga": true
},
{
"id": "pub_cam1",
"type": "publish",
"role": "filter",
"enable": true,
"codec": "h264",
"fps": 30,
"gop": 60,
"bitrate_kbps": 2000,
"use_mpp": true,
"use_ffmpeg_mux": true,
"outputs": [
{ "proto": "rtsp_server", "port": 8555, "path": "/live/cam1" },
{ "proto": "hls", "path": "./web/hls/cam1/index.m3u8", "segment_sec": 2 }
]
},
{
"id": "alarm_cam1",
"type": "alarm",
"role": "sink",
"enable": true,
"eval_fps": 10,
"labels": [],
"rules": [
{
"name": "person_in_view",
"class_ids": [0],
"roi": { "x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0 },
"min_score": 0.4,
"min_box_area_ratio": 0.02,
"require_track_id": true,
"min_duration_ms": 1500,
"min_hits": 3,
"hit_window_ms": 1500,
"cooldown_ms": 5000,
"per_track_cooldown_ms": 5000
}
],
"face_rules": [],
"actions": {
"log": { "enable": true, "level": "info" },
"snapshot": {
"enable": true,
"format": "jpg",
"quality": 85,
"upload": {
"type": "minio",
"endpoint": "http://10.0.0.49:9000",
"bucket": "myminio",
"region": "us-east-1",
"access_key": "minioadmin",
"secret_key": "minioadmin"
}
},
"clip": {
"enable": true,
"pre_sec": 5,
"post_sec": 10,
"format": "mp4",
"fps": 30,
"upload": {
"type": "minio",
"endpoint": "http://10.0.0.49:9000",
"bucket": "myminio",
"region": "us-east-1",
"access_key": "minioadmin",
"secret_key": "minioadmin"
}
},
"http": {
"enable": false,
"url": "http://127.0.0.1:8080/api/alarm",
"timeout_ms": 3000,
"include_media_url": true,
"method": "POST"
},
"external_api": {
"enable": true,
"getTokenUrl": "http://127.0.0.1:8080/api/getToken",
"putMessageUrl": "http://127.0.0.1:8080/api/putMessage",
"tenantCode": "32",
"channelNo": "${vod_channelNo}",
"timeout_ms": 3000,
"include_media_url": true,
"token_header": "X-Access-Token",
"token_json_path": "responseBody.token",
"token_cache_sec": 1200
}
}
},
{
"id": "alarm_face_cam1",
"type": "alarm",
"role": "sink",
"enable": true,
"eval_fps": 5,
"labels": [],
"rules": [],
"face_rules": [
{ "name": "unknown_face", "type": "unknown", "cooldown_ms": 7000, "min_sim": 0.35, "min_hits": 2, "hit_window_ms": 1500, "min_face_area_ratio": 0.01, "min_face_aspect": 0.6, "max_face_aspect": 1.6 },
{ "name": "known_person", "type": "person", "cooldown_ms": 7000, "min_sim": 0.6, "min_hits": 2, "hit_window_ms": 1500, "min_face_area_ratio": 0.01, "min_face_aspect": 0.6, "max_face_aspect": 1.6 }
],
"actions": {
"log": { "enable": false, "level": "info" },
"snapshot": {
"enable": true,
"format": "jpg",
"quality": 85,
"upload": {
"type": "minio",
"endpoint": "http://10.0.0.49:9000",
"bucket": "myminio",
"region": "us-east-1",
"access_key": "minioadmin",
"secret_key": "minioadmin"
}
},
"clip": { "enable": false },
"http": {
"enable": true,
"url": "http://127.0.0.1:8080/api/alarm",
"timeout_ms": 3000,
"include_media_url": true,
"method": "POST"
}
}
}
],
"edges": [
["in_cam1", "pre_cam1"],
["in_cam1", "pre_face_cam1"],
["pre_cam1", "yolo_cam1"],
["yolo_cam1", "trk_cam1"],
["trk_cam1", "osd_cam1"],
["osd_cam1", "post_cam1"],
["post_cam1", "pub_cam1"],
["pub_cam1", "alarm_cam1"],
["pre_face_cam1", "face_det_cam1"],
["face_det_cam1", "face_recog_cam1"],
["face_recog_cam1", "alarm_face_cam1"]
]
}
]
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -8,6 +8,12 @@ winget install Gyan.FFmpeg
- 安装完成后,关闭并重新打开终端,验证:
ffmpeg -version
-查看本地摄像头信息
ffmpeg -list_devices true -f dshow -i dummy
- 本地运行RTSP服务器
mediamtx.exe
- 推流到RTSP服务器设置摄像头的分辨率为720P
ffmpeg -f dshow -rtbufsize 100M -video_size 1280x720 -framerate 30 -vcodec mjpeg -i video="4K AutoFocus Webcam" -c:v libx264 -preset ultrafast -pix_fmt yuv420p -f rtsp rtsp://localhost:8554/cam

View File

@ -0,0 +1,526 @@
# RK3588 4路全流程压力测试方案
本文档描述在单台 RK3588 设备上进行 4 路全流程视频处理的压力测试方案。
**方案说明:** 本方案使用单一 RTSP 视频源 (`rtsp://10.0.0.49:8554/test`)4 条流水线独立处理同一路输入,模拟 4 路摄像头并发场景。配置与 `sample_cam2.json` 完全一致。
---
## 1. 测试目标
### 1.1 核心指标
| 指标项 | 目标值 | 说明 |
|--------|--------|------|
| **并发路数** | 4 路 | 单设备同时处理 4 路视频 |
| **输入分辨率** | 1280×720 @ 30fps | 每路输入为 720p 实时视频 |
| **端到端延迟** | ≤ 500ms | 从采集到输出的完整延迟 |
| **NPU 利用率** | ≥ 70% | NPU 高效利用,但不超载 |
| **CPU 占用** | ≤ 50% | 预留系统余量 |
| **内存占用** | ≤ 2.5GB | 避免内存压力 |
| **运行稳定性** | 24 小时无崩溃 | 长稳测试 |
### 1.2 全流程覆盖
测试需覆盖所有节点类型(与 `sample_cam2.json` 一致的拓扑):
```
[共享输入源]
┌────────────────┼────────────────┐
│ │ │
[Pipeline 1] [Pipeline 2] [Pipeline 3] [Pipeline 4]
│ │ │ │
input_rtsp input_rtsp input_rtsp input_rtsp
│ │ │ │
preprocess preprocess preprocess preprocess
│ │ │ │
ai_yolo ──→ tracker ─┤ ai_yolo ──→ tracker (目标检测+跟踪)
│ │ │ │
ai_face_det ──→ ai_face_recog (人脸识别)
│ │ │ │
└──────────→ osd ←─────────────────────────────┘
preprocess (后处理)
publish (RTSP/HLS 输出)
┌──────────┴──────────┐
│ │
alarm (目标) alarm_face (人脸)
```
**节点类型统计(每路,与 sample_cam2.json 一致):**
- **Source (1)**`input_rtsp`
- **Filter (7)**`preprocess`×2, `ai_yolo`, `ai_face_det`, `ai_face_recog`, `tracker`, `osd`
- **Sink (3)**`alarm`×2, `publish`
**4路总计**
- 4 个 input_rtsp 节点(连接同一 RTSP 源)
- 28 个 Filter 节点
- 12 个 Sink 节点
---
## 2. 测试环境要求
### 2.1 硬件要求
| 项目 | 规格 | 说明 |
|------|------|------|
| **主控芯片** | RK3588 | 4×A76@2.4GHz + 4×A55@1.8GHz |
| **NPU** | 6 TOPS@INT8 | 用于 AI 推理 |
| **内存** | ≥ 8GB LPDDR4/LPDDR5 | 建议 8GB 或以上 |
| **存储** | ≥ 32GB eMMC/SSD | 用于 HLS 切片存储 |
| **网络** | 千兆以太网 | 单路 4-8Mbps 输入4路约 16-32Mbps |
| **散热** | 主动散热 | 长时间高负载运行 |
### 2.2 软件要求
| 项目 | 版本/配置 | 说明 |
|------|-----------|------|
| **操作系统** | Ubuntu 22.04 / Debian 11 | ARM64 架构 |
| **内核** | Linux 5.10+ | 需支持 RGA、MPP、DMA-BUF |
| **NPU 驱动** | rknpu driver 0.9.6+ | 建议最新版本 |
| **CMake** | ≥ 3.20 | 构建工具 |
| **GCC** | ≥ 10.0 | 编译器 |
### 2.3 依赖库检查
```bash
# 检查 NPU 驱动
ls /dev/rknpu
# 检查 RGA 驱动
ls /dev/rga
# 检查内存
free -h
# 检查存储空间
df -h
```
### 2.4 视频源要求
| 项目 | 要求 | 说明 |
|------|------|------|
| **视频源类型** | RTSP Server | 建议使用 ffmpeg 或 ZLMediaKit 推流 |
| **视频源数量** | 1 路共享流 | 4 条流水线共用同一输入 |
| **视频规格** | 1280×720 @ 30fps, H.264 | 模拟真实摄像头 |
| **码率** | 4-8 Mbps | 总输入带宽 4-8 Mbps |
| **内容要求** | 包含行人和人脸 | 用于验证检测和识别功能 |
**视频源准备命令(在 10.0.0.49 服务器执行):**
```bash
# 使用 ffmpeg 推送 1 路 720p 测试流
ffmpeg -re -stream_loop -1 -i test_video.mp4 \
-c:v libx264 -preset fast -b:v 4M -r 30 -s 1280x720 \
-f rtsp rtsp://0.0.0.0:8554/test
# 或使用 ZLMediaKit 等 RTSP 服务器
```
**测试视频内容建议:**
- 包含行人移动(验证目标检测和跟踪)
- 包含正脸人像(验证人脸识别)
- 时长 5-10 分钟,循环播放
### 2.5 外部服务要求
| 服务 | 用途 | 配置 |
|------|------|------|
| **MinIO** | 报警截图/录像存储 | 地址: `10.0.0.49:9000`<br>Bucket: `stress-test`<br>账号: `minioadmin/minioadmin` |
**MinIO 准备(在 10.0.0.49 上执行):**
```bash
# 创建 bucket
mc alias set myminio http://10.0.0.49:9000 minioadmin minioadmin
mc mb myminio/stress-test
```
---
## 3. 测试配置文件
### 3.1 主配置文件
配置文件路径:`configs/stress_test_4ch_shared_source.json`
**配置特点:**
- 4 个独立 Graphstress_cam1 ~ stress_cam4
- 所有 Graph 的输入 RTSP URL 相同:`rtsp://10.0.0.49:8554/test`
- 每路独立输出到不同端口8555-8558
- **节点拓扑和参数与 `sample_cam2.json` 完全一致**
**节点配置详情(每路,与 sample_cam2.json 一致):**
| 节点 | 类型 | 作用 | 关键参数 |
|------|------|------|----------|
| `input_rtsp` | source | 拉取 RTSP 流 | 1280×720@30fps, use_mpp=true |
| `preprocess` | filter | 图像预处理 | 720p → 640×640 RGB, use_rga=true |
| `ai_yolo` | filter | 目标检测 | yolov8n-640.rknn, v8, conf=0.35, nms=0.45, infer_fps=10 |
| `ai_face_det` | filter | 人脸检测 | RetinaFace_mobile320.rknn, conf=0.7, max_faces=10 |
| `ai_face_recog` | filter | 人脸识别 | mobilefacenet_arcface.rknn, align=true, max_faces=10 |
| `tracker` | filter | 目标跟踪 | bytetrack_lite, max_age_ms=1500, track_classes=[0] |
| `preprocess` | filter | 人脸分支预处理 | 原始分辨率 → RGB (pre_face) |
| `osd` | filter | 屏幕显示 | draw_bbox=true, draw_text=true, line_width=2 |
| `preprocess` | filter | 后处理 | 640×640 → 720p NV12, use_rga=true |
| `publish` | sink | 视频输出 | h264, 30fps, 2000kbps, RTSP+HLS |
| `alarm` | sink | 目标报警 | eval_fps=10, MinIO上传截图+录像 |
| `alarm_face` | sink | 人脸报警 | eval_fps=5, MinIO上传截图 |
**输出端口分配:**
| Graph | RTSP 端口 | HLS 路径 |
|-------|-----------|----------|
| stress_cam1 | 8555 | `./web/hls/cam1/index.m3u8` |
| stress_cam2 | 8556 | `./web/hls/cam2/index.m3u8` |
| stress_cam3 | 8557 | `./web/hls/cam3/index.m3u8` |
| stress_cam4 | 8558 | `./web/hls/cam4/index.m3u8` |
**模型文件清单:**
| 模型文件 | 路径 | 说明 |
|----------|------|------|
| yolov8n-640.rknn | `./models/yolov8n-640.rknn` | YOLOv8 目标检测 |
| RetinaFace_mobile320.rknn | `./models/RetinaFace_mobile320.rknn` | 人脸检测 |
| mobilefacenet_arcface.rknn | `./models/mobilefacenet_arcface.rknn` | 人脸识别 |
| face_gallery.db | `./models/face_gallery.db` | 人脸库 |
---
## 4. 测试步骤
### 4.1 环境准备
```bash
# 1. 检查 NPU 驱动
ls /dev/rknpu
# 2. 检查内存
free -h
# 3. 检查存储空间
df -h
# 4. 准备模型文件
ls models/
# 应有yolov8n-640.rknn, RetinaFace_mobile320.rknn, mobilefacenet_arcface.rknn, face_gallery.db
# 5. 创建 HLS 输出目录
mkdir -p web/hls/cam{1,2,3,4}
# 6. 检查网络连接
ping 10.0.0.49
```
### 4.2 启动视频源
在服务器10.0.0.49)上执行:
```bash
# 推送单路 720p 测试流
ffmpeg -re -stream_loop -1 -i test_video.mp4 \
-c:v libx264 -preset fast -b:v 4M -r 30 -s 1280x720 \
-f rtsp rtsp://0.0.0.0:8554/cam1
```
### 4.3 启动测试程序
```bash
cd /home/orangepi/apps/OrangePi3588Media
# 启动 media-server
./build/media-server --config configs/stress_test_4ch_shared_source.json
```
### 4.4 监控脚本
创建监控脚本 `monitor_4ch.sh`
```bash
#!/bin/bash
LOG_FILE="stress_test_4ch_$(date +%Y%m%d_%H%M%S).log"
echo "Time,CPU%,MemMB,NPU%,Temp°C" > $LOG_FILE
while true; do
TIME=$(date '+%H:%M:%S')
# CPU 使用率
CPU=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
# 内存使用 (MB)
MEM=$(free -m | awk 'NR==2{printf "%.0f", $3}')
# NPU 使用率
NPU=$(cat /sys/kernel/debug/rknpu/load 2>/dev/null || echo "0")
# CPU 温度
TEMP=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null | awk '{print $1/1000}')
echo "$TIME,$CPU,$MEM,$NPU,$TEMP" >> $LOG_FILE
echo "[$TIME] CPU:${CPU}% Mem:${MEM}MB NPU:${NPU}% Temp:${TEMP}°C"
sleep 5
done
```
运行监控:
```bash
chmod +x monitor_4ch.sh
./monitor_4ch.sh
```
---
## 5. 测试结果验证方法
### 5.1 功能验证
| 验证项 | 方法 | 通过标准 |
|--------|------|----------|
| **输入连接** | 查看日志 | 4 路都显示 "connected" |
| **视频输出** | VLC 播放 | `rtsp://<rk3588_ip>:8555/live/cam1` ~ `:8558/live/cam4` 都能正常播放 |
| **HLS 输出** | 浏览器/播放器 | 4 路 HLS 流可正常播放 |
| **目标检测** | 观察 OSD 输出 | 画面中出现检测框和 "person" 标签 |
| **人脸识别** | 观察 OSD 输出 | 人脸被框选,显示姓名或 "unknown" |
| **目标跟踪** | 观察 OSD 输出 | 同一目标的 ID 保持稳定 |
| **报警触发** | 查看日志 | alarm 节点输出 "person_detect" 或 "unknown_face" |
| **MinIO 上传** | 检查 MinIO | `stress-test` bucket 中有截图/录像文件 |
| **4路并发** | 同时播放4路输出 | 4路输出画面都流畅无明显卡顿 |
### 5.2 性能验证
| 指标 | 测量方法 | 通过标准 |
|------|----------|----------|
| **帧率** | 日志中 node_output_fps | ≥ 25 fps每路 |
| **延迟** | 对比输入输出时间戳 | ≤ 500ms |
| **NPU 利用率** | `/sys/kernel/debug/rknpu/load` | 50-90% |
| **CPU 占用** | `top` 命令 | ≤ 50% |
| **内存占用** | `free` 命令 | ≤ 2.5GB |
| **队列积压** | 日志中 queue_length | 不持续增长 |
### 5.3 稳定性验证
| 测试项 | 时长 | 通过标准 |
|--------|------|----------|
| **短稳测试** | 1 小时 | 无崩溃,无内存泄漏 |
| **中稳测试** | 8 小时 | 性能指标稳定,无异常重启 |
| **长稳测试** | 24 小时 | 无崩溃,内存增长 < 100MB |
### 5.4 验证命令示例
```bash
# 1. 检查 4 路进程状态
ps aux | grep media-server
# 2. 查看 NPU 负载
watch -n 1 cat /sys/kernel/debug/rknpu/load
# 3. 查看资源使用
htop
# 4. 播放测试(另开终端)
ffplay rtsp://localhost:8555/live/cam1 &
ffplay rtsp://localhost:8556/live/cam2 &
ffplay rtsp://localhost:8557/live/cam3 &
ffplay rtsp://localhost:8558/live/cam4 &
# 5. 检查 HLS 文件生成
ls -la web/hls/cam*/
# 6. 查看日志中的报警
tail -f media-server.log | grep alarm
# 7. 检查 MinIO 上传(在 10.0.0.49 上)
mc ls myminio/stress-test
```
---
## 6. 预期结果
### 6.1 正常情况
```
┌─────────────────────────────────────────────────────────┐
│ 4 路 720p 全流程测试预期结果 │
├─────────────────────────────────────────────────────────┤
│ • 4 路视频正常解码、处理、输出 │
│ • 端到端延迟: 200-400ms │
│ • NPU 利用率: 70-90% │
│ • CPU 占用: 30-50% │
│ • 内存占用: 1.5-2.5GB │
│ • 无丢帧或轻微丢帧 (< 1%)
│ • 报警功能正常触发MinIO上传成功 │
│ • 4路输出画面质量一致 │
└─────────────────────────────────────────────────────────┘
```
### 6.2 性能数据记录表
| 指标 | 测试前 | 1小时 | 8小时 | 24小时 |
|------|--------|-------|-------|--------|
| CPU 平均占用 | - | | | |
| CPU 峰值占用 | - | | | |
| 内存占用 (MB) | - | | | |
| NPU 平均利用率 | - | | | |
| NPU 峰值利用率 | - | | | |
| 平均延迟 (ms) | - | | | |
| 帧率 cam1 (fps) | - | | | |
| 帧率 cam2 (fps) | - | | | |
| 帧率 cam3 (fps) | - | | | |
| 帧率 cam4 (fps) | - | | | |
| 报警次数 (目标) | - | | | |
| 报警次数 (人脸) | - | | | |
| MinIO 文件数 | - | | | |
| 异常重启次数 | - | | | |
### 6.3 输出验证清单
- [ ] 4 路 RTSP 流均可正常播放
- [ ] 4 路 HLS 流均可正常播放
- [ ] 画面中有检测框OSD 正常工作)
- [ ] 检测到人脸时显示姓名或 "unknown"
- [ ] 目标 ID 保持稳定(跟踪正常工作)
- [ ] 日志中有报警输出
- [ ] MinIO `stress-test` bucket 中有文件上传
- [ ] 无 Error/Warning 刷屏
---
## 7. 故障排查
### 7.1 常见问题
| 现象 | 可能原因 | 解决方法 |
|------|----------|----------|
| **启动失败** | NPU 驱动未加载 | `ls /dev/rknpu` 检查,重新加载驱动 |
| **拉流失败** | 网络不通/视频源问题 | 检查网络,`ping 10.0.0.49`,验证 RTSP 流 |
| **高 CPU 占用** | RGA 未启用 | 检查配置 `use_rga: true``use_mpp: true` |
| **高延迟** | 队列积压 | 检查 `queue.size`,适当调小 |
| **NPU 利用率低** | 推理频率设置过低 | 调整 `infer_fps` |
| **4路中某路无输出** | 端口冲突 | 检查端口 8555-8558 是否被占用 |
| **OSD 不显示** | 格式不支持 | 确保 OSD 输入为 RGB/BGR/NV12 格式 |
| **MinIO 上传失败** | 网络/认证问题 | 检查 MinIO 地址和账号密码 |
### 7.2 调试命令
```bash
# 查看 NPU 状态
cat /sys/kernel/debug/rknpu/version
cat /sys/kernel/debug/rknpu/load
# 查看 RGA 状态
cat /sys/kernel/debug/rga/info
# 查看进程资源使用
pidof media-server | xargs -I {} ps -p {} -o pid,ppid,cmd,%cpu,%mem
# 查看线程数
ps -eLf | grep media-server | wc -l
# 查看端口占用
netstat -tlnp | grep media-server
# 查看系统日志
sudo dmesg | tail -50
```
### 7.3 日志分析
```bash
# 检查错误
grep -i "error\|fail\|warn" media-server.log | head -20
# 检查各路口连接状态
grep "input_rtsp\|connected\|disconnected" media-server.log
# 检查帧率
grep "output_fps" media-server.log | tail -20
# 检查报警触发
grep "alarm" media-server.log | tail -20
# 检查 MinIO 上传
grep "minio\|snapshot\|clip" media-server.log | tail -20
```
---
## 8. 测试报告模板
### 8.1 基本信息
- **测试时间**:
- **测试人员**:
- **设备型号**:
- **固件版本**:
- **软件版本**:
- **视频源**: `rtsp://10.0.0.49:8554/test` (1280×720@30fps)
- **MinIO**: `http://10.0.0.49:9000`, bucket: `stress-test`
### 8.2 测试结论
- [ ] 4 路全流程测试通过
- [ ] 性能指标达标
- [ ] 稳定性测试通过
- [ ] MinIO 上传功能正常
### 8.3 详细数据
(填入第 6 章的表格数据)
### 8.4 问题记录
| 序号 | 问题描述 | 严重程度 | 状态 |
|------|----------|----------|------|
| 1 | | | |
| 2 | | | |
### 8.5 优化建议
(根据测试结果填写)
---
## 9. 附录
### 9.1 快速开始
```bash
# 1. 准备环境
cd /home/orangepi/apps/OrangePi3588Media
mkdir -p web/hls/cam{1,2,3,4}
# 2. 确认模型存在
ls models/yolov8n-640.rknn models/RetinaFace_mobile320.rknn \
models/mobilefacenet_arcface.rknn models/face_gallery.db
# 3. 启动测试
./build/media-server --config configs/stress_test_4ch_shared_source.json
# 4. 另开终端监控
./monitor_4ch.sh
# 5. 验证输出
ffplay rtsp://localhost:8555/live/cam1
```
### 9.2 相关文档
- [Readme.md](../Readme.md) - 项目总览
- [deployment.md](deployment.md) - 部署说明
- [dag_graph_node_edge.md](architecture/dag_graph_node_edge.md) - 架构说明
### 9.3 配置文件清单
| 文件 | 说明 |
|------|------|
| `configs/stress_test_4ch_shared_source.json` | 4路全流程测试配置共享源与 sample_cam2.json 一致) |
| `monitor_4ch.sh` | 资源监控脚本 |

BIN
models/best-640.rknn Normal file

Binary file not shown.

BIN
models/yolov5s-640-640.rknn Normal file

Binary file not shown.

BIN
models/yolov8n-640.rknn Normal file

Binary file not shown.

1206
scripts/d8_1.py Normal file

File diff suppressed because it is too large Load Diff

186
scripts/mock_alarm_server.py Executable file
View File

@ -0,0 +1,186 @@
#!/usr/bin/env python3
"""
模拟告警服务器
用于测试 RK3588 Media Server alarm 节点 external_api 功能
提供两个接口
1. POST /api/getToken - 获取访问令牌
2. POST /api/putMessage - 接收告警信息
使用方法
python3 mock_alarm_server.py
默认监听0.0.0.0:8080
"""
from flask import Flask, request, jsonify
from datetime import datetime
import json
import sys
app = Flask(__name__)
# 模拟 token 存储
mock_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.mock_token_for_testing"
token_expire_time = 30 * 60 # 30分钟过期时间
# 统计信息
stats = {
"token_requests": 0,
"alarm_requests": 0,
"last_alarm": None
}
@app.route('/api/getToken', methods=['POST'])
def get_token():
"""
模拟获取 token 接口
返回格式与 d8_1.py 中一致
"""
stats["token_requests"] += 1
# 打印请求信息
print(f"\n[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 收到 Token 请求")
print(f" 请求头: {dict(request.headers)}")
print(f" 请求体: {request.data.decode('utf-8') if request.data else 'None'}")
response = {
"errorDesc": None,
"message": None,
"responseBody": {
"userInfo": {
"username": "szls",
"realname": "数字孪生",
"deptName": "精密铸造厂",
"id": "8a746b7d91deb3270191df35f42e000e"
},
"expireTime": str(token_expire_time),
"token": mock_token,
"refreshToken": mock_token + "_refresh"
},
"retCode": "200"
}
print(f" 响应: 返回 mock token")
return jsonify(response)
@app.route('/api/putMessage', methods=['POST'])
def put_message():
"""
模拟接收告警信息接口
接收格式与 d8_1.py send_post_request 一致
"""
stats["alarm_requests"] += 1
# 获取请求头中的 token
token = request.headers.get('X-Access-Token', 'None')
# 解析请求体
try:
data = request.json if request.is_json else json.loads(request.data)
except:
data = {}
# 打印告警信息
print(f"\n[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 收到告警信息")
print(f" Token: {token[:30]}...")
print(f" 租户代码: {data.get('tenantCode', 'N/A')}")
print(f" 频道号: {data.get('channelNo', 'N/A')}")
print(f" 告警内容: {data.get('alarmContent', 'N/A')}")
print(f" 告警时间: {data.get('alarmTime', 'N/A')}")
# 打印图片/视频信息
pic_info = data.get('picInfo', [])
video_info = data.get('videoInfo', [])
if pic_info:
print(f" 图片地址: {len(pic_info)}")
for i, pic in enumerate(pic_info[:3]): # 只打印前3个
print(f" [{i+1}] {pic.get('url', 'N/A')}")
if len(pic_info) > 3:
print(f" ... 还有 {len(pic_info) - 3}")
if video_info:
print(f" 视频地址: {len(video_info)}")
for i, video in enumerate(video_info[:3]):
print(f" [{i+1}] {video.get('url', 'N/A')}")
# 更新统计
stats["last_alarm"] = {
"time": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
"content": data.get('alarmContent', 'N/A'),
"channel": data.get('channelNo', 'N/A')
}
# 返回成功响应
response = {
"responseBody": "1",
"message": None,
"retCode": "200",
"errorDesc": None
}
print(f" 响应: 告警接收成功")
return jsonify(response)
@app.route('/stats', methods=['GET'])
def get_stats():
"""查看统计信息"""
return jsonify({
"token_requests": stats["token_requests"],
"alarm_requests": stats["alarm_requests"],
"last_alarm": stats["last_alarm"]
})
@app.route('/', methods=['GET'])
def index():
"""首页说明"""
return """
<h1>RK3588 模拟告警服务器</h1>
<p>可用接口:</p>
<ul>
<li><b>POST /api/getToken</b> - 获取访问令牌</li>
<li><b>POST /api/putMessage</b> - 接收告警信息</li>
<li><b>GET /stats</b> - 查看统计信息</li>
</ul>
<p>当前统计:</p>
<ul>
<li>Token 请求次数: {token_requests}</li>
<li>告警请求次数: {alarm_requests}</li>
</ul>
""".format(**stats)
def main():
host = "0.0.0.0"
port = 8080
print("=" * 60)
print("RK3588 模拟告警服务器")
print("=" * 60)
print(f"启动时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"监听地址: http://{host}:{port}")
print("")
print("接口列表:")
print(f" 1. POST http://{host}:{port}/api/getToken")
print(f" 2. POST http://{host}:{port}/api/putMessage")
print(f" 3. GET http://{host}:{port}/stats")
print("")
print("按 Ctrl+C 停止服务")
print("=" * 60)
try:
app.run(host=host, port=port, debug=False, threaded=True)
except KeyboardInterrupt:
print("\n\n服务已停止")
print(f"总计 Token 请求: {stats['token_requests']}")
print(f"总计告警请求: {stats['alarm_requests']}")
sys.exit(0)
if __name__ == '__main__':
main()

Binary file not shown.