Go to file
haotian e4b98a8edb fix: use system MPP/RGA libraries by default
Remove rknpu2 prebuilt paths from library search because they contain
broken symlinks when cloned from Git (symbol link targets not included).

Now CMake searches libraries in priority:
1. User-provided path (RK_MPP_LIB_PATH)
2. Self-built in third_party/mpp/build
3. System default paths (/usr/lib/aarch64-linux-gnu/)

This allows fresh git clone to compile without manual fixes on new RK3588 boards.

Verified: Compiles successfully using build.sh
2026-02-28 17:26:24 +08:00
agent 重新转换了模型,解决了RKNN输出配置错误 2026-02-28 12:37:59 +08:00
cmake/toolchain Initial commit: OrangePi3588 Media Server project 2025-12-08 17:05:22 +08:00
configs 修改配置文件,默认用stretch模式 2026-02-28 17:04:59 +08:00
docs 修改配置文件,默认用stretch模式 2026-02-28 17:04:59 +08:00
include 添加letterbox 模式可以保持目标宽高比,避免变形,同时通过 FrameTransformMeta 自动处理坐标映射 2026-02-28 14:50:21 +08:00
models 修改配置文件,默认用stretch模式 2026-02-28 17:04:59 +08:00
plugins fix: use system MPP/RGA libraries by default 2026-02-28 17:26:24 +08:00
samples Update CMakeLists.txt 2025-12-09 11:18:22 +08:00
scripts 重新转换了模型,解决了RKNN输出配置错误 2026-02-28 12:37:59 +08:00
src 重新转换了模型,解决了RKNN输出配置错误 2026-02-28 12:37:59 +08:00
tests 解耦开发6 2026-01-16 20:29:32 +08:00
third_party Add ZLMediaKit libmk_api.so for embedded HTTP server 2026-02-27 00:09:09 +08:00
transform 重新转换了模型,解决了RKNN输出配置错误 2026-02-28 12:37:59 +08:00
web 修复HLS的bug,增加HLS播放器 2026-02-26 21:07:57 +08:00
.clang-format 性能优化4 2026-01-12 20:51:27 +08:00
.gitignore 去掉对agent编译结果的忽略 2026-02-27 18:10:12 +08:00
Agent_API_Extensions.md 更新agent接口 2026-01-17 16:57:51 +08:00
Agent_Feature_Gaps.md 更新agent接口 2026-01-17 16:57:51 +08:00
API_Device_RemoteMgmt_InterfaceTable.md 更新agent接口 2026-01-17 16:57:51 +08:00
best.onnx.data 修复yolov8的FP16转换错误,增加压测脚本 2026-02-26 20:28:33 +08:00
best.pt Refactor code structure for improved readability and maintainability 2026-02-26 13:52:41 +08:00
check0_base_optimize.onnx 修复yolov8的FP16转换错误,增加压测脚本 2026-02-26 20:28:33 +08:00
check1_fold_constant.onnx 修复yolov8的FP16转换错误,增加压测脚本 2026-02-26 20:28:33 +08:00
check2_correct_ops.onnx 修复yolov8的FP16转换错误,增加压测脚本 2026-02-26 20:28:33 +08:00
check3_fuse_ops.onnx 修复yolov8的FP16转换错误,增加压测脚本 2026-02-26 20:28:33 +08:00
CMakeLists.txt fix: use system MPP/RGA libraries by default 2026-02-28 17:26:24 +08:00
Plan.md 开发sprint5 2025-12-29 16:07:13 +08:00
Readme.md 修复letterbox模式变形的问题 2026-02-28 15:46:10 +08:00
yolov8n_cxn.onnx 修复yolov8的FP16转换错误,增加压测脚本 2026-02-26 20:28:33 +08:00
yolov8n.onnx Refactor code structure for improved readability and maintainability 2026-02-26 13:08:37 +08:00
yolov8n.pt AiScheduler 开发 2025-12-26 15:24:03 +08:00

RK3588 智能视频分析系统 PRDv1.2


一、文档综述

1.1 项目背景

为了充分利用瑞芯微 RK3588 芯片提供的:

  • NPU约 6 TOPSRKNNINT8 推理能力)
  • VPU多路 1080p 硬件解码/编码
  • RGA硬件图像处理缩放/颜色转换/OSD 等)

构建一套纯 C++ 实现、高性能、低延迟、模块化的边缘计算视频分析平台。平台主要部署在园区、仓库、工厂等场景,实现就地智能分析与处理,减少带宽与中心算力压力。

1.2 核心目标

  • 高性能
    • 单 RK3588 支持 816 路 1080p 实时分析
    • 单路端到端(拉流→推理→输出)延迟 < 500ms
  • 低耦合
    • 基于插件 + DAG 流水线架构
    • 算法、业务逻辑与底层框架完全解耦
  • 易运维
    • JSON 配置驱动
    • 支持热更新,无需重启进程即可变更流程/参数
  • 多场景覆盖
    • 同一套框架通过不同配置图覆盖:
      • 安防报警
      • 录像存储
      • 转码网关RTSP/HLS
      • 混合场景(报警 + 存储 + 推流)

二、总体架构设计

2.1 系统逻辑架构

