3. 在Jetson Nano上运行YOLOv4-tiny进行视频流目标检测

任务目标

在Jetson Nano(JetPack 4.6,ARM64架构)上从源码编译Darknet框架,使用预训练的YOLOv4-tiny权重,对USB摄像头实时视频流执行目标检测,并在显示窗口中叠加边界框、类别标签与实时FPS。

环境与依赖

  • 硬件:Jetson Nano(2GB/4GB均可),USB摄像头(UVC协议)
  • 系统:JetPack 4.6(Ubuntu 18.04,aarch64),已安装CUDA 10.2、cuDNN 8.2、OpenCV 4.1.1(随JetPack预装)
  • 依赖包:build-essential、git、wget

实现步骤

1. 克隆并配置Darknet

Darknet官方仓库已支持Jetson平台的CUDA后端。克隆后需修改Makefile以启用GPU、cuDNN和OpenCV,并适配Jetson的算力架构(5.3)。

# 安装基础依赖
sudo apt update && sudo apt install -y build-essential git wget

# 克隆Darknet
git clone https://github.com/pjreddie/darknet.git
cd darknet

# 修改Makefile配置
sed -i 's/GPU=0/GPU=1/' Makefile
sed -i 's/CUDNN=0/CUDNN=1/' Makefile
sed -i 's/OPENCV=0/OPENCV=1/' Makefile
# Jetson Nano的Maxwell架构算力为5.3
sed -i 's/ARCH= -gencode arch=compute_30,code=sm_30/ARCH= -gencode arch=compute_53,code=sm_53/' Makefile
sed -i 's/ARCH+= -gencode arch=compute_50,code=sm_50/ARCH+= -gencode arch=compute_53,code=sm_53/' Makefile
sed -i 's/ARCH+= -gencode arch=compute_52,code=sm_52/ARCH+= -gencode arch=compute_53,code=sm_53/' Makefile
# 移除其他不匹配的算力行(若存在)
sed -i '/compute_6[01]/d' Makefile
sed -i '/compute_70/d' Makefile
sed -i '/compute_75/d' Makefile

# 编译(使用4线程加速)
make -j4

2. 下载YOLOv4-tiny权重与配置文件

# 权重文件(约23MB)
wget https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v4_pre/yolov4-tiny.weights

# 配置文件(若仓库内已有则跳过)
wget https://raw.githubusercontent.com/AlexeyAB/darknet/master/cfg/yolov4-tiny.cfg

# 类别名称文件(COCO 80类)
wget https://raw.githubusercontent.com/pjreddie/darknet/master/data/coco.names

3. 摄像头实时检测程序

Darknet提供的darknet_video.py可直接调用编译好的libdarknet.so。以下为最小可运行版本,使用Python 3绑定摄像头并显示FPS:

# detector_usb.py
import cv2
import time
import darknet

# 加载模型
net = darknet.load_net(b"yolov4-tiny.cfg", b"yolov4-tiny.weights", 0)
meta = darknet.load_meta(b"coco.names")

# 打开USB摄像头(通常为0号设备)
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

if not cap.isOpened():
    raise IOError("无法打开摄像头,请检查设备编号")

# 预热
ret, frame = cap.read()
if not ret:
    raise IOError("无法读取视频帧")

frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
darknet_image = darknet.make_image(640, 480, 3)
darknet.copy_image_from_bytes(darknet_image, frame_rgb.tobytes())

fps_display = "FPS: 0.0"
prev_time = time.time()

while True:
    ret, frame = cap.read()
    if not ret:
        break

    # 转换为Darknet输入格式
    frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    darknet.copy_image_from_bytes(darknet_image, frame_rgb.tobytes())

    # 推理(阈值0.25)
    detections = darknet.detect_image(net, meta, darknet_image, thresh=0.25)

    # 计算FPS
    curr_time = time.time()
    fps = 1.0 / (curr_time - prev_time)
    prev_time = curr_time
    fps_display = f"FPS: {fps:.1f}"

    # 绘制检测框
    for label, confidence, bbox in detections:
        x, y, w, h = bbox
        x1, y1 = int(x - w/2), int(y - h/2)
        x2, y2 = int(x + w/2), int(y + h/2)
        cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
        text = f"{label.decode()} {confidence:.2f}"
        cv2.putText(frame, text, (x1, y1 - 5),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)

    # 显示FPS
    cv2.putText(frame, fps_display, (10, 25),
                cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

    cv2.imshow("YOLOv4-tiny USB Camera", frame)

    # 按q退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
darknet.free_image(darknet_image)

4. 运行与验证

# 确保libdarknet.so在库路径中
export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH

# 运行检测程序
python3 detector_usb.py

验证通过条件:

  • 程序启动后弹出实时视频窗口,画面来自USB摄像头;
  • 画面中可见的常见物体(人、杯子、手机等)被绿色框标出,并显示类别与置信度;
  • 窗口左上角持续显示红色FPS数值,在Jetson Nano上典型值为8~15 FPS(640×480输入);
  • 按q键可正常退出,终端无报错。

常见问题

  • 编译报错nvcc not found:确认已安装JetPack的CUDA组件,执行sudo apt install nvidia-cuda-toolkit(仅当CUDA未随JetPack安装时)。
  • 摄像头无法打开:执行ls /dev/video*确认设备节点;若为/dev/video1,将代码中VideoCapture(0)改为VideoCapture(1)。
  • 检测FPS过低(<5):将输入分辨率降至416×416(修改cap.set与make_image参数),或关闭桌面环境以释放GPU内存。
  • 权重加载失败:确认yolov4-tiny.weights与yolov4-tiny.cfg位于当前目录,且文件名拼写一致。

预期输出示例(非实测值):当摄像头对准一个行人时,窗口显示绿色框标注"person 0.85",左上角显示"FPS: 10.2"。实际FPS取决于摄像头分辨率、场景复杂度与Jetson Nano的功耗模式(建议运行sudo nvpmodel -m 0开启最大性能模式)。