【RK3568】Buildroot 环境下火焰检测项目实战(无网络 / 无图形界面适配)

一、项目背景

基于 RK3568 开发板(Buildroot 极简系统)实现火焰检测,核心需求:

  1. 基于 YOLOv8 ONNX 模型完成火焰实时检测;
  2. 无网络环境下运行,避免在线安装依赖;
  3. 无 X11/GTK 图形库支持,解决 OpenCV imshow 报错问题;
  4. 无需 U 盘 / SD 卡 / 文件传输,直接在终端输出检测核心信息。

二、环境准备

1. 硬件环境

  • 开发板:ATK-DLRK3568(Buildroot 系统)
  • 外设:USB 摄像头(CAMERA_ID=9)
  • 调试工具:MobaXterm 12.3(串口连接 + 共享目录)

2. 软件环境

  • Python3 + OpenCV(无 highgui 图形库)
  • ONNX Runtime(CPU 推理)
  • YOLOv8 火焰检测 ONNX 模型(best.onnx)

三、核心问题与解决方案

问题 1:串口传文件乱码(sz/rz 命令异常)

现象

执行sz /root/fire_detect.jpg后终端出现▒**B00000000000000乱码,无法触发 MobaXterm 文件接收弹窗。

解决方案

放弃 ZModem 传输,改用 MobaXterm 共享目录:MobaXterm 串口连接 RK3568 时,自动将电脑端C:\Users\用户名\MobaXterm\home映射到开发板/home目录,开发板写入该目录的文件可在电脑端直接查看。

问题 2:OpenCV imshow 函数报错

现象
cv2.error: OpenCV(4.5.5) window.cpp:1268: error: (-2:Unspecified error) The function is not implemented.
原因

Buildroot 系统中 OpenCV 编译时未开启 X11/GTK 图形界面支持,cv2.imshow()功能被阉割。

解决方案

移除所有图形界面相关代码,仅保留终端信息输出(无网络 / 无依赖最优解)。

问题 3:无网络环境下依赖安装

解决方案
  • 核心原则:避免在线安装,使用本地模型 + 纯原生 Python/OpenCV 核心功能;

四、完整实现代码

import onnxruntime as ort
import numpy as np
import cv2
import time

# 配置参数
MODEL_PATH = "/home/best.onnx"  # 离线传输的ONNX模型路径
CAMERA_ID = 9                   # 摄像头ID
CONF_THRESHOLD = 0.3            # 置信度阈值
NMS_THRESHOLD = 0.5             # NMS非极大值抑制阈值
CLASS_NAMES = ["fire"]          # 检测类别
CAM_W = 640                     # 摄像头预设分辨率
CAM_H = 480

# 加载ONNX模型(纯本地运算)
try:
    session = ort.InferenceSession(MODEL_PATH, providers=['CPUExecutionProvider'])
    input_name = session.get_inputs()[0].name
    input_shape = session.get_inputs()[0].shape
    input_h, input_w = input_shape[2], input_shape[3]
    output_names = [out.name for out in session.get_outputs()]
    print("="*50)
    print("✅ 模型加载成功!")
    print(f"📌 模型输入分辨率:{input_w}×{input_h}")
    print("="*50)
except Exception as e:
    print(f"❌ 模型加载失败:{e}")
    exit(1)

# 图像预处理(仅格式转换,无画面操作)
def preprocess(image):
    img = cv2.resize(image, (input_w, input_h))
    # 格式转换:BGR→RGB(适配YOLOv8)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) if len(image.shape)==3 else cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
    img = img.transpose(2, 0, 1).astype(np.float32) / 255.0  # 归一化+维度转换
    return np.expand_dims(img, axis=0)

# 后处理解析检测结果(仅计算坐标,无绘制)
def postprocess_yolov8(outputs, img_shape):
    h_img, w_img = img_shape[:2]  # 实际画面宽高
    pred = outputs[0].transpose(0, 2, 1)[0]
    boxes = []
    for det in pred:
        xc, yc, w, h, conf, cls = det[:6]
        if conf < CONF_THRESHOLD:
            continue
        # 解归一化,转换为像素坐标
        x1 = int((xc - w/2) * w_img)
        y1 = int((yc - h/2) * h_img)
        x2 = int((xc + w/2) * w_img)
        y2 = int((yc + h/2) * h_img)
        # 坐标边界裁剪,避免超出画面
        x1 = max(0, min(x1, w_img-1))
        y1 = max(0, min(y1, h_img-1))
        x2 = max(0, min(x2, w_img-1))
        y2 = max(0, min(y2, h_img-1))
        if x2 > x1 and y2 > y1:
            boxes.append([x1, y1, x2, y2, int(cls), round(float(conf), 3)])
    # NMS非极大值抑制去重
    if not boxes:
        return []
    boxes = np.array(boxes)
    try:
        keep = cv2.dnn.NMSBoxes(boxes[:, :4].tolist(), boxes[:, 5].tolist(), CONF_THRESHOLD, NMS_THRESHOLD)
        keep = keep.flatten() if isinstance(keep, (np.ndarray, list)) else [keep]
        return boxes[keep].tolist()
    except:
        return boxes.tolist()

