yolov8双线程代码报错

Viewed 157

重现步骤

1.自己按https://www.kendryte.com/answer/questions/10010000000005867的提示自己写了一个yolov8训练模型双线程的代码
2.按下按钮屏幕部分花屏一下,但是寻迹的代码有打印输出,但是LCD没有寻迹的代码的显示
3.上电报错AttributeError: 'Image' object has no attribute 'create
主要是这里(完整代码太多所以发不出来)

# ---------- AI 线程 ----------
def ai_thread():
    """AI目标检测线程"""
    global sensor, det_osd_img, det_stop
    
    kmodel_path = "/sdcard/best.kmodel"
    labels = ["1", "2", "3", "4", "5", "6", "7", "8"]
    model_input_size = [224, 224]
    display_mode = "lcd"
    confidence_threshold = 0.7
    nms_threshold = 0.2
    
    

     
    # 初始化显示与模型
    pl = sensor.snapshot(CAM_CHN_ID_2) 
    pl.create()
    display_size = pl.get_display_size()
    
    yolo = YOLOv8(
        task_type="detect",
        mode="video",
        kmodel_path=kmodel_path,
        labels=labels,
        rgb888p_size=rgb888p_size,
        model_input_size=model_input_size,
        display_size=display_size,
        conf_thresh=confidence_threshold,
        nms_thresh=nms_threshold,
        max_boxes_num=4,
        debug_mode=0
    )
    yolo.config_preprocess()
    
    try:
        while not det_stop:
            # 获取一帧RGBP888的数据
            img_rgbp888 = sensor.snapshot(CAM_CHN_ID_2)
            # 转成ulab.numpy格式
            img = img_rgbp888.to_numpy_ref()
            
            # 执行目标检测
            res = yolo.run(img)
            ```
4 Answers

pl = sensor.snapshot(CAM_CHN_ID_2)
pl.create()
不要这么用,直接snapshot得到的就是image

pl = sensor.snapshot(CAM_CHN_ID_2)
pl.create()
大佬,我可以这样子理解,是把上面这一段直接改成sensor.snapshot(CAM_CHN_ID_2)吗?
还是这两句直接删掉即可。

https://www.kendryte.com/answer/questions/10010000000005867,我再这个贴子里已经写清楚了,你直接把snapshot的内容调用to_numpy_ref转成numpy数据然后给run就完了,不要再用pl了

好的,谢谢大佬,现在两份代码都能跑了。但是lcd的显示仍然有一些问题,稍后发实物图片与已经修改的代码,希望大佬能指点

这是yolo代码与main

# ---------- AI 线程 ----------
def ai_thread():
    """AI目标检测线程"""
    global sensor, det_osd_img, det_stop
    
    kmodel_path = "/sdcard/best.kmodel"
    labels = ["1", "2", "3", "4", "5", "6", "7", "8"]
    model_input_size = [224, 224]
    display_mode = "lcd"
    confidence_threshold = 0.7
    nms_threshold = 0.2
    
    

     
    # 初始化显示与模型
    pl = sensor.snapshot(CAM_CHN_ID_2) 
    pl.create()
    display_size = pl.get_display_size()
    
    yolo = YOLOv8(
        task_type="detect",
        mode="video",
        kmodel_path=kmodel_path,
        labels=labels,
        rgb888p_size=rgb888p_size,
        model_input_size=model_input_size,
        display_size=display_size,
        conf_thresh=confidence_threshold,
        nms_thresh=nms_threshold,
        max_boxes_num=4,
        debug_mode=0
    )
    yolo.config_preprocess()
    
    try:
        while not det_stop:
            # 获取一帧RGBP888的数据
            img_rgbp888 = sensor.snapshot(CAM_CHN_ID_2)
            # 转成ulab.numpy格式
            img = img_rgbp888.to_numpy_ref()
            
            # 执行目标检测
            res = yolo.run(img)
            
            # 处理检测结果
            for detection in res:
                x1, y1, x2, y2 = detection[0], detection[1], detection[2], detection[3]
                conf = detection[4]
                class_id = int(detection[5])
                label = labels[class_id]

                # 计算中心点坐标
                center_x = int((x1 + x2) / 2)
                center_y = int((y1 + y2) / 2)

                # 标签编码
                Dat_intersection = 0x00
                if label == "1": 
                    Dat_intersection = 0x01
                elif label == "2": 
                    Dat_intersection = 0x02
                elif label == "3": 
                    Dat_intersection = 0x03
                elif label == "4": 
                    Dat_intersection = 0x04
                elif label == "5": 
                    Dat_intersection = 0x05
                elif label == "6": 
                    Dat_intersection = 0x06
                elif label == "7": 
                    Dat_intersection = 0x07
                elif label == "8": 
                    Dat_intersection = 0x08

                # 判断左右位置
                midpoint = rgb888p_size[0] // 2  # 计算图像中心线
                position_byte = 0x01  # 默认左侧标识 (0x01)
                if center_x > midpoint:  # 当中心点在右侧区域
                    position_byte = 0x02  # 右侧标识 (0x02)

                # 构建数据帧
                FH = bytearray([
                    0xA3, 0xB3,             # 帧头
                    position_byte,           # 位置标识 (0x01左, 0x02右)
                    Dat_intersection,        # 数字标签
                    0xC3                     # 帧尾
                ])
                print(FH)
                uart.write(FH)

            # 显示结果
            yolo.draw_result(res, pl.osd_img)
            pl.show_image()
            gc.collect()
            
    except Exception as e:
        print("AI线程异常:", e)
    finally:
        # 释放资源
        yolo.deinit()
        pl.destroy()

if __name__ == "__main__":
    # 初始化硬件
    media_init()
    
    # ---------- 启动线程 ----------
    _thread.start_new_thread(redline_thread, ())  # 红线识别线程
    _thread.start_new_thread(ai_thread, ())       # AI检测线程
    
    try:
        # 主循环 (保持程序运行)
        while True:
            time.sleep_ms(50)
    except KeyboardInterrupt:
        print("程序被中断")
    except Exception as e:
        print("主程序异常:", e)
    finally:
        # 设置停止标志
        redline_stop = True
        det_stop = True
        time.sleep(1)  # 等待线程退出
        
        # 释放资源
        media_deinit()
        gc.collect()  # 垃圾回收
        print("程序已退出")

这是全局变量与初始的代码(因为内容太多所以分开发送)

from libs.PipeLine import PipeLine
from libs.YOLO import YOLOv8
from libs.Utils import *
from machine import UART, FPIOA
import os, sys, gc
import ulab.numpy as np
import image
from media.sensor import *        # 摄像头传感器模块
from media.display import *       # 显示屏模块
from media.media import *         # 多媒体框架核心模块
from machine import UART, FPIOA, Pin  # 硬件接口:串口、引脚映射、GPIO
import time, gc, _thread, ujson  # 系统库:时间、垃圾回收、线程、JSON

# ---------- 全局配置 ----------
sensor = None          # 摄像头传感器对象
display_size = [800, 480]  # 主显示屏分辨率
redline_img_size = [800, 480]  # 红线识别使用的图像尺寸
rgb888p_size = [1280, 720]   # yolov8检测使用的高分辨率图像尺寸
det_stop = False       # yolov8线程停止标志
redline_stop = False   # 红线线程停止标志
det_osd_img = None     # yolov8检测结果的屏幕叠加层
redline_osd_img = None  # 红线识别结果的屏幕叠加层

# 颜色阈值和巡线状态
threshold = None
line_following_enabled = False
button_last_state = 0
last_press_time = 0

# ---------- 串口 & 按键 ----------
# 串口配置
fpioa = FPIOA()
# 配置引脚11为UART2_TXD,引脚12为UART2_RXD
fpioa.set_function(11, FPIOA.UART2_TXD)
fpioa.set_function(12, FPIOA.UART2_RXD)
# 初始化UART2,波特率115200,8数据位,无校验,1停止位
uart = UART(UART.UART2, 115200, 8, 0, 1, timeout=1000)

# ========== 初始化按键:按下时高电平 ==========
fpioa.set_function(53, FPIOA.GPIO53)
key = Pin(53, Pin.IN, Pin.PULL_DOWN)
debounce_delay = 100  # 按键消抖时长(ms)

# ---------- 红线识别与学习的参数 ----------
# 学习颜色阈值时的感兴趣区域,格式为 (x, y, width, height)
LEARNING_ROI = (0, 231, 800, 50)
# 学习颜色阈值时收集的帧数
LEARNING_FRAME_COUNT = 30

# 巡线检测时将图像水平分割的区域数量
LINE_SECTION_COUNT = 8
# 巡线检测起始行的比例
START_Y_RATIO = 0 / 8
# 巡线检测结束行的比例
END_Y_RATIO = 2 / 8
# 检测色块时的像素数量阈值
BLOB_PIXELS_THRESHOLD = 1006
# 检测色块时的面积阈值
BLOB_AREA_THRESHOLD = 100

# ---------- 状态变量 ----------
move_record = []  # 路径动作记录 (用于自动回程)
return_mode = False  # 回程模式标志
current_index = 0   # 当前回程动作索引

def media_init():
    """初始化多媒体系统(摄像头和显示屏)"""
    global sensor, det_osd_img, redline_osd_img, threshold, line_following_enabled, button_last_state, last_press_time

    # 初始化显示屏 (ST7701驱动芯片, 800x480分辨率)
    Display.init(Display.ST7701, width=display_size[0], height=display_size[1],
                to_ide=True, osd_num=3)  # osd_num=3表示支持3个叠加层

    # 初始化摄像头传感器
    sensor = Sensor(fps=30)  # 30帧/秒
    sensor.reset()

    # 配置通道0:YUV420SP格式,800x480 (主显示)
    sensor.set_framesize(w=display_size[0], h=display_size[1], chn=CAM_CHN_ID_0)
    sensor.set_pixformat(Sensor.YUV420SP, chn=CAM_CHN_ID_0)

    # 配置通道1:RGB565格式,800x480 (红线识别)
    sensor.set_framesize(w=redline_img_size[0], h=redline_img_size[1], chn=CAM_CHN_ID_1)
    sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_1)

    # 配置通道2:RGBP888格式,1280x720 (AI检测)
    sensor.set_framesize(w=rgb888p_size[0], h=rgb888p_size[1], chn=CAM_CHN_ID_2)
    sensor.set_pixformat(Sensor.RGBP888, chn=CAM_CHN_ID_2)

    # 绑定摄像头通道到显示层
    sensor_bind_info = sensor.bind_info(x=0, y=0, chn=CAM_CHN_ID_0)
    Display.bind_layer(**sensor_bind_info, layer=Display.LAYER_VIDEO1)

    # 初始化OSD叠加层 (用于绘制检测结果)
    Display.init(Display.ST7701, osd_num=2, to_ide=True)
    det_osd_img = image.Image(display_size[0], display_size[1], image.ARGB8888)  # AI检测OSD
    redline_osd_img = image.Image(display_size[0], display_size[1], image.ARGB8888)  # 红线OSD

    # 初始化媒体管理器并启动摄像头
    MediaManager.init()
    sensor.run()
    
    # 初始化颜色阈值和巡线状态
    threshold = None
    line_following_enabled = False
    button_last_state = key.value()  # 初始化为当前按键状态
    last_press_time = 0

def redline_thread():
    """红线识别主线程"""
    global sensor, redline_osd_img, threshold, line_following_enabled, button_last_state, last_press_time
    
    while not redline_stop:
        # 从摄像头通道1获取RGB565图像
        img = sensor.snapshot(chn=CAM_CHN_ID_1)
        
        # 检测按键状态
        button_state = key.value()
        current_time = time.ticks_ms()
        
        # 检测按键状态变化(上升沿)
        if button_state == 1 and button_last_state == 0:
            # 检查按键是否在消抖时间外
            if current_time - last_press_time > debounce_delay:
                # 学习颜色阈值
                threshold = learn_color_threshold(sensor)
                # 启用巡线功能
                line_following_enabled = True
                last_press_time = current_time
        
        # 更新按键状态
        button_last_state = button_state
        
        # 如果已经学习到颜色阈值并且巡线功能已启用
        if threshold and line_following_enabled:
            # 进行巡线检测
            width = img.width()
            height = img.height()
            
            # 计算巡线检测的起始行和结束行
            start_y = int(height * START_Y_RATIO)
            end_y = int(height * END_Y_RATIO)
            # 计算每个分割区域的宽度
            section_width = width // LINE_SECTION_COUNT
            
            # 初始化巡线检测结果列表
            detection_result = ['0'] * LINE_SECTION_COUNT
            
            # 遍历每个分割区域
            for i in range(LINE_SECTION_COUNT):
                # 定义当前区域的感兴趣区域
                roi = (i * section_width, start_y, section_width, end_y - start_y)
                # 在图像上绘制当前区域的矩形框
                img.draw_rectangle(roi, color=(0, 255, 0), thickness=2)
                
                # 在当前区域内查找符合颜色阈值的色块
                blobs = img.find_blobs([threshold], roi=roi, pixels_threshold=BLOB_PIXELS_THRESHOLD,
                                       area_threshold=BLOB_AREA_THRESHOLD, merge=True)
                
                # 如果找到色块
                if blobs:
                    # 找到最大的色块
                    largest_blob = max(blobs, key=lambda b: b.pixels())
                    # 在最大色块的中心绘制一个圆圈
                    img.draw_circle(largest_blob.cx(), largest_blob.cy(), 10, color=(0, 255, 0), thickness=2)
                    # 更新检测结果列表
                    detection_result[i] = '1'
            
            # 判断是否需要左转
            left_turn = ('1' in detection_result[3:4]) and (detection_result[0] == '1' or detection_result[1] == '1')
            # 判断是否需要右转
            right_turn = ('1' in detection_result[3:4]) and (detection_result[6] == '1' or detection_result[7] == '1')
            
            # 初始化路口类型
            intersection_type = None
            # 判断是否为T字路口
            if left_turn and right_turn:
                intersection_type = "T字路口"
            # 判断是否为左转路口
            elif left_turn:
                intersection_type = "左转路口"
            # 判断是否为右转路口
            elif right_turn:
                intersection_type = "右转路口"
            
            # 打印状态信息
            print_status(img, detection_result, intersection_type)
        else:
            # 在图像上绘制学习区域的矩形框
            img.draw_rectangle(LEARNING_ROI, color=(255, 0, 0), thickness=2)
            # 显示提示信息
            img.draw_string(10, 10, "请按下按键学习颜色阈值", color=(255, 0, 0), scale=2)
        
        # 显示图像到OSD层
        redline_osd_img.draw_image(img, 0, 0)
        Display.show_image(redline_osd_img)
        
        time.sleep_ms(20)  # 控制循环频率
# ---------- AI 线程 ----------
def ai_thread():
    """AI目标检测线程"""
    global sensor, det_osd_img, det_stop
    kmodel_path = "/sdcard/best.kmodel"
    labels = ["1", "2", "3", "4", "5", "6", "7", "8"]
    model_input_size = [224, 224]
    display_mode = "lcd"
    confidence_threshold = 0.7
    nms_threshold = 0.2
    # 初始化显示与模型
    yolo = YOLOv8(
        task_type="detect",
        mode="video",
        kmodel_path=kmodel_path,
        labels=labels,
        rgb888p_size=rgb888p_size,
        model_input_size=model_input_size,
        display_size=display_size,
        conf_thresh=confidence_threshold,
        nms_thresh=nms_threshold,
        max_boxes_num=4,
        debug_mode=0
    )
    yolo.config_preprocess()
    
    try:
        while not det_stop:
            # 获取一帧RGBP888的数据
            img_rgbp888 = sensor.snapshot(CAM_CHN_ID_2)
            # 转成ulab.numpy格式
            img = img_rgbp888.to_numpy_ref()
            
            # 执行目标检测
            res = yolo.run(img)
            
            # 处理检测结果
            for detection in res:
                x1, y1, x2, y2 = detection[0], detection[1], detection[2], detection[3]
                conf = detection[4]
                class_id = int(detection[5])
                label = labels[class_id]

                # 计算中心点坐标
                center_x = int((x1 + x2) / 2)
                center_y = int((y1 + y2) / 2)

                # 标签编码
                Dat_intersection = 0x00
                if label == "1": 
                    Dat_intersection = 0x01
                elif label == "2": 
                    Dat_intersection = 0x02
                elif label == "3": 
                    Dat_intersection = 0x03
                elif label == "4": 
                    Dat_intersection = 0x04
                elif label == "5": 
                    Dat_intersection = 0x05
                elif label == "6": 
                    Dat_intersection = 0x06
                elif label == "7": 
                    Dat_intersection = 0x07
                elif label == "8": 
                    Dat_intersection = 0x08

                # 判断左右位置
                midpoint = rgb888p_size[0] // 2  # 计算图像中心线
                position_byte = 0x01  # 默认左侧标识 (0x01)
                if center_x > midpoint:  # 当中心点在右侧区域
                    position_byte = 0x02  # 右侧标识 (0x02)

                # 构建数据帧
                FH = bytearray([
                    0xA3, 0xB3,             # 帧头
                    position_byte,           # 位置标识 (0x01左, 0x02右)
                    Dat_intersection,        # 数字标签
                    0xC3                     # 帧尾
                ])
                print(FH)
                uart.write(FH)

            # 显示结果
            yolo.draw_result(res, det_osd_img)
            gc.collect()
            
    except Exception as e:
        print("AI线程异常:", e)
    finally:
        # 释放资源
        yolo.deinit()