现在的智能交通摄像头普遍都是“傻摄像头”:只能采集视频,所有计算都传到云端处理,有三个致命问题:一是延迟高,从采集到云端返回结果要200ms以上,违章抓拍、交通事故预警根本来不及;二是带宽成本高,一路1080P摄像头一个月的流量费就要几十块钱,几百路的话成本特别高;三是隐私风险大,所有视频都传到云端,很容易泄露。

去年我给某三线城市做智慧道路改造项目,用瑞芯微RK3566搭建边缘AI网关,本地处理8路摄像头的视频流,跑YOLO12-N做车辆、行人、违章检测,端到端延迟只有82ms,完全在本地处理,不需要传视频到云端,带宽成本降为0,误报率低于1%,已经稳定运行6个月了。

今天把完整的边缘AI网关搭建方案分享给大家,不管是智能交通、安防还是工业检测都能用,单网关成本只要300多块钱,比云端方案成本降了90%以上。

一、项目需求与架构设计

1.1 项目需求

  • 接入8路1080P 25FPS的RTSP摄像头流
  • 实时检测车辆、行人、闯红灯、压线、逆行5种目标/事件
  • 端到端延迟≤100ms(从摄像头采集到事件上报的时间)
  • 检测准确率≥95%,误报率≤1%
  • 网关功耗≤10W,7×24小时稳定运行
  • 事件上报到本地平台,不需要上传视频流到云端

1.2 硬件选型

选瑞芯微RK3566作为网关的核心处理器,原因是:

  • 算力强:有1TOPS的NPU,跑YOLO12-N 640×640可以跑到30FPS以上
  • 功耗低:整板功耗≤5W,加外设也不到10W,适合长期运行
  • 接口丰富:有2个千兆网口,支持POE供电,可以直接接POE摄像头,不用额外供电
  • 成本低:开发板只要200多块钱,加上外壳、电源整机成本不到400块钱
  • 生态完善:RKNN-Toolkit2支持YOLO系列模型的量化和部署,资料多,踩坑少

如果需要更高的算力,可以选RK3588,有6TOPS NPU,可以接入16路以上摄像头,成本只要800多块钱。

1.3 软件架构设计

采用流水线多线程架构,最大限度降低延迟:

RTSP拉流线程(8个) → 帧预处理线程(4个) → NPU推理线程(2个) → 后处理&事件上报线程(2个)

每个环节用无锁队列传递数据,避免线程切换开销,所有内存都是零拷贝,不用来回拷贝数据,延迟特别低。

二、网关搭建完整步骤

2.1 硬件准备

  • RK3566开发板(推荐用野火或者瑞芯微官方的开发板,资料全)
  • 8路POE交换机(给摄像头供电和传输数据)
  • 固态硬盘(存事件截图和视频片段,至少128G)
  • 外壳+散热风扇(长期运行一定要做好散热,不然会降频)

总硬件成本不到400块钱,就能接8路摄像头,比买商用的边缘网关便宜好几倍。

2.2 系统环境配置

先给开发板刷入官方的Ubuntu 20.04固件,然后安装依赖:

# 安装RKNN-Toolkit2运行时
sudo apt install -y python3 python3-pip librknnrt2
pip3 install rknn-toolkit2==1.4.0

# 安装FFmpeg和GStreamer,用来拉RTSP流
sudo apt install -y ffmpeg libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-good gstreamer1.0-plugins-bad
pip3 install opencv-python numpy paho-mqtt

2.3 模型优化与转换

YOLO12-N是专门为边缘设备设计的轻量模型,精度比YOLOv8n高2%,速度还快15%,特别适合边缘部署。

第一步:训练自定义模型

用交通场景的数据集训练YOLO12-N,检测车辆、行人、红灯、车道线4类目标,训练完之后mAP@0.5达到96.8%。

第二步:INT8量化转换为RKNN模型

用RKNN-Toolkit2把模型转换成RKNN格式,做INT8量化:

from rknn.api import RKNN

rknn = RKNN()
# 加载ONNX模型
rknn.load_onnx(model="yolov12n-traffic.onnx")
# 配置量化参数
rknn.config(
    target_platform="rk3566",
    mean_values=[[0, 0, 0]],
    std_values=[[255, 255, 255]],  # YOLO的归一化是除以255,所以均值0,方差255
    quantize_dtype="int8",
    optimization_level=3
)
# 量化校准,用200张实际交通场景的图片
rknn.build(do_quantization=True, dataset="calib.txt")
# 导出RKNN模型
rknn.export_rknn("yolov12n-traffic.rknn")
rknn.release()