[ media-server 主进程 ]
        |
        +-- Graph Manager        (图管理加载配置、构建DAG、热更新/回滚)
        |
        +-- Plugin Loader        (插件加载dlopen .so检查 ABI 版本)
        |
        +-- AiScheduler          (统一调度 NPU 推理请求)
        |
        +-- Metrics & Logger     (监控指标、日志)
        |
        +-- HTTP Server          (REST API + Web 控制台 + 状态查询)
  • media-server:主入口,负责初始化配置、图构建、主事件循环。
  • Graph ManagerGraphMgr
    • 对应多个 graph每个 graph 一路/一组业务流程)。
    • 实例化节点Node建立边Edge负责热更新时的 diff & 切换。
  • Plugin Loader
    • 动态加载插件 .so,校验节点类型和 ABI 版本。
  • AiScheduler
    • 所有 AI 节点统一将推理任务提交到 AiScheduler由其管理 NPU 资源。
  • HTTP Server
    • 对外提供图状态查询、节点指标、配置热更新触发等。

2.2 数据流架构DAG

  • Graph一个完整业务通道。例如Camera 1 的“拉流→AI→报警+录像+推流”。
  • Node节点:功能基本单元,由动态加载的 .so 实例实现。
  • Edge:节点间的数据通道,每条边对应一个 SPSC单生产者单消费者环形队列
  • 多分支支持:一个节点可以有多个下游(多条边),实现一对多分支。

示意:

[in] → [pre] → [ai_yolo] → [det_post] ───→ [alarm]
                              └──────→ [osd] → [storage]
                                         └──→ [publish]

说明:det_post 是检测结果后处理节点(可选)。它会对 frame->det 做过滤/修正/二次验证,使 OSD 与告警读取到一致的“后处理结果”。

2.3 多硬件解耦架构(已完成)

为支持 RK3588 之外的平台Atlas/Jetson 等),已将硬件强绑定模块抽象为统一接口层,并保留 RK3588 默认实现与 DMA-BUF 快路径:

  • 推理IInferBackend(默认 Rk3588InferBackend
  • 图像处理IImageProcessor(默认 Rk3588ImageProcessorRGA/Swscale 兜底)
  • 编解码IDecoder / IEncoder(默认 RK3588 MPP 路径)
  • 缓冲区FrameBuffer 统一 DMA/内存同步语义

运行时通过 HwFactory 根据配置选择后端实现:

  • 配置字段:platform / hw_platform(不填默认 rk3588
  • 可扩展实现:Atlas* / Jetson*(保持现有业务配置不变,仅切换硬件实现)

三、数据模型Frame & 元数据)

3.1 检测结果结构Detection & DetectionResult

约定:所有 AI 节点写入此结构OSD / 报警 等节点只读。

struct Rect {
    float x;   // 左上角X像素或归一化坐标
    float y;   // 左上角Y
    float w;   // 宽
    float h;   // 高
};

struct Detection {
    int   cls_id;    // 类别ID
    float score;     // 置信度 [0,1]
    Rect  bbox;      // 检测框
    int   track_id;  // 跟踪ID可选无则 -1 或 0
};

struct DetectionResult {
    std::vector<Detection> items;
    int img_w;              // 推理输入宽
    int img_h;              // 推理输入高
    std::string model_name; // 模型名/版本
};

约定:

  • bbox 坐标标准在 PRD 中约定好(建议:像素坐标),避免后续插件互相搞混。
  • class 映射表由 AI 节点自行维护OSD/报警通过 cls_id 做规则匹配。

3.2 Frame 结构

enum class PixelFormat {
    NV12,
    YUV420,
    RGB,
    BGR,
    UNKNOWN
};

struct Frame {
    int width;
    int height;
    PixelFormat format;

    int dma_fd;             // DMA-BUF FD无则为 -1
    uint8_t* data;          // CPU 内存指针可选有则指向内存buffer

    uint64_t pts;           // 时间戳(微秒)
    uint64_t frame_id;      // 单调递增ID

    std::shared_ptr<DetectionResult> det;       // AI 检测结果(通用目标检测)
    std::shared_ptr<FaceDetResult> face_det;    // 人脸检测结果
    std::shared_ptr<FaceRecogResult> face_recog;// 人脸识别结果
    std::shared_ptr<void> user_meta;            // 扩展元数据(具体类型由插件间约定)
};

约定:

  • 零拷贝优先:解码/前处理尽量通过 dma_fd 传递,必要时才用 data。
  • 节点默认只读图像数据,可以更新 det / face_det / face_recog / user_meta
  • 所有队列中传递的都是 std::shared_ptr<Frame>,实现多下游共享。

3.3 人脸检测结果结构FaceDetResult

struct Point2f {
    float x;
    float y;
};

struct FaceDetItem {
    Rect bbox;                          // 人脸框
    float score;                        // 检测置信度
    int track_id;                       // 跟踪ID可选-1表示无
    bool has_landmarks;                 // 是否有关键点
    std::array<Point2f, 5> landmarks;   // 5点关键点左眼/右眼/鼻尖/左嘴角/右嘴角
};

struct FaceDetResult {
    std::vector<FaceDetItem> faces;
    int img_w;                          // 原始图像宽
    int img_h;                          // 原始图像高
    std::string model_name;             // 模型名(如 "retinaface"
};

3.4 人脸识别结果结构FaceRecogResult

struct FaceRecogItem {
    Rect bbox;                          // 人脸框(与检测结果对应)
    int best_person_id;                 // 匹配的人员ID-1表示陌生人
    std::string best_name;              // 匹配的人员姓名("unknown"表示陌生人)
    float best_sim;                     // 最高相似度
    float second_sim;                   // 次高相似度用于margin判定
    bool unknown;                       // 是否为陌生人
    std::vector<float> embedding;       // 特征向量(可选,调试/注册用)
};

struct FaceRecogResult {
    std::vector<FaceRecogItem> items;
    int img_w;
    int img_h;
    std::string model_name;             // 模型名(如 "arcface"
};

四、插件模型与生命周期

4.1 节点通用字段Node 通用配置)

