ELF-RV1126B YOLOv8官方例程目标检测部署验证

电子说

1.4w人已加入

描述

基于官方资料包例程完成图片或摄像头目标检测、结果显示和 FPS 记录。

1. 实验目的

• 验证 YOLOv8 官方例程能在 ELF-RV1126B 上正确启动并调用 NPU 推理。

• 验证摄像头节点、模型路径和例程关键参数配置是否正确。

• 记录目标检测效果和平均 FPS,形成可归档的 AI 部署验收结果。

2. 前置条件与目录要求

1)板端准备以下内容:main_camera_fps_v8.py、rknnpool 目录、func 目录、rknnModel/best.rknn。(注意所有文件夹创建在/dev/mmcblk0p8分区的/userdata下,默认安装路径是在/dev/root分区的home/elf下,会导致/dev/root的空间满了

AI

在大分区创建缓存目录

mkdir -p /userdata/.cache/pip

创建软链接,让系统以为它还在原位,其实存在了 49G 的分区里

ln -s /userdata/.cache/pip ~/.cache/pip

2)若使用摄像头,请先确认摄像头已经被系统识别:这里使用的是HIKVISION的USB摄像头

无摄像头时的处理:如果暂时没有接摄像头,可以先把官方例程改为读取静态图片或视频文件,先验证“模型加载 + 前后处理 + 推理输出”链路;但正式验收时,仍建议补做摄像头路径测试。

步骤 2.1  激活 Python 环境并安装 OpenCV(接上一个实验)

cd /userdata/rknn_test
python3 -m venv venv
source venv/bin/activate
pip install rknn-toolkit-lite2==2.3.2 -i https://pypi.tuna.tsinghua.edu.cn/simple                                                                                                                                                                                pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple
AI

步骤 2.2  检查当前摄像头节点

v4l2-ctl --list-devices
v4l2-ctl --list-formats-ext -d /dev/video52
AI
AI

说明:请把 /dev/videoXX 替换为你的实际摄像头节点,这是video52。若是 UVC 摄像头,节点往往位于较靠后的 /dev/video 号;若是 OV13855,则可参考官方手册中的摄像头测试章节确认节点(这里使用的是HIKVISION的USB摄像头,1080p USB)。

3. 关键目录与参数检查

检查项 建议内容 备注
主程序 main_camera_fps_v8.py 建议直接使用官方资料包版本
模型目录 ./rknnModel/best.rknn 模型路径要与脚本一致
依赖目录 rknnpool / func 保持与主程序在同一工程目录下
摄像头节点 /dev/videoXX 必须按实际节点修改
线程 TPEs = 1~2 初测,稳定后再提高 先求稳定,再追求 FPS

步骤 3.1  官方例程中的关键参数示例(按需修改,这里用X11转发至Mobaxterm进行显示)

import os
# 抑制 Qt 字体警告
os.environ["QT_LOGGING_RULES"] = "qt.qpa.fonts.warning=false"
import cv2
import time
from rknnpool.rknnpool_ld import rknnPoolExecutor
from func.func_yolov8_optimize import myFunc
# --------------- 性能采集工具函数 ---------------
def get_npu_load():
    """读取 RV1126B NPU 负载百分比,返回字符串如 '45%' 或 'N/A'"""
    paths = [
         "/sys/kernel/debug/rknpu/load",
         "/sys/class/devfreq/ffbc0000.npu/load",
    ]
    for p in paths:
        try:
            with open(p, "r") as f:
                text = f.read().strip()
            # 取第一行
            first_line = text.split("n")[0].strip()
            # 格式可能是 "NPU load:   4%" 或 "45%" 或 "45 / 100"
            if "%" in first_line:
                # 提取百分比数字,如 "NPU load:   4%" -> "4%"
                import re
                m = re.search(r'(d+)s*%', first_line)
                if m:
                    return f"{m.group(1)}%"
                return first_line
            parts = first_line.replace("/", " ").split()
            if len(parts) >= 2:
                return f"{int(int(parts[0]) * 100 / int(parts[1]))}%"
            return first_line
        except (FileNotFoundError, PermissionError, ValueError):
            continue
    return "N/A"
