完善V1.4踢乒乓球版本
This commit is contained in:
1 parent
d14fe6c37b
commit
72455a5e76
1864 files changed
+19294
-218
No files matched your search
@@ -0,0 +1,48 @@
|
||||
# ------------------------------
|
||||
# LED配置
|
||||
# ------------------------------
|
||||
|
||||
from maix import camera, display, image, nn, app, sys
|
||||
from maix import uart, pinmap, time, err, gpio
|
||||
import traceback
|
||||
|
||||
LED_PIN_NAME = "A14"
|
||||
LED_GPIO_NAME = "GPIOA14"
|
||||
BLINK_INTERVAL = 500 # LED闪烁间隔(ms)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# 全局业务状态变量
|
||||
# ------------------------------
|
||||
led = None # LED实例
|
||||
serial_dev = None # 舵机串口全局变量
|
||||
previous_millis = 0 # LED闪烁计时
|
||||
|
||||
# ------------------------------
|
||||
# LED控制
|
||||
# ------------------------------
|
||||
def init_led():
|
||||
"""初始化LED引脚"""
|
||||
global led
|
||||
try:
|
||||
err.check_raise(
|
||||
pinmap.set_pin_function(LED_PIN_NAME, LED_GPIO_NAME),
|
||||
f"设置LED引脚{LED_PIN_NAME}功能失败"
|
||||
)
|
||||
led = gpio.GPIO(LED_GPIO_NAME, gpio.Mode.OUT)
|
||||
led.value(0) # 初始熄灭
|
||||
print(f"LED初始化完成:引脚{LED_PIN_NAME}({LED_GPIO_NAME})")
|
||||
except Exception as e:
|
||||
print(f"LED初始化失败: {e}")
|
||||
led = None
|
||||
|
||||
def update_led():
|
||||
"""更新LED状态:改为固定闪烁模式"""
|
||||
global previous_millis
|
||||
if led is None:
|
||||
return
|
||||
|
||||
current_millis = time.time_ms()
|
||||
if current_millis - previous_millis >= BLINK_INTERVAL:
|
||||
previous_millis = current_millis
|
||||
led.toggle()
|
||||
@@ -0,0 +1,66 @@
|
||||
# main.py - 主业务逻辑(人脸跟踪、舵机控制、LED)
|
||||
from maix import camera, display, image, nn, app, sys
|
||||
from maix import uart, pinmap, time, err, gpio
|
||||
from maix import app, nn
|
||||
import traceback
|
||||
from config import *
|
||||
from test_ball import *
|
||||
|
||||
# ------------------------------
|
||||
# 系统初始化
|
||||
# ------------------------------
|
||||
def init_system():
|
||||
"""初始化相机、舵机、串口、LED"""
|
||||
global serial_dev # 声明使用全局serial_dev
|
||||
|
||||
init_led()
|
||||
|
||||
# 人脸检测器
|
||||
detector = nn.YOLOv8(model="/root/models/yolov8n_face.mud", dual_buff=True)
|
||||
img_width = detector.input_width()
|
||||
img_height = detector.input_height()
|
||||
print(f"[系统] 图像尺寸: {img_width}x{img_height}")
|
||||
image_center = (img_width // 2, img_height // 2)
|
||||
|
||||
# 相机/显示器
|
||||
cam = camera.Camera(img_width, img_height, detector.input_format())
|
||||
disp = display.Display()
|
||||
|
||||
# 串口(舵机)
|
||||
pin_function = {"A19": "UART1_TX", "A18": "UART1_RX"}
|
||||
device = "/dev/ttyS1"
|
||||
for pin, func in pin_function.items():
|
||||
err.check_raise(pinmap.set_pin_function(pin, func),
|
||||
f"Failed set pin{pin} function to {func}")
|
||||
serial_dev = uart.UART(device, 115200)
|
||||
|
||||
# 初始化值 距离中间点像素距离
|
||||
initial_cmd = f"DX:0,DY:0;"
|
||||
serial_dev.write_str(initial_cmd)
|
||||
print(f"[舵机] 初始角度: {initial_cmd}")
|
||||
time.sleep(1)
|
||||
|
||||
return {
|
||||
"detector": detector, "cam": cam, "disp": disp, "serial_dev": serial_dev,
|
||||
"img_width": img_width, "img_height": img_height, "image_center": image_center
|
||||
}
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# 系统执行
|
||||
# ------------------------------
|
||||
|
||||
try:
|
||||
# 初始化系统资源
|
||||
system_resources = init_system()
|
||||
|
||||
update_led()
|
||||
|
||||
# 进入主循环
|
||||
trace_ball_main(system_resources)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[异常] 程序异常: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
from maix import image, app, time
|
||||
|
||||
# 橘黄色乒乓球的颜色阈值配置(HSV色域)
|
||||
# yxl
|
||||
thresholds = [
|
||||
[73, 93, 7, 27, 13, 33],
|
||||
[40, 65, 30, 60, 40, 70],
|
||||
[39, 59, 41, 61, 48, 68],
|
||||
]
|
||||
|
||||
# mgw
|
||||
thresholds = [
|
||||
[60, 85, 10, 35, 60, 90],
|
||||
[40, 65, 20, 50, 60, 90],
|
||||
[30, 60, 30, 60, 40, 70],
|
||||
]
|
||||
|
||||
# 丢失目标的阈值配置
|
||||
LOST_FRAME_THRESHOLD = 20 # 连续x帧没识别到才判定为真正丢失
|
||||
|
||||
# 全局参数初始化
|
||||
kick_status = ""
|
||||
|
||||
# ------------------------------
|
||||
# 兵乓球追踪
|
||||
# ------------------------------
|
||||
def select_largest_blob(blobs):
|
||||
"""
|
||||
筛选并选取最大的有效blob(替换原融合逻辑)
|
||||
1. 筛除宽/高小于10px的小blob(过滤噪声)
|
||||
2. 从剩余blob中选取面积最大的单个blob
|
||||
:param blobs: 识别到的所有blob列表
|
||||
:return: 最大blob的矩形参数 (x, y, width, height) 和中心点 (cx, cy);未识别到则返回None, None
|
||||
"""
|
||||
if not blobs:
|
||||
return None, (-1,-1)
|
||||
|
||||
# 步骤1:过滤掉宽/高小于10px的无效blob
|
||||
valid_blobs = []
|
||||
for b in blobs:
|
||||
x, y, w, h = b[0], b[1], b[2], b[3]
|
||||
if w >= 10 and h >= 10: # 筛除边距(宽/高)小于10px的blob
|
||||
valid_blobs.append(b)
|
||||
|
||||
# 过滤后无有效blob,返回None
|
||||
if not valid_blobs:
|
||||
return None, (-1,-1)
|
||||
|
||||
# 步骤2:选取面积(w*h)最大的blob
|
||||
largest_blob = max(valid_blobs, key=lambda b: b[2] * b[3])
|
||||
|
||||
# 提取最大blob的矩形参数和中心点
|
||||
x, y, w, h = largest_blob[0], largest_blob[1], largest_blob[2], largest_blob[3]
|
||||
cx = x + w // 2
|
||||
cy = y + h // 2
|
||||
|
||||
return (x, y, w, h), (cx, cy)
|
||||
|
||||
def calculate_deviation_ball(center, image_center):
|
||||
"""
|
||||
优化:仅计算偏移量,不直接发送指令(将指令发送逻辑移到主循环统一处理)
|
||||
:return: (deviation_x, deviation_y) 或 None(无有效目标)
|
||||
"""
|
||||
center_x, center_y = center
|
||||
|
||||
if center_x == -1 and center_y == -1 :
|
||||
return None # 无有效目标
|
||||
|
||||
# 计算x/y方向的偏移偏差(目标中心 - 图像中心)
|
||||
deviation_x = center_x - image_center[0]
|
||||
deviation_y = center_y - image_center[1]
|
||||
|
||||
# 仅当偏移超过阈值时返回有效偏移,否则返回None(避免微小抖动)
|
||||
if abs(deviation_x) > 10 or abs(deviation_y) > 10 :
|
||||
return (deviation_x, deviation_y)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def trace_ball_main(resources):
|
||||
|
||||
# 声明使用全局变量(两个参数都需要更新)
|
||||
global kick_status
|
||||
|
||||
detector = resources["detector"]
|
||||
cam = resources["cam"]
|
||||
disp = resources["disp"]
|
||||
serial_dev = resources["serial_dev"]
|
||||
img_width = resources["img_width"]
|
||||
img_height = resources["img_height"]
|
||||
image_center = resources["image_center"]
|
||||
|
||||
last_valid_deviation = None # 记录最后一次有效偏移量
|
||||
lost_frames = 0 # 丢失帧数计数
|
||||
|
||||
while not app.need_exit():
|
||||
|
||||
img = cam.read().flip(image.FlipDir.Y)
|
||||
bcmd = ""
|
||||
|
||||
# 识别色块(像素阈值设为10,过滤小噪声)
|
||||
blobs = img.find_blobs(thresholds, pixels_threshold=10)
|
||||
|
||||
# 筛选并选取最大的有效blob
|
||||
largest_rect, target_center = select_largest_blob(blobs)
|
||||
|
||||
# 计算当前帧的偏移量
|
||||
current_deviation = calculate_deviation_ball(target_center, image_center)
|
||||
|
||||
# -------------------------- 核心优化逻辑 --------------------------
|
||||
if current_deviation is not None:
|
||||
# 1. 识别到有效目标:重置丢失计数,更新最后一次有效偏移
|
||||
lost_frames = 0
|
||||
last_valid_deviation = current_deviation
|
||||
dx, dy = current_deviation
|
||||
bcmd = f"DX:{dx},DY:{dy};"
|
||||
else:
|
||||
# 2. 未识别到目标:累计丢失帧数
|
||||
lost_frames += 1
|
||||
if lost_frames < LOST_FRAME_THRESHOLD and last_valid_deviation is not None:
|
||||
dx, dy = last_valid_deviation
|
||||
bcmd = f"DX:{dx},DY:{dy};"
|
||||
else:
|
||||
# 2.2 丢失帧数达阈值:发送9999,重置最后有效偏移
|
||||
bcmd = "DX:9999,DY:9999;"
|
||||
last_valid_deviation = None
|
||||
|
||||
# 绘制识别结果
|
||||
if largest_rect:
|
||||
x, y, w, h = largest_rect
|
||||
img.draw_rect(x, y, w, h, image.COLOR_GREEN, 2)
|
||||
cx, cy = target_center
|
||||
img.draw_circle(cx, cy, 3, image.COLOR_RED, -1)
|
||||
|
||||
|
||||
serial_dev.write_str(bcmd)
|
||||
print(bcmd)
|
||||
|
||||
disp.show(img)
|
||||
time.sleep_ms(1)
|
||||
Reference in new issue
Block a user