每个节点配置中推荐包含以下通用字段:

{
  "id": "唯一节点ID",
  "type": "节点类型,如 input_rtsp / ai_yolo",
  "enable": true,
  "role": "source|filter|sink",          // 可选,但建议写上,便于校验
  "queue": {                             // 可选:覆盖边上的默认队列策略
    "size": 128,
    "policy": "drop_oldest"              // drop_oldest / drop_newest / block
  },
  "debug": {                             // 可选:调试/日志开关(生产默认建议关闭)
    "stats": false,                      // 各节点的周期性统计日志processed/queue/drops 等)
    "stats_interval": 100,               // 统计日志间隔(帧数)
    "detections": false,                 // 部分 AI 节点的前几帧检测框调试输出
    "ffmpeg_log_level": "error"         // FFmpeg 日志级别(影响 HLS/RTSP mux 的内部日志;默认 error
  },
  "...特定节点参数..."
}
  • enable = falseGraphMgr 构建图时可直接忽略该节点及相关 edges。
  • role 主要用于配置校验source 不应有上游sink 不应有下游等)。

日志/性能建议:

  • 生产环境建议:global.log_level=warn,并保持各节点 debug.stats=false,避免高频 stdout/stderr 造成 CPU 抖动。
  • 需要定位丢帧/队列积压时,再对目标节点打开 debug.stats=true(并把 stats_interval 调大,如 500/1000

4.2 INode 接口(含 Drain

// NodeStatus 用于描述处理结果
enum class NodeStatus {
    OK,       // 正常处理
    DROP,     // 丢弃该帧(例如不满足条件)
    ERROR     // 发生错误,需要记录日志
};

class INode {
public:
    virtual ~INode() = default;

    // 1. 初始化:解析 JSON 配置,申请静态资源
    virtual bool Init(const Json::Value& config) = 0;

    // 2. 启动:启动内部线程(如有),准备接收/发送数据
    virtual bool Start() = 0;

    // 3. 处理数据GraphMgr 从队列中取到 Frame 后调用
    //  - 对 filter/sink 节点:正常消费上游 Frame
    //  - 对 source 节点:通常可忽略(见下文约定)
    virtual NodeStatus Process(std::shared_ptr<Frame> frame) = 0;

    // 4. 动态配置更新支持热更参数如改变报警阈值、bitrate
    //    返回 true 表示就地更新成功,无需重建节点
    virtual bool UpdateConfig(const Json::Value& new_config) { return false; }

    // 5. Drain停止前**不再有新输入**,但需处理完内部队列/缓冲数据
    //    如storage 完成当前切片写入publish 发送剩余帧等
    virtual void Drain() {}

    // 6. 停止释放资源线程、句柄、内存等Drain 之后调用
    virtual void Stop() = 0;
};

4.3 插件导出与 ABI 版本

#define ABI_VERSION 20250101

#define REGISTER_NODE(TYPE_NAME, CLASS_NAME)    \
extern "C" {                                    \
    INode* CreateNode() {                       \
        return new CLASS_NAME();                \
    }                                           \
    const char* GetNodeType() {                 \
        return TYPE_NAME;                       \
    }                                           \
    int GetAbiVersion() {                       \
        return ABI_VERSION;                     \
    }                                           \
    const char* GetNodeDescription() {          \
        return "node: " TYPE_NAME;              \
    }                                           \
}

GraphMgr 在加载插件 .so 时:

  • 调用 GetAbiVersion() 校验兼容性。
  • 通过 GetNodeType() 找到对应 type 的构造函数。

4.4 Source 节点约定

为避免把所有采集逻辑都硬塞进 Process()PRD 中统一约定:

  • role == "source" 的节点(如 input_rtsp / input_file
    • Start() 中启动内部采集线程
    • 线程中从外部拉流/读文件,构造 Frame,通过框架提供的 FrameEmitter 写入下游队列。
    • Process() 可以是空实现或仅做心跳/监控。
  • role != "source" 的节点:
    • 由 GraphMgr 驱动 Process(frame),消费上游队列元素。

GraphMgr 负责:

  • 对非 source 节点:循环从对应队列取 Frame → 调用 Process()
  • 对所有节点:在热更新 / 停止时,控制 Drain()Stop() 的调用顺序。

五、配置系统Config System

整个系统由 config.json 驱动,主要包含三层:

  • global:全局配置
  • templatespipeline 模板(复用节点与拓扑)
  • instances / graphs:具体业务实例(通道)

5.1 顶层结构示例

{
  "global": {
    "plugin_path": "/usr/lib/rknodes",
    "log_level": "info",
    "metrics_port": 9000
  },
  "templates": { ... },
  "instances": [ ... ],
  "graphs": [ ... ]  // 可选直接手写graph
}

说明:global.log_level 只控制框架内部 Loggerdebug/info/warn/error 若启用了 FFmpeg例如 publish 的 HLS mux建议通过节点 debug.ffmpeg_log_level 将 FFmpeg 内部日志降到 error 避免出现大量类似 hls ... Opening ... 的 info 日志刷屏。

  • 优先方式:使用 templates + instances 快速生成多路通道。
  • 高级用法:直接使用 graphs 写复杂拓扑(例如:一条流拆成多个完全不同的分支)。

5.2 DAG 多分支完整示例(带报警 + 存储 + 推流)

{
  "graphs": [
    {
      "name": "cam1_pipeline",
      "nodes": [
        {
          "id": "in_cam1",
          "type": "input_rtsp",
          "role": "source",
          "enable": true,
          "url": "rtsp://user:pwd@ip/stream1"
        },
        {
          "id": "pre_cam1",
          "type": "preprocess",
          "role": "filter",
          "enable": true,
          "dst_w": 768,
          "dst_h": 768,
          "dst_format": "rgb",
          "resize_mode": "letterbox"
        },
        {
          "id": "ai_cam1",
          "type": "ai_yolo",
          "role": "filter",
          "enable": true,
          "model_path": "./models/best-768.rknn",
          "model_version": "v8",
          "model_w": 768,
          "model_h": 768,
          "num_classes": 11,
          "conf": 0.35,
          "nms": 0.45,
          "class_filter": [3, 6]
        },
        {
          "id": "pp_cam1",
          "type": "det_post",
          "role": "filter",
          "enable": true,
          "default": { "enable": false },
          "per_class": [
            {
              "class_id": 0,
              "enable": true,
              "processors": [
                { "type": "conf_gate", "conf_min": 0.65 },
                { "type": "bbox_gate", "min_w": 10, "min_h": 10 }
              ]
            }
          ]
        },
        {
          "id": "osd_cam1",
          "type": "osd",
          "role": "filter",
          "enable": true,
          "draw_bbox": true,
          "draw_text": true
        },
        {
          "id": "alarm_cam1",
          "type": "alarm",
          "role": "sink",
          "enable": true,
          "rules": [
            {
              "name": "intrusion_day",
              "object": ["person"],
              "roi": "roi1",
              "min_duration_ms": 500,
              "schedule": "08:00-20:00"
            }
          ]
        },
        {
          "id": "storage_cam1",
          "type": "storage",
          "role": "sink",
          "enable": true,
          "mode": "continuous",
          "segment_sec": 300,
          "path": "/rec/cam1"
        },
        {
          "id": "publish_cam1",
          "type": "publish",
          "role": "sink",
          "enable": true,
          "codec": {
            "video": "h264",
            "bitrate_kbps": 2048,
            "gop": 50
          },
          "outputs": [
            { "proto": "rtsp", "port": 8554, "path": "/live/cam1" },
            { "proto": "hls",  "path": "/data/hls/cam1/index.m3u8", "segment_sec": 2 }
          ]
        }
      ],
      "edges": [
        ["in_cam1", "pre_cam1"],
        ["pre_cam1", "ai_cam1"],
        ["ai_cam1",  "pp_cam1"],
        ["pp_cam1",  "alarm_cam1"],
        ["pp_cam1",  "osd_cam1"],
        ["osd_cam1", "storage_cam1"],
        ["osd_cam1", "publish_cam1"]
      ]
    }
  ]
}

说明:如果不需要后处理:

  • 推荐:保留 pp_cam1 节点(enable:true),并确保其不启用任何处理链(例如:default.enable=falseper_class 为空),此时 det_post 会作为轻量透传。
  • 若确实要移除该节点:需要同时删除并重连拓扑(例如改回 ai_cam1 -> osd_cam1ai_cam1 -> alarm_cam1),仅设置 enable=false 会导致下游节点无输入而构图失败。

通过简单改动即可支持不同组合:

  • 不报警:alarm_cam1.enable = false 或移除该节点/边。
  • 只转码网关:直接连 ["in_cam1", "publish_cam1"],并禁用 AI/OSD/报警/storage。
  • 只录像:["in_cam1", "storage_cam1"]

5.3 模板templates与实例instances

5.3.1 模板示例:标准安防流程

"templates": {
  "standard_security": {
    "nodes": [
      { "id": "in",  "type": "input_rtsp", "role": "source", "url": "${url}" },
      { "id": "pre", "type": "preprocess", "role": "filter", "dst_w": 768, "dst_h": 768, "resize_mode": "letterbox" },
      { "id": "ai",  "type": "ai_yolo", "role": "filter", "model_path": "./models/best-768.rknn", "model_version": "v8", "num_classes": 11, "conf": 0.35, "class_filter": [3, 6] },
      { "id": "pp",  "type": "det_post", "role": "filter", "enable": true, "default": { "enable": false } },
      { "id": "osd", "type": "osd",      "role": "filter" },
      { "id": "pub", "type": "publish",  "role": "sink", "path": "/live/${name}" }
    ],
    "edges": [
      ["in", "pre"],
      ["pre", "ai"],
      ["ai", "pp"],
      ["pp", "osd"],
      ["osd", "pub"]
    ]
  }
}

5.3.2 实例与 override禁用某些节点

"instances": [
  {
    "name": "cam_01",
    "template": "standard_security",
    "params": {
      "url": "rtsp://192.168.1.101/stream1",
      "name": "office"
    },
    "override": {
      "nodes": {
        "ai":  { "enable": true },
        "osd": { "enable": true }
      }
    }
  },
  {
    "name": "cam_02_only_transcode",
    "template": "standard_security",
    "params": {
      "url": "rtsp://192.168.1.102/stream1",
      "name": "transcoder"
    },
    "override": {
      "nodes": {
        "ai":  { "enable": false },
        "osd": { "enable": false }
      }
    }
  }
]

GraphMgr 在加载时将模板 + 实例 merge 成具体的 graph节点 ID 会按实例名做前缀防冲突)。

5.4 热更新与回滚(简要)

  • 通过 inotify 监控 config.json
  • 检测到变更时:
    1. 读取新配置 → JSON Schema 校验;
    2. 构建新的图对象dry run包括插件加载与节点实例化
    3. 与旧图做 diff
      • UpdateConfig 的节点优先调用 UpdateConfig
      • 需要重建的节点:先创建新节点并预热(如加载模型),然后做 边的原子切换
    4. 对旧节点:依次调用 Drain()Stop()
    5. 若任意步骤失败:回滚到旧配置,并输出错误日志。
  • 保留上一份有效配置,支持手动回滚。

六、功能模块定义(插件类型)

下表是“典型插件类型”,后续可以扩展更多。

6.1 输入与前处理

节点类型 功能描述 关键参数 硬件依赖
input_rtsp 拉取 RTSP 流,断线重连 url, reconnect_sec MPP VDEC / 网络
input_file 读取本地 MP4/MKV 文件循环播放 path, loop MPP VDEC
preprocess 缩放、裁剪、NV12→RGB/BGR 等 dst_w, dst_h, dst_format, resize_mode RGA / GPU

Preprocess 节点参数说明

关键参数:

参数 类型 说明 默认值
dst_w int 输出宽度 640
dst_h int 输出高度 640
dst_format string 输出格式: rgb, bgr, nv12, yuv420 保持原格式
resize_mode string 缩放模式: stretch, keep_ratio, letterbox stretch
use_rga bool 是否使用 RGA 硬件加速 true

resize_mode 说明:

  • stretch: 拉伸填充,可能改变目标宽高比
  • keep_ratio: 保持比例缩放,可能无法填满目标尺寸
  • letterbox: 保持比例缩放,边缘填充黑色(推荐,避免目标变形)

当使用 letterbox 模式时,系统会自动记录变换元数据(FrameTransformMeta),后续节点(如 ai_yoloosd)会自动处理坐标映射,无需手动转换。

6.2 AI 分析

节点类型 功能描述 关键参数 硬件依赖
ai_yolo 通用目标检测YOLOv5/v8 model_path, model_version, num_classes, conf, nms, class_filter NPU(RKNN)

ai_yolo 配置示例best-768.rknn

{
  "id": "yolo_cam1",
  "type": "ai_yolo",
  "model_path": "./models/best-768.rknn",
  "model_version": "v8",
  "model_w": 768,
  "model_h": 768,
  "num_classes": 11,
  "conf": 0.35,
  "nms": 0.45,
  "class_filter": [3, 6]
}

关键参数:

参数 类型 说明
model_path string RKNN 模型路径
model_version string 模型版本: v5v8
num_classes int 模型类别数(必须与实际一致)
conf float 置信度阈值0-1
nms float NMS IoU 阈值0-1
class_filter array 只输出指定类别的检测结果,如 [3, 6] 表示只检测 boots 和 Person
ai_face_det 人脸检测含5点关键点 model_path, conf, nms, max_faces, output_landmarks
ai_face_recog 人脸识别(对齐+特征提取+检索) model_path, gallery, threshold, align
ai_pose 关键点检测(可选扩展) model_path
ai_custom 自定义 AI 算法 model_path, custom_param

6.2.1 det_post检测结果后处理按类别策略链

det_post 是一个 Filter 节点,位于 ai_yolo 之后,负责对 frame->det->items按类别可配置的后处理。

设计目标:

  • 不同类别可以挂不同的处理器链Processor/Strategy Registry避免“一个类别一个插件”。
  • 默认改写 frame->det,让 osd / alarm 读到一致结果。

配置示例:

{
  "id": "pp_cam1",
  "type": "det_post",
  "role": "filter",
  "enable": true,

  "default": { "enable": false },
  "per_class": [
    {
      "class_id": 3,
      "enable": true,
      "processors": [
        { "type": "conf_gate", "conf_min": 0.65 },
        { "type": "bbox_gate", "min_w": 10, "min_h": 10 },
        { "type": "roi_gate", "roi": { "x": 0.1, "y": 0.1, "w": 0.8, "h": 0.8 }, "mode": "center" }
      ]
    },
    {
      "class_id": 5,
      "enable": true,
      "processors": [
        { "type": "hsv_ratio", "h_min": 0, "h_max": 20, "s_min": 20, "v_min": 70, "ratio_min": 0.7,
          "max_samples": 2048, "step": 0, "min_samples": 32,
          "crop": { "x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0 } }
      ]
    },
    {
      "class_id": 7,
      "enable": true,
      "processors": [
        { "type": "lab_kmeans", "k": 3, "iters": 8, "l_max": 50, "ratio_min": 0.4,
          "max_samples": 2048, "step": 0, "min_samples": 64,
          "crop": { "x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0 } }
      ]
    }
  ]
}

内置 processors当前实现

  • conf_gate:按置信度过滤(conf_min
  • bbox_gate:按框尺寸过滤(min_w, min_h
  • roi_gate:按 ROI 过滤(roi:{x,y,w,h} 归一化;mode:center 使用 bbox 中心点判断)
  • hsv_ratio:框内采样像素转 HSV统计阈值命中占比h_min/h_max/s_min/s_max/v_min/v_max/ratio_min,支持 crop/max_samples/step
  • lab_kmeans:框内采样像素转 LabKMeans 聚类后取最暗簇做判定(k/iters/l_max/ratio_min,支持 crop/max_samples/step

注意:hsv_ratio/lab_kmeans 需要 Frame 提供可读的像素数据(frame->data 可访问)。若上游是纯 DMA-BUF 且未映射到 CPU这两类处理器会自然退化为“不过滤”。

ai_yolo 参数说明:

  • model_version: "v5", "v8""auto"(自动检测)
  • num_classes: 模型类别数(默认 80
  • class_filter: 只检测指定类别 ID 的数组,如 [0, 1, 2]

所有 AI 节点通过 AiScheduler 使用 NPU不直接持有 NPU 句柄。

6.2.2 ai_face_det人脸检测

人脸检测节点,基于 RetinaFace/SCRFD 等轻量模型,输出人脸框和 5 点关键点。

配置示例:

{
  "id": "face_det_cam1",
  "type": "ai_face_det",
  "role": "filter",
  "enable": true,
  "model_path": "./models/RetinaFace_mobile320.rknn",
  "conf": 0.6,
  "nms": 0.4,
  "max_faces": 10,
  "output_landmarks": true,
  "input_format": "rgb"
}

参数说明:

  • model_path: RetinaFace RKNN 模型路径
  • conf: 置信度阈值(默认 0.6
  • nms: NMS 阈值(默认 0.4
  • max_faces: 最大检测人脸数
  • output_landmarks: 是否输出 5 点关键点(用于后续对齐)
  • input_format: 输入格式 rgbbgr

输出: 写入 frame->face_det,供 ai_face_recogosd 读取。

6.2.3 ai_face_recog人脸识别

人脸识别节点,读取检测结果,对每个人脸进行对齐、特征提取、人脸库检索。

配置示例:

{
  "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",
  "threshold": {
    "accept": 0.45,
    "margin": 0.05
  },
  "gallery": {
    "backend": "sqlite",
    "path": "./models/face_gallery.db",
    "load_on_start": true,
    "expected_dim": 512
  }
}

参数说明:

  • model_path: ArcFace/MobileFaceNet RKNN 模型路径(输入 112x112输出 512D embedding
  • align: 是否使用 5 点关键点做仿射对齐(推荐开启)
  • emit_embedding: 是否在结果中输出 embedding调试用生产关闭以省内存
  • threshold.accept: 相似度接受阈值(高于此值认为匹配成功)
  • threshold.margin: top1 与 top2 相似度差值阈值(可选,用于降低误识率)
  • gallery.backend: 人脸库后端(当前支持 sqlite
  • gallery.path: SQLite 数据库路径
  • gallery.expected_dim: embedding 维度(需与模型匹配)

判定规则:

  • top1_sim >= accept 且(可选)top1_sim - top2_sim >= margin → 识别为该人
  • 否则标记为 unknown

输出: 写入 frame->face_recog,供 osdalarm 读取。

6.2.4 人脸识别 Pipeline 示例

[input_rtsp] → [preprocess] → [ai_face_det] → [ai_face_recog] → [osd] → [publish]

完整配置示例:

{
  "name": "cam1_face_recog",
  "nodes": [
    { "id": "in_cam1", "type": "input_rtsp", "url": "rtsp://..." },
    { "id": "pre_cam1", "type": "preprocess", "dst_w": 1280, "dst_h": 720, "dst_format": "rgb" },
    { "id": "face_det_cam1", "type": "ai_face_det", "model_path": "./models/RetinaFace_mobile320.rknn", "conf": 0.6 },
    { "id": "face_recog_cam1", "type": "ai_face_recog", "model_path": "./models/mobilefacenet_arcface.rknn", "gallery": { "path": "./models/face_gallery.db" } },
    { "id": "osd_cam1", "type": "osd", "draw_face_det": true },
    { "id": "pub_cam1", "type": "publish", "outputs": [{ "proto": "rtsp_server", "port": 8555, "path": "/live/face" }] }
  ],
  "edges": [
    ["in_cam1", "pre_cam1"],
    ["pre_cam1", "face_det_cam1"],
    ["face_det_cam1", "face_recog_cam1"],
    ["face_recog_cam1", "osd_cam1"],
    ["osd_cam1", "pub_cam1"]
  ]
}

注意:人脸识别应在原始分辨率(如 1280x720上进行检测模型内部会自动缩放到 320x320 推理,输出坐标缩放回原始尺寸。这样从高清图裁剪对齐人脸,识别质量更高。

6.2.5 人脸库Gallery说明

人脸库使用 SQLite 存储,表结构:

CREATE TABLE person (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    created_at TEXT,
    extra TEXT           -- JSON 扩展字段
);

CREATE TABLE embedding (
    id INTEGER PRIMARY KEY,
    person_id INTEGER NOT NULL,
    embedding BLOB NOT NULL,  -- 512D float32 向量
    created_at TEXT,
    FOREIGN KEY (person_id) REFERENCES person(id)
);

检索实现:

  • 小库≤几千人CPU 暴力点积,无需额外索引
  • 大库(≥几万人):可扩展 HNSW/Faiss 等 ANN 索引

6.3 业务逻辑与输出

节点类型 功能描述 关键参数 硬件依赖
osd 绘制检测框、文字等 draw_bbox, draw_text, labels, line_width, font_scale RGA / CPU
alarm 报警规则 + 多种报警动作 rules, actions(见下文详细说明) CPU
storage 持续录像MP4/TS 切片) mode, segment_sec, path MPP VENC + IO
publish 编码并推流RTSP/HLS codec, bitrate, outputs MPP VENC + 网络
zlm_http 内嵌 HTTP 文件服务(为 HLS 提供访问) root, prefix, port, ssl ZLMediaKit(mk_api)

说明:当前实现中 publishproto:"hls"落盘生成 m3u8/ts 文件path 应指向 index.m3u8 文件路径;若需要通过网络播放,建议新增 zlm_http 节点启动内嵌 HTTP并将 root 指向 HLS 父目录(如 /data/hls)。

osd 参数说明:

  • labels: 自定义类别标签数组,如 ["cat", "dog", "bird"],为空则使用 COCO 默认标签

6.4 alarm 插件详细说明

alarm 插件采用模块化 Actions 架构,支持多种报警动作的灵活组合:

架构设计:

┌─────────────────────────────────────────────────────────┐
│  AlarmNode                                              │
│  ┌──────────────┐  ┌──────────────────────────────────┐ │
│  │ RuleEngine   │  │ FrameRingBuffer (pre-event)     │ │
│  │ - 类别过滤    │  │ - 保存最近 N 秒的帧             │ │
│  │ - ROI 检测    │  └──────────────────────────────────┘ │
│  │ - 持续时间    │                                       │
│  │ - 冷却控制    │  ┌──────────────────────────────────┐ │
│  └──────────────┘  │ AlarmActions (可配置)             │ │
│                    │  - LogAction      → 日志输出      │ │
│                    │  - HttpAction     → HTTP 回调     │ │
│                    │  - ExternalApiAction → 外部API报警 │ │
│                    │  - SnapshotAction → 截图+上传     │ │
│                    │  - ClipAction     → 视频片段+上传 │ │
│                    └──────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

完整配置示例:

{
  "id": "alarm_cam1",
  "type": "alarm",
  "role": "sink",
  "labels": ["矿体A", "矿体B", "异物"],
  "rules": [
    {
      "name": "ore_detection",
      "class_ids": [0, 1],
      "objects": ["矿体A"],
      "roi": { "x": 0.1, "y": 0.1, "w": 0.8, "h": 0.8 },
      "min_duration_ms": 500,
      "cooldown_ms": 5000,
      "schedule": "08:00-20:00"
    }
  ],
  "actions": {
    "log": { "enable": true, "level": "info" },
    "http": {
      "enable": true,
      "url": "http://业务系统/api/alarm",
      "timeout_ms": 3000,
      "include_media_url": true
    },
    "external_api": {
      "enable": false,
      "getTokenUrl": "http://业务系统/api/getToken",
      "putMessageUrl": "http://业务系统/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": 600
    },
    "snapshot": {
      "enable": true,
      "format": "jpg",
      "quality": 85,
      "upload": {
        "type": "minio",
        "endpoint": "http://minio:9000",
        "bucket": "alarms",
        "access_key": "${MINIO_ACCESS_KEY}",
        "secret_key": "${MINIO_SECRET_KEY}"
      }
    },
    "clip": {
      "enable": true,
      "pre_sec": 5,
      "post_sec": 10,
      "format": "mp4",
      "upload": { "type": "local", "path": "/tmp/alarms" }
    }
  }
}

external_api 动作说明(对接外部系统报警:先取 token再上报告警

  • 取 tokengetTokenUrl 发起 HTTP POST默认从响应 JSON 的 responseBody.token 读取(可用 token_json_path 修改点号路径)。
  • 上报告警:对 putMessageUrl 发起 HTTP POST并携带 HeaderContent-Type: application/jsonX-Access-Token: <token>(可用 token_header 修改)。
  • 上报请求体(固定结构):
{
  "tenantCode": "32",
  "channelNo": "<vod_channelNo>",
  "alarmContent": "<msg>",
  "alarmTime": "YYYY-MM-DD HH:MM:SS",
  "picInfo": [{"url": "<picUrl>"}],
  "videoInfo": [{"url": "<videoUrl>"}]
}
  • alarmTime 使用本地时间格式化。
  • picUrl/videoUrl 来自 snapshot/clip 动作上传后的 URL通常为 MinIO 返回链接);若为空则对应数组为空。
  • alarmContent:若配置中未提供 alarmContent,默认使用 rule_name

external_api 参数说明:

  • 必填:getTokenUrl, putMessageUrl, tenantCode, channelNo
  • 常用可选:timeout_ms(默认 3000include_media_url(默认 truetoken_cache_sec(默认 600为 0 时仅在 401/403 时刷新 token
  • 其它可选:token_headertoken_json_pathalarmContentmax_queue_size / queue_policymax_retries / retry_backoff_ms

规则参数说明:

  • class_ids: 要监控的类别 ID 数组(与模型输出对应)
  • objects: 要监控的类别名称数组(需配合 labels 使用)
  • roi: 感兴趣区域,归一化坐标 (0-1)
  • min_duration_ms: 目标在 ROI 内持续多久才触发报警
  • cooldown_ms: 同一规则连续触发的冷却时间
  • schedule: 有效时间段,格式 "HH:MM-HH:MM"

上传器类型:

  • local: 保存到本地文件系统
  • minio: 上传到 MinIO 对象存储
  • s3: 上传到 AWS S3兼容 MinIO

6.5 storage 插件详细说明

storage 插件用于持续录像7x24 小时),与 alarm 的事件录像功能互补。

配置示例:

{
  "id": "storage_cam1",
  "type": "storage",
  "role": "sink",
  "mode": "continuous",
  "format": "mp4",
  "codec": "h264",
  "segment_sec": 300,
  "path": "/rec/cam1",
  "filename_pattern": "%Y%m%d/%H%M%S",
  "fps": 25,
  "bitrate_kbps": 2000
}

参数说明:

  • mode: 录像模式,当前支持 continuous(持续录像)
  • format: 输出格式,mp4ts
  • segment_sec: 单个文件时长(秒)
  • filename_pattern: 文件名模式,支持 strftime 格式

七、线程模型Thread Model

7.1 线程类型与职责

线程类型 职责 绑核策略(可配置)
main 程序入口、配置加载、GraphMgr 调度 默认 CPU0
input-io RTSP 网络 IO 大核/通用
decode 调用 MPP 解码 大核
preprocess RGA/GPU 前处理 视情况绑核
ai-worker 调度 AiScheduler异步调用 NPU 大核
post/osd OSD 绘制、报警逻辑等 小核优先
storage 文件写入、切片管理 小核
publish RTSP/HLS 推流 小核
metrics/log 指标采集、日志异步写入 任意核

7.2 配置控制

  • 节点配置中可增加字段:
"cpu_affinity": [2, 3]   // 可选
  • 若未配置,由框架根据 role 和负载自动分配。

八、性能指标Performance Targets

场景 指标 目标值
8×1080p@25fps 整机总处理帧率 ≥ 200 fps
单路端到端 延迟 ≤ 500 ms典型 ≤ 380
NPU 利用率 ≥ 90%
8 路全开 CPU 占用 ≤ 30%
7×24 小时运行 内存泄漏 无明显增长(< 10MB

测试要求(简要):

  • 在实际 RK3588 设备上,使用典型 YOLO 模型和 8 路 RTSP 输入进行长稳测试。
  • 统计长时间运行中的 CPU、内存、NPU 使用情况。

九、监控与运维Metrics & Ops

9.1 节点级指标

每个节点提供以下基本指标(通过 HTTP / Prometheus 等暴露):

  • node_input_fps / node_output_fps
  • queue_length(入队/出队队列长度)
  • frame_drop_count
  • error_count
  • avg_process_time_msProcess 平均处理耗时)

9.2 图 / 通道级指标

  • 通道状态running / error / disabled
  • 报警次数 / 最近报警时间
  • 当前推流客户端数RTSP/HLS
  • 当前录像文件信息(路径 / 大小 / 最近片段时间)

9.3 管理接口REST 简要)

  • GET /api/graphs:列出所有 graph 状态
  • GET /api/graphs/{name}:单通道详情
  • POST /api/config/reload触发配<EFBFBD><EFBFBD><EFBFBD>重加载
  • GET /api/nodes/{id}/metrics:查询节点指标
  • POST /api/nodes/{id}/config:动态更新节点配置(调用 UpdateConfig

十、开发与部署环境

  • 硬件平台RK35884×A76 + 4×A55 + NPU + VPU + RGA
  • 工具链
    • 板上编译GCC 10+(推荐)
    • 交叉编译:aarch64-linux-gnu-gcc 12+(可选)
    • 构建CMake ≥ 3.20
  • 调试
    • gdbserver + VSCode 远程调试
    • RKNN Profiler 分析 NPU 利用率
  • 日志
    • spdlog 异步日志
    • 日志级别可运行时调整(通过配置或 REST 接口)

10.1 板上编译(推荐)

在 RK3588 板子上直接编译,无需配置交叉编译环境。

依赖安装:

# Ubuntu/Debian
sudo apt update
sudo apt install -y build-essential cmake git pkg-config \
    libavformat-dev libavcodec-dev libavutil-dev libswscale-dev

编译命令:

cd Rk3588Sys

# 配置(启用所有硬件加速)
cmake -S . -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DRK3588_ENABLE_FFMPEG=ON \
  -DRK3588_ENABLE_MPP=ON \
  -DRK3588_ENABLE_RGA=ON \
  -DRK3588_ENABLE_ZLMEDIAKIT=ON \
  -DRK_ZLMK_API_LIB_PATH=$PWD/third_party/rknpu2/examples/3rdparty/zlmediakit/aarch64/libmk_api.so \
  -DRK_ZLMEDIAKIT_INCLUDE_DIR=$PWD/third_party/rknpu2/examples/3rdparty/zlmediakit/include

# 编译
cmake --build build -j$(nproc)

CMake 选项说明:

选项 说明 默认值
RK3588_ENABLE_FFMPEG 启用 FFmpeg 拉流/软解码 OFF
RK3588_ENABLE_MPP 启用 MPP 硬件编解码 OFF
RK3588_ENABLE_RGA 启用 RGA 硬件图像处理 OFF
RK3588_ENABLE_RKNN 启用 RKNN NPU 推理 OFF
RK3588_ENABLE_ZLMEDIAKIT 启用内置 RTSP Server OFF

运行:

# 纯转码网关
./build/media-server --config configs/sample_cam1.json

# 带预处理的 pipeline
./build/media-server --config configs/sample_preprocess.json

# 内置 RTSP Server 模式
./build/media-server --config configs/sample_cam1_rtsp_server.json

10.2 交叉编译(可选)

在 x86 主机上交叉编译,适合 CI/CD 或批量部署。

# 需要安装交叉编译工具链
sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu

# 配置
cmake -S . -B build-cross \
  -DCMAKE_TOOLCHAIN_FILE=cmake/aarch64-linux-gnu.cmake \
  -DCMAKE_BUILD_TYPE=Release \
  -DRK3588_ENABLE_FFMPEG=ON \
  -DRK3588_ENABLE_MPP=ON \
  -DRK3588_ENABLE_RGA=ON

# 编译
cmake --build build-cross -j$(nproc)

# 同步到板子
rsync -avz build-cross/ user@board_ip:/opt/media-server/

十一、扩展与演进

  1. 新增算法插件

    • 实现新的 INode 子类(例如 ai_cls, ai_reid)。
    • 使用 REGISTER_NODE 编译为 .so 放入 plugin_path
    • config.json 中新增节点即可接入全流程。
  2. 新增输出类型

    • 新增 publish_webrtcstorage_s3 等节点类型。
    • 不改动上游图结构,只调整 sink 部分。
  3. 分布式扩展(后续版本)

    • 引入 ZeroMQ / NanoMSG将远端设备作为特殊节点处理remote_sink)。
    • 中控只维护抽象图,实际在多台 RK3588 设备上分布执行。


附录:问题修复与调试文档

已知问题修复

问题 文档 日期
YOLOv8 FP16 数据解析错误 docs/bugfix_yolov8_fp16.md 2025-02
RKNN 输出格式配置错误 docs/fix_rknn_output_format.md 2026-02

调试与开发文档