def get_cpu_usage(prev_stat):
    """
    通过 /proc/stat 两次采样差值计算 CPU 使用率。
    prev_stat: 上次采样的 (idle, total) 元组,首次传 None。
    返回 (cpu_percent_str, current_stat)
    """
    try:
        with open("/proc/stat", "r") as f:
            line = f.readline()  # 第一行 "cpu  ..."
        parts = [int(x) for x in line.split()[1:]]
        idle = parts[3]
        total = sum(parts)
        if prev_stat is None:
            return ("--", (idle, total))
        d_idle = idle - prev_stat[0]
        d_total = total - prev_stat[1]
        if d_total == 0:
            return ("0.0%", (idle, total))
        usage = (1.0 - d_idle / d_total) * 100
        return (f"{usage:.1f}%", (idle, total))
    except (FileNotFoundError, PermissionError, ValueError):
        return ("N/A", prev_stat)
def draw_osd(frame, fps_str, cpu_str, npu_str):
    """在帧左上角绘制半透明黑底 + 性能信息"""
    lines = [
        f"FPS : {fps_str}",
        f"CPU : {cpu_str}",
        f"NPU : {npu_str}",
    ]
    font = cv2.FONT_HERSHEY_SIMPLEX
    scale, thickness = 0.7, 2
    y0, dy = 25, 30
    # 半透明背景
    overlay = frame.copy()
    cv2.rectangle(overlay, (5, 5), (220, y0 + dy * len(lines) - 10), (0, 0, 0), -1)
    cv2.addWeighted(overlay, 0.5, frame, 0.5, 0, frame)
    for i, text in enumerate(lines):
        cv2.putText(frame, text, (10, y0 + i * dy), font, scale, (0, 255, 0), thickness)
    return frame
# --------------- X11 显示检测 ---------------
# 修复 sudo 下 X11 授权丢失问题:
# sudo 会切换到 root,但 root 没有原用户的 .Xauthority 令牌
sudo_user = os.environ.get("SUDO_USER")
if sudo_user:
    # 从原用户的 home 目录继承 .Xauthority
    sudo_home = os.path.expanduser(f"~{sudo_user}")
    xauth_file = os.path.join(sudo_home, ".Xauthority")
    if os.path.exists(xauth_file) and "XAUTHORITY" not in os.environ:
        os.environ["XAUTHORITY"] = xauth_file
        print(f"已设置 XAUTHORITY={xauth_file} (来自用户 {sudo_user})")
if "DISPLAY" not in os.environ:
    print("警告: 未检测到 DISPLAY 环境变量")
    print("请确保 MobaXterm 已开启 X11 转发 (ssh -X / -Y)")
    print("尝试设置 DISPLAY=localhost:10.0 继续...")
    os.environ["DISPLAY"] = "localhost:10.0"
# --------------- 初始化 ---------------
cap = cv2.VideoCapture("/dev/video52")
modelPath = "./rknnModel/best.rknn"
TPEs = 8
os.makedirs("output", exist_ok=True)
if not cap.isOpened():
    print("错误: 无法打开摄像头 /dev/video52")
    os.system("ls -l /dev/video*")
    exit(-1)
print(f"摄像头已打开, 分辨率: {int(cap.get(3))}x{int(cap.get(4))}")
pool = rknnPoolExecutor(
    rknnModel=modelPath,
    TPEs=TPEs,
    func=myFunc
)
# 预填充推理流水线
if cap.isOpened():
    for i in range(TPEs + 1):
        ret, frame = cap.read()
        if not ret:
            print(f"错误: 第 {i} 帧读取失败")
            cap.release()
            pool.release()
            exit(-1)
        pool.put(frame)
# --------------- X11 窗口 (带降级) ---------------
WINDOW_NAME = "YOLOv8 - RV1126B"
use_x11 = False
try:
    cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL)
    cv2.resizeWindow(WINDOW_NAME, 640, 480)
    use_x11 = True
    print("X11 显示窗口已创建")