转换之后的模型只有2.3M,在RK3566上推理速度达到32FPS,量化后精度96.2%,只掉了0.6%。

2.4 低延迟推理代码实现

核心是用多线程流水线+零拷贝,我写了一个简化版的代码示例:

import cv2
import numpy as np
import rknn_runtime
import queue
import threading
import time
import paho.mqtt.client as mqtt
import json

# 配置
RTSP_URLS = [
    "rtsp://admin:123456@192.168.1.100/stream1",
    "rtsp://admin:123456@192.168.1.101/stream1",
    # 剩下6路摄像头地址
]
MODEL_PATH = "yolov12n-traffic.rknn"
MQTT_HOST = "192.168.1.200"  # 本地事件平台地址

# 无锁队列,线程间传递数据
frame_queue = queue.Queue(maxsize=16)
result_queue = queue.Queue(maxsize=16)

# RKNN推理类
class RKNNInfer:
    def __init__(self, model_path):
        self.rknn = rknn_runtime.RKNN(model_path)
        self.input_size = (640, 640)
    
    def preprocess(self, frame):
        # 预处理:用OpenCV的向量化操作,零拷贝
        h, w = frame.shape[:2]
        scale = min(self.input_size[0]/h, self.input_size[1]/w)
        new_h, new_w = int(h*scale), int(w*scale)
        # 用cv2.INTER_NEAREST速度最快,精度损失可以忽略
        img_resized = cv2.resize(frame, (new_w, new_h), interpolation=cv2.INTER_NEAREST)
        pad_h, pad_w = (self.input_size[0]-new_h)//2, (self.input_size[1]-new_w)//2
        # 直接在原始数组上修改,不拷贝
        img_padded = cv2.copyMakeBorder(img_resized, pad_h, self.input_size[0]-new_h-pad_h, pad_w, self.input_size[1]-new_w-pad_w, cv2.BORDER_CONSTANT, value=(114,114,114))
        # BGR转RGB,HWC转CHW,用numpy的视图,不拷贝
        img_input = img_padded[..., ::-1].transpose(2, 0, 1)
        img_input = np.ascontiguousarray(img_input, dtype=np.uint8)  # RKNN接受UINT8输入,不用归一化,速度更快
        return img_input, scale, pad_h, pad_w
    
    def infer(self, img_input):
        # 推理,RKNN的输入输出都是零拷贝
        outputs = self.rknn.inference(inputs=[img_input])
        return outputs

# RTSP拉流线程
def rtsp_worker(rtsp_url, camera_id):
    cap = cv2.VideoCapture(rtsp_url, cv2.CAP_FFMPEG)
    # 设置缓冲区大小为1,减少延迟,不要缓存帧
    cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
    while True:
        ret, frame = cap.read()
        if not ret:
            time.sleep(0.01)
            cap.open(rtsp_url)
            continue
        # 跳帧策略:队列满了就丢旧帧,保证最新的帧被处理
        if frame_queue.full():
            try:
                frame_queue.get_nowait()
            except:
                pass
        frame_queue.put((camera_id, time.time(), frame))

# 推理线程
def infer_worker():
    infer = RKNNInfer(MODEL_PATH)
    while True:
        camera_id, ts, frame = frame_queue.get()
        img_input, scale, pad_h, pad_w = infer.preprocess(frame)
        outputs = infer.infer(img_input)
        result_queue.put((camera_id, ts, frame, outputs, scale, pad_h, pad_w))

# 后处理和事件上报线程
def postprocess_worker():
    mqtt_client = mqtt.Client()
    mqtt_client.connect(MQTT_HOST, 1883, 60)
    while True:
        camera_id, ts, frame, outputs, scale, pad_h, pad_w = result_queue.get()
        # 后处理,解析YOLO输出
        results = postprocess(outputs, scale, pad_h, pad_w)
        # 检测违章事件:闯红灯、压线、逆行
        events = detect_violation(results, camera_id)
        # 上报事件,只上报事件和截图,不上传视频
        for event in events:
            event["camera_id"] = camera_id
            event["timestamp"] = ts
            # 把截图编码成JPG,大小只有几十KB
            _, img_encoded = cv2.imencode(".jpg", frame, [cv2.IMWRITE_JPEG_QUALITY, 80])
            event["image"] = img_encoded.tobytes()
            mqtt_client.publish(f"traffic/event/{camera_id}", payload=json.dumps(event))
        # 计算延迟
        delay = (time.time() - ts) * 1000
        print(f"Camera {camera_id} delay: {delay:.1f}ms")

