2025电赛视觉——K230将不同功能组合(Base亚博智能)

2025电赛视觉——K230将不同功能组合(Base亚博智能)

K230将不同功能组合(Base亚博智能)

将颜色识别和形状识别组合(以黑色矩形检测为例)

23年E题要求识别一个电工胶带缠起来的矩形,首先要做到的就是矩形识别,为了减少其他矩形带来的干扰,就要加上颜色阈值的控制,这是写好的代码

# 导入必要的模块 Import required modules
import time, os, sys
from media.sensor import *
from media.display import *
from media.media import *

# 图像分辨率设置 Image resolution settings
PICTURE_WIDTH = 640
PICTURE_HEIGHT = 480
BLACK_THRESHOLD = (30, 60, -24, 5, -5, 14)
# 摄像头配置 Camera configuration
sensor = None

# 显示模式选择 Display mode selection
# 可选: "VIRT", "LCD"
# Options: "VIRT"(Virtual Display), "LCD"
DISPLAY_MODE = "LCD"

# 根据显示模式设置分辨率 Set resolution based on display mode
if DISPLAY_MODE == "VIRT":
    DISPLAY_WIDTH = ALIGN_UP(1920, 16)
    DISPLAY_HEIGHT = 1080
elif DISPLAY_MODE == "LCD":
    DISPLAY_WIDTH = 640
    DISPLAY_HEIGHT = 480
else:
    raise ValueError("Unknown DISPLAY_MODE, please select 'VIRT', 'LCD'")

# 创建时钟对象用于FPS计算 Create clock object for FPS calculation
clock = time.clock()

try:
    # 初始化摄像头 Initialize camera
    sensor = Sensor()
    sensor.reset()

    # 设置图像分辨率和格式 Set image resolution and format
    sensor.set_framesize(width=PICTURE_WIDTH, height=PICTURE_HEIGHT, chn=CAM_CHN_ID_0)
    sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)

    # 初始化显示器 Initialize display
    if DISPLAY_MODE == "VIRT":
        Display.init(Display.VIRT, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, fps=60)
    elif DISPLAY_MODE == "LCD":
        Display.init(Display.ST7701, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)

    # 初始化媒体管理器 Initialize media manager
    MediaManager.init()
    sensor.run()

    while True:
        os.exitpoint()
        clock.tick() # 开始计时 Start timing

        # 捕获图像 Capture image
        img = sensor.snapshot(chn=CAM_CHN_ID_0)

        for r in img.find_rects(threshold = 8000):
            stats = img.get_statistics(roi=r.rect())
            print(f"DEBUG: Found rect with l_mean = {stats.l_mean()}")
            if BLACK_THRESHOLD[0] < stats.l_mean() < BLACK_THRESHOLD[1]:
                img.draw_rectangle(r.rect(), color = (40, 167, 225),thickness=2) 
                for p in r.corners(): img.draw_circle(p[0], p[1], 8, color = (78, 90, 34))
                print(r)


        # 居中显示图像 Display image centered
        x = int((DISPLAY_WIDTH - PICTURE_WIDTH) / 2)
        y = int((DISPLAY_HEIGHT - PICTURE_HEIGHT) / 2)
        Display.show_image(img, x=x, y=y)

except KeyboardInterrupt as e:
    print("User Stop: ", e)
except BaseException as e:
    print(f"Exception: {e}")
finally:
    # 清理资源 Cleanup resources
    if isinstance(sensor, Sensor):
        sensor.stop()
    Display.deinit()
    os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
    time.sleep_ms(100)
    MediaManager.deinit()

改动思路如下:首先运行亚博官方提供的矩形检测代码,确认电工胶带的矩形能够识别出来;随后调用阈值编辑器将阈值设定好并自定义变量BLACK_THRESHOLD存放阈值,在识别矩阵并打印相关信息前,先获取当前LAB值,再结合阈值加上判断条件:if BLACK_THRESHOLD[0] < stats.l_mean() < BLACK_THRESHOLD[1]:就可以实现既限制形状又限制颜色。在这个过程中我发现阈值编辑器获取的值不准,因此还打印了print(f"DEBUG: Found rect with l_mean = {stats.l_mean()}")辅助我的判断。

注意:threshold = 8000越大代表矩形识别越精确,但是也会导致可能有矩形识别不出来;越小就越容易把矩形识别出来但是精度也会降低。