except cv2.error as e:
    print(f"警告: X11 窗口创建失败 ({e})")
    print("将以无头模式运行 (仅保存图片到 output/)")
    print("提示: 请尝试以下命令解决 X11 问题:")
    print(f"  1. 用普通用户运行: python3 {os.path.basename(__file__)}")
    print(f"     (先执行: sudo chmod 666 /dev/video52)")
    print(f"  2. 或传递 X11 授权: sudo -E XAUTHORITY=$HOME/.Xauthority ./venv/bin/python3 {os.path.basename(__file__)}")
    print(f"  3. 或允许 root 访问 X11: xhost +local:root")
# --------------- 推理主循环 ---------------
# X11 转发带宽有限,跳帧显示以避免拖慢推理帧率
# 每 X11_SKIP 帧才做一次 imshow,其余帧只推理不显示
X11_SKIP = 10
print("开始推理循环, 按 'q' 或 Ctrl+C 停止...")
frames, loopTime, initTime = 0, time.time(), time.time()
fps_display = "..."
# 预采样 CPU 一次,使首次输出不是 "--"
cpu_display, cpu_stat = get_cpu_usage(None)
npu_display = get_npu_load()
last_good_frame = None
try:
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            print(f"第 {frames + 1} 帧读取失败, 退出")
            break
        pool.put(frame)
        frame, flag = pool.get()
        if not flag:
            print(f"第 {frames + 1} 帧推理失败, 退出")
            break
        frames += 1
        last_good_frame = frame
        # 每30帧更新性能数据
        if frames % 30 == 0:
            elapsed = time.time() - loopTime
            fps_val = 30 / elapsed if elapsed > 0 else 0
            fps_display = f"{fps_val:.1f}"
            npu_display = get_npu_load()
            cpu_display, cpu_stat = get_cpu_usage(cpu_stat)
            loopTime = time.time()
            # 保存图片
            save_path = f"output/result_{frames}.jpg"
            cv2.imwrite(save_path, frame)
            print(f"FPS: {fps_display}  CPU: {cpu_display}  NPU: {npu_display}  |  已保存: {save_path}")
        # OSD 叠加
        draw_osd(frame, fps_display, cpu_display, npu_display)
        # 通过 X11 显示 (跳帧以减少 X11 转发开销)
        if use_x11 and frames % X11_SKIP == 0:
            cv2.imshow(WINDOW_NAME, frame)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                print("按 'q' 退出")
                break
except KeyboardInterrupt:
    print("n手动停止")
finally:
    if last_good_frame is not None:
         cv2.imwrite("output/result_latest.jpg", last_good_frame)
        print(f"最后一帧已保存: output/result_latest.jpg")
    if frames > 0:
        total_time = time.time() - initTime
        print(f"总帧数: {frames}  总平均帧率: {frames / total_time:.2f}")
    else:
        print("未处理任何帧")
    cap.release()
    pool.release()
    if use_x11:
        cv2.destroyAllWindows()

步骤 3.2  运行 YOLOv8 官方例程

sudo ./venv/bin/python3 main_camera_fps_v8.py
可以设置跳帧X11_SKIP = 15(X11转发是瓶颈)
AI

4. 预期结果与判定标准

检查项 成功标志 判定
程序启动 无 import 错误、无模型路径错误 通过 / 不通过
摄像头打开 可正常读取画面或至少确认节点可用 通过 / 不通过
目标检测 画面中能看到检测框 / 类别结果 通过 / 不通过
性能记录 能够记录平均 FPS 或等效性能指标 通过 / 不通过

5. 常见问题与排查

• 程序启动失败:先检查 main_camera_fps_v8.py、rknnpool、func 和 rknnModel 目录是否完整。

• 画面不出图:优先检查 /dev/videoXX 是否写对,摄像头是否被 v4l2-ctl 正确识别。

• SSH 下无窗口:这通常是显示环境问题,不一定是模型推理失败;建议接本地显示器,或改为保存图片/日志方式验证。

• FPS 偏低:先把 TPEs 调到 1~2 保证稳定,再逐步提升;同时避免在高分辨率显示输出下直接判断模型性能。

• 检测结果为空:检查 best.rknn 是否与当前前处理 / 后处理函数匹配,避免脚本模型版本不对应。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分