if __name__ == "__main__":
    # 启动拉流线程,每个摄像头一个线程
    for i, url in enumerate(RTSP_URLS):
        threading.Thread(target=rtsp_worker, args=(url, i), daemon=True).start()
    # 启动2个推理线程
    for _ in range(2):
        threading.Thread(target=infer_worker, daemon=True).start()
    # 启动2个后处理线程
    for _ in range(2):
        threading.Thread(target=postprocess_worker, daemon=True).start()
    # 主线程保活
    while True:
        time.sleep(3600)

2.5 低延迟优化技巧

我做了几个关键优化,把延迟从原来的200ms降到了82ms:

  1. 设置FFmpeg缓冲区为1:默认的FFmpeg缓冲区会缓存3-5帧,延迟会增加100ms以上,设置为1之后只缓存最新的1帧
  2. 跳帧策略:队列满了就丢旧帧,永远处理最新的帧,避免队列堆积导致延迟越来越高
  3. 零拷贝预处理:所有预处理都用numpy视图和OpenCV的原地操作,不做内存拷贝,预处理耗时从10ms降到2ms
  4. UINT8输入:RKNN直接接受0-255的UINT8输入,不用做归一化,节省了归一化的时间,还减少了精度损失
  5. 多线程流水线:拉流、预处理、推理、后处理分别用不同的线程,并行处理,吞吐量提升2倍以上

三、实测效果

我们在实际道路上做了7×24小时测试,效果如下:

  • 平均延迟:82ms,最大延迟95ms,远低于100ms的要求
  • 检测准确率:96.2%,误报率0.8%,满足项目要求
  • 吞吐量:同时处理8路25FPS的流,CPU占用率只有40%,NPU占用率75%,还有余量
  • 功耗:整板功耗6.8W,一年的电费只有不到20块钱
  • 稳定性:连续运行6个月没有出现过崩溃或者死机的情况,掉电自动重启,自动重连摄像头

和云端方案对比:

方案 端到端延迟 单路月成本 隐私安全性 稳定性
云端处理 200-300ms 50元/路 低,视频传到云端 依赖网络,网络断了就用不了
边缘网关 80ms左右 5元/路(只有电费) 高,所有数据本地处理 不依赖外网,断网也能正常运行

单路的月成本只有云端的1/10,8路摄像头一年就能省4000多块钱,性价比特别高。

四、落地避坑指南

  1. 一定要做散热:RK3566跑满的时候温度会升到60度以上,不加散热的话会降频,速度变慢甚至死机,我用的是铝制外壳加散热风扇,温度稳定在40度左右
  2. RTSP流用TCP传输:默认UDP传输容易丢包,导致花屏,拉流的时候加上?tcp后缀,比如rtsp://xxx/stream1?tcp,用TCP传输更稳定
  3. 不要用太高的分辨率:1080P足够了,2K的话会增加预处理和推理的耗时,延迟会升高,而且1080P的精度已经足够检测交通违章了
  4. 事件存储用循环覆盖:本地存储的事件和截图要设置循环覆盖,不然硬盘满了会导致网关崩溃,我设置的是保留最近7天的事件,满了自动删除最早的
  5. 远程运维要做好:网关要支持远程SSH登录,远程升级模型,远程查看日志,不然几百个网关部署之后,运维成本会特别高,我用的是frp做内网穿透,远程管理特别方便

五、总结

边缘AI网关是未来智能感知的主流方向,成本低、延迟低、隐私性好,不管是智能交通、安防监控还是工业检测,都能大大降低落地成本。我这套方案已经在多个城市的智慧交通项目落地,效果稳定,成本只有商用方案的1/10,非常适合中小项目落地。

如果你们也有边缘端实时检测的需求,强烈建议试试这个方案,硬件成本几百块钱,半天就能搭好,比买商用网关划算太多了。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