# 主检测函数:仅终端打印信息,无画面/文件操作
def detect_fire():
    # 打开摄像头
    cap = cv2.VideoCapture(CAMERA_ID)
    cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, CAM_W)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, CAM_H)
    
    # 摄像头有效性校验
    if not cap.isOpened():
        print(f"❌ 摄像头打开失败!请检查ID({CAMERA_ID})或硬件连接")
        return
    
    # 读取首帧获取实际分辨率
    ret, frame = cap.read()
    if not ret:
        print("❌ 无法读取摄像头画面!")
        cap.release()
        return
    actual_h, actual_w = frame.shape[:2]
    
    # 初始信息打印
    print("✅ 摄像头打开成功!")
    print(f"📸 实际画面分辨率:{actual_w}×{actual_h}")
    print(f"⚙️  检测阈值:置信度{CONF_THRESHOLD} | NMS{NMS_THRESHOLD}")
    print("="*50)
    print("🚀 火焰检测已启动!按Ctrl+C退出")
    print("="*50)
    
    prev_time = time.time()
    try:
        while True:
            ret, frame = cap.read()
            if not ret:
                print("\n❌ 画面读取中断!")
                break
            
            # 模型推理+结果解析
            img = preprocess(frame)
            outputs = session.run(output_names, {input_name: img})
            det_boxes = postprocess_yolov8(outputs, frame.shape)
            fire_detected = len(det_boxes) > 0
            
            # 计算实时FPS
            curr_time = time.time()
            fps = round(1 / (curr_time - prev_time), 1) if (curr_time - prev_time) > 0 else 0.0
            prev_time = curr_time
            
            # 终端实时打印核心信息(单行刷新)
            base_info = f"📊 FPS:{fps} | 画面:{actual_w}×{actual_h} | 火焰:{'✅ 存在' if fire_detected else '❌ 无'}"
            if fire_detected:
                base_info += f" | 检测框数:{len(det_boxes)} | 首个框:({int(det_boxes[0][0])},{int(det_boxes[0][1])})-({int(det_boxes[0][2])},{int(det_boxes[0][3])}) | 置信度:{det_boxes[0][5]}"
            print(f"\r{base_info}", end="", flush=True)
    
    # 捕获Ctrl+C退出信号
    except KeyboardInterrupt:
        print("\n" + "="*50)
        print("🛑 检测已手动退出!")
    finally:
        cap.release()
        print("✅ 摄像头资源已释放!")
        print("="*50)

if __name__ == "__main__":
    detect_fire()

五、操作步骤

1. 创建并运行代码

在 RK3568 串口终端执行:

# 删除旧文件(如有)
rm -f /root/fire.py
# 新建代码文件
vi /root/fire.py
# 按i进入编辑模式,粘贴上述代码,按Esc后输入:wq保存退出
# 运行检测程序
python3 /root/fire.py

2.终端输出说明

运行后3568终端实时打印以下核心信息(单行刷新,不刷屏):

==================================================
✅ 模型加载成功!
📌 模型输入分辨率:640×640
==================================================
✅ 摄像头打开成功!
📸 实际画面分辨率:640×480
⚙️  检测阈值:置信度0.3 | NMS0.5
==================================================
🚀 火焰检测已启动!按Ctrl+C退出
==================================================
📊 FPS:15.2 | 画面:640×480 | 火焰:✅ 存在 | 检测框数:1 | 首个框:(120,80)-(250,200) | 置信度:0.925

六、关键技巧

1. 确认 MobaXterm 共享目录

开发板端执行以下命令,验证共享目录映射:

# 查看共享目录挂载信息
mount | grep home
# 测试可写性(电脑端能看到test.txt则映射正常)
touch /home/test.txt

2. 串口终端常用操作

# 切换目录
cd /usr/bin
# 查看当前路径
pwd
# 查找文件
find / -name "fire.py" 2>/dev/null
# 重启网口(如需临时联网)
ifconfig eth0 down && ifconfig eth0 up

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