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("程序已退出。")