借助串口通信发指令实现多套代码融合在main.py

有时候可能既要完成基础检测又要基于目标识别完成对物体的追踪,也就是普通代码和运行权重代码杂糅在一起。这时候博主就想尝试通过串口接收完成使用哪个代码的操作。

# -*- coding: utf-8 -*-
# ==================================================================================
# === 主控脚本:集成矩形检测与YOLO目标检测,通过UART指令切换模式 (重构版) ===
# ==================================================================================
# 版本说明:
# 根据新的需求重构了任务调度逻辑。
# - 指令 b'10'   : 启动 YOLO 目标检测任务。
# - 指令 b'1000' : 启动传统视觉矩形检测任务。
# - 指令 b'100'  : 停止当前运行的任务,返回到待机状态。
#
# Version Description:
# Refactored the task scheduling logic based on new requirements.
# - Command b'10'   : Starts the YOLO object detection task.
# - Command b'1000' : Starts the traditional vision rectangle detection task.
# - Command b'100'  : Stops the currently running task and returns to standby.
# ==================================================================================

# ----------------- 1. 导入所有需要的模块 -----------------
# 公共模块
import os, sys, gc, time

# 硬件与媒体相关
from media.sensor import *
from media.display import *
from media.media import *

# YOLO 与 PipeLine 相关
from libs.PipeLine import PipeLine, ScopedTiming
from libs.YOLO import YOLOv5
import ulab.numpy as np
import struct
# UART 通信
from ybUtils.YbUart import YbUart

# ----------------- 2. 初始化全局对象 -----------------
uart = YbUart(baudrate=115200)

# ==================================================================================
# 任务一 (指令 b'10'): AI视觉 - YOLO目标检测
# ==================================================================================
def task_detect_yolo_10():
    """
    执行YOLO目标检测任务。
    由指令 b'10' 触发。
    """
    print("\n--- 系统模式切换:进入 [YOLO目标检测] 模式 ---")

    kmodel_path = "/data/best.kmodel"
    labels = ["stm"]
    confidence_threshold = 0.3
    nms_threshold = 0.5
    model_input_size = [320, 320]

    # 初始化PipeLine
    pl = PipeLine(rgb888p_size=[224, 224], display_size=[640, 480], display_mode="lcd")
    pl.create()

    # 初始化YOLOv5实例
    yolo=YOLOv5(task_type="detect",mode="video",kmodel_path=kmodel_path,labels=labels,rgb888p_size=[224, 224],model_input_size=model_input_size,display_size=[640, 480],conf_thresh=confidence_threshold,nms_thresh=nms_threshold,max_boxes_num=50,debug_mode=0)
    yolo.config_preprocess()
    print("[YOLO目标检测] 初始化完成,开始运行...")

    # 任务主循环
    while True:

        if uart:
            command = uart.read(decode=False)
            if command == b'100':
                print(f"[YOLO目标检测] 收到停止指令 {command},准备退出...")
                break

        img = pl.get_frame()
        res = yolo.run(img)

        if res is not None and len(res) > 0:
            # 处理并发送数据
            for box_data in res:
                x_min, y_min, x_max, y_max = map(int, box_data[:4])
                w = x_max - x_min
                h = y_max - y_min
                center_x = x_min + w // 2
                center_y = y_min + h // 2
                label_name = labels[int(box_data[5])]
                label_id = int(box_data[5])

                # --- 这是修改的核心部分 ---
                # 1. 定义包头
                header = b'\x3C\x3b'


                # 2. 定义数据打包格式
                #    '<' 表示小端序 (Little-Endian),这是STM32等MCU的常用格式
                #    'H' 表示 unsigned short (2字节)
                #    'B' 表示 unsigned char (1字节)
                #    所以 'HHHHHHB' 对应 6个坐标值 和 1个label_id
                payload_format = '>HHHHHHB'
                payload = struct.pack(payload_format,
                                                  x_min, y_min, x_max, y_max,
                                                  center_x, center_y,
                                                  label_id)
                data_to_send = header + payload
                print(f"发送YOLO二进制数据 ({len(data_to_send)} bytes): {data_to_send.hex(' ')}")

                pto_data = f"${x_min},{y_min},{x_max},{y_max},{center_x},{center_y},{label_name}#"
                print("发送串口数据:", pto_data)

                if uart:
                    uart.send(data_to_send)

        # 在 OSD 图像上绘制结果
        yolo.draw_result(res, pl.osd_img)
        pl.show_image()
        gc.collect()

    yolo.deinit()
    pl.destroy()

    print("--- 系统模式切换:已退出 [YOLO目标检测] 模式 ---\n")


