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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# 导入必要的模块 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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# -*- 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("程序已退出。")