# ==================================================================================
# 任务二 (指令 b'1000'): 传统视觉 - 矩形检测
# ==================================================================================
def task_detect_rects_1000():
    """
    执行矩形检测任务。
    由指令 b'1000' 触发。
    """
    print("\n--- 系统模式切换:进入 [矩形检测] 模式 ---")

    sensor = None
    try:
        # 任务相关的参数
        PICTURE_WIDTH = 400
        PICTURE_HEIGHT = 240
        DISPLAY_WIDTH = 640
        DISPLAY_HEIGHT = 480

        # 初始化摄像头
        sensor = Sensor()
        sensor.reset()
        sensor.set_framesize(width=PICTURE_WIDTH, height=PICTURE_HEIGHT, chn=CAM_CHN_ID_0)
        sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)

        # 初始化显示
        Display.init(Display.ST7701, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)

        # 初始化媒体管理器
        MediaManager.init()
        sensor.run()
        print("[矩形检测] 初始化完成,开始运行...")

        # 任务主循环
        while True:
            os.exitpoint()

            # 检查是否收到'停止'指令
            if uart:
                command = uart.read(decode=False)
                if command == b'100':
                    print(f"[矩形检测] 收到停止指令 {command},准备退出...")
                    break

            # 捕获图像并执行算法
            img = sensor.snapshot(chn=CAM_CHN_ID_0)
            for r in img.find_rects(threshold=8000):
                img.draw_rectangle(r.rect(), color=(40, 167, 225), thickness=2)
                for p in r.corners():
                    img.draw_circle(p[0], p[1], 8, color=(78, 90, 34))
                    #print(r)

                    corners = r.corners()
                    if len(corners) == 4:
                        # 2. 从列表中提取出4个点的坐标
                        p1 = corners[0]
                        p2 = corners[1]
                        p3 = corners[2]
                        p4 = corners[3]

                        # 3. 按新格式 "$x1,y1,x2,y2,x3,y3,x4,y4#" 格式化字符串
                        #    注意:角点的顺序通常是固定的(例如顺时针),您的接收端需要按此顺序解析】


                        pto_data = f"${p1[0]},{p1[1]},{p2[0]},{p2[1]},{p3[0]},{p3[1]},{p4[0]},{p4[1]}#"

                        # 4. 打印调试信息并通过串口发送数据
                        print("发送串口数据:", pto_data)
                        uart.send(pto_data)

            # 居中显示图像
            x = int((DISPLAY_WIDTH - PICTURE_WIDTH) / 2)
            y = int((DISPLAY_HEIGHT - PICTURE_HEIGHT) / 2)
            Display.show_image(img, x=x, y=y)

    except Exception as e:
        print(f"[矩形检测] 任务执行出错: {e}")
    finally:
        # 关键步骤:清理本任务使用的所有资源
        print("[矩形检测] 正在清理资源...")
        if isinstance(sensor, Sensor):
            sensor.stop()
        Display.deinit()
        MediaManager.deinit()
        gc.collect()
        print("--- 系统模式切换:已退出 [矩形检测] 模式 ---\n")


# ==================================================================================
# === 主调度器 ===
# ==================================================================================
if __name__ == "__main__":

    print("系统启动成功,进入主循环调度器。")
    print("系统待机中,等待任务指令 (b'10', b'1000', b'100')...")

    while True:
        try:

            # 使用阻塞方式等待指令,此时CPU占用率很低
            command = uart.read(decode=False)

            if command == b'10':
                task_detect_yolo_10()

            elif command == b'1000':
                task_detect_rects_1000()

            elif command == b'100':
                print("系统已处于待机模式。")
                pass

            else:
                if command:
                    print(f"收到未知指令: {command}")
                # 短暂延时防止未知指令导致循环过快
                time.sleep_ms(100)

        except KeyboardInterrupt:
            print("用户通过Ctrl+C中断程序。")
            break
        except Exception as e:
            print(f"主调度器发生严重错误: {e}")
            break

    print("程序已退出。")