# K230 圆形检测滤波优化版
# 专为K230平台优化,使用兼容的API和滤波技术
# 加入高斯滤波和形态学滤波,减少资源消耗
from machine import Pin, FPIOA, PWM
from media.sensor import *
from media.display import *
from media.media import *
import time
import math
import gc
print(">>> K230 圆形检测滤波优化版 <<<")
print("🚀 专为K230优化,兼容API+滤波技术")
# 硬件配置
X_SERVO_GPIO, Y_SERVO_GPIO = 42, 52
X_SERVO_PWM_ID, Y_SERVO_PWM_ID = 0, 4
SERVO_FREQ = 50
PULSE_CENTER = 1.6
PULSE_MIN = 0.5
PULSE_MAX = 2.5
# 停止按键
fpioa = FPIOA()
fpioa.set_function(27, FPIOA.GPIO27)
key1 = Pin(27, Pin.IN, Pin.PULL_UP)
class K230FilteredCircleSystem:
"""K230滤波优化版圆形检测系统"""
def __init__(self):
# 🚀 优化的检测参数
self.BINARY_THRESHOLD = [(62, 32)] # 二值化阈值
self.CIRCLE_AREA_THRESHOLD = 1000 # 圆形面积阈值
self.MIN_RADIUS = 25 # 最小半径
self.MAX_RADIUS = 180 # 最大半径
self.DETECTION_THRESHOLD = 3500 # 检测阈值
# 🚀 滤波参数
self.GAUSSIAN_KERNEL_SIZE = 3 # 高斯滤波核大小
self.USE_MORPHOLOGICAL = True # 启用形态学滤波
self.ERODE_DILATE_SIZE = 1 # 腐蚀膨胀核大小
# 🚀 性能优化参数
self.FRAME_SKIP = 2 # 帧跳跃间隔
self.GC_INTERVAL = 20 # 垃圾回收间隔
self.STABLE_FRAMES_REQUIRED = 30 # 稳定帧数要求(减少)
# 🚀 自适应参数
self.adaptive_enabled = True # 启用自适应调整
self.detection_failure_count = 0 # 检测失败计数
self.adaptive_threshold_step = 200 # 阈值调整步长
# 分辨率设置
self.DISPLAY_WIDTH, self.DISPLAY_HEIGHT = 480, 800
self.saved_circle = None
self.gimbal = None
self.sensor = None
self.frame_counter = 0
print("🚀 K230滤波优化参数:")
print(" 显示分辨率: {}x{}".format(self.DISPLAY_WIDTH, self.DISPLAY_HEIGHT))
print(" 高斯滤波核: {}x{}".format(self.GAUSSIAN_KERNEL_SIZE, self.GAUSSIAN_KERNEL_SIZE))
print(" 形态学滤波: {}".format("启用" if self.USE_MORPHOLOGICAL else "禁用"))
print(" 检测阈值: {}".format(self.DETECTION_THRESHOLD))
print(" 帧跳跃间隔: {}".format(self.FRAME_SKIP))
print(" 自适应调整: {}".format("启用" if self.adaptive_enabled else "禁用"))
def init_hardware(self):
"""初始化硬件"""
print("🚀 初始化硬件...")
self.gimbal = K230OptimizedGimbal()
self.init_camera_and_display()
print("✅ 硬件初始化完成")
def init_camera_and_display(self):
"""摄像头和显示初始化"""
print("📷 开始摄像头和显示初始化...")
try:
# 初始化Display
print("🖥️ 初始化Display...")
Display.init(Display.ST7701, width=self.DISPLAY_WIDTH, height=self.DISPLAY_HEIGHT, to_ide=True)
print("✅ Display初始化成功")
# 初始化MediaManager
print("📺 初始化MediaManager...")
MediaManager.init()
print("✅ MediaManager初始化成功")
# 初始化摄像头
print("📷 初始化摄像头传感器...")
self.sensor = Sensor(id=2)
self.sensor.reset()
print("✅ 摄像头传感器重置完成")
# 设置摄像头参数
print("⚙️ 设置摄像头参数...")
self.sensor.set_framesize(width=self.DISPLAY_WIDTH, height=self.DISPLAY_HEIGHT)
self.sensor.set_pixformat(Sensor.RGB565)
print("✅ 摄像头参数设置完成")
# 启动摄像头
print("🚀 启动摄像头...")
self.sensor.run()
print("✅ 摄像头启动成功")
# 等待稳定
print("⏳ 等待摄像头稳定...")
time.sleep(2)
# 测试快照
print("📸 测试摄像头快照...")
test_img = self.sensor.snapshot()
if test_img:
print("✅ 摄像头快照测试成功")
print("📊 图像尺寸: {}x{}".format(test_img.width(), test_img.height()))
else:
raise RuntimeError("摄像头快照测试失败")
print("🎉 摄像头和显示系统初始化完成")
except Exception as e:
print("❌ 初始化失败: {}".format(e))
self.cleanup_resources()
raise e
def cleanup_resources(self):
"""清理资源"""
try:
if self.sensor:
self.sensor.stop()
self.sensor = None
Display.deinit()
MediaManager.deinit()
gc.collect()
except:
pass
def deinit_camera(self):
"""安全关闭摄像头"""
print("📷 正在关闭摄像头...")
try:
if self.sensor:
self.sensor.stop()
time.sleep(0.5)
Display.deinit()
MediaManager.deinit()
self.sensor = None
print("📷 摄像头已关闭")
except Exception as e:
print("⚠️ 摄像头关闭警告: {}".format(e))
def apply_filtering(self, img):
"""🚀 K230兼容的图像滤波处理"""
try:
# 转换为灰度图(就地操作,节省内存)
img.to_grayscale(copy=False)
# 高斯滤波降噪
img.gaussian(self.GAUSSIAN_KERNEL_SIZE, copy=False)
# 二值化
img.binary(self.BINARY_THRESHOLD, copy=False)
# 形态学滤波(如果启用)
if self.USE_MORPHOLOGICAL:
# 开运算:先腐蚀后膨胀,去除小噪声
img.erode(self.ERODE_DILATE_SIZE, threshold=1)
img.dilate(self.ERODE_DILATE_SIZE, threshold=1)
return img
except Exception as e:
print("⚠️ 滤波处理失败: {}".format(e))
return None
def detect_filtered_circles(self, img):
"""🚀 K230兼容的滤波圆形检测"""
try:
# 应用滤波处理
filtered_img = self.apply_filtering(img.copy())
if not filtered_img:
return None
# 使用K230支持的find_circles方法
circles = filtered_img.find_circles(threshold=self.DETECTION_THRESHOLD,
x_margin=15, y_margin=15, r_margin=15,
r_min=self.MIN_RADIUS, r_max=self.MAX_RADIUS, r_step=3)
del filtered_img # 立即释放内存
if not circles:
return None
print("🔍 滤波检测到 {} 个圆形".format(len(circles)))
# 选择最佳圆形
best_circle = self.select_best_circle(circles)
return best_circle
except Exception as e:
print("⚠️ 滤波检测失败: {}".format(e))
return None
def select_best_circle(self, circles):
"""选择最佳圆形"""
try:
valid_circles = []
for circle in circles:
area = math.pi * circle.r() * circle.r()
# 基本过滤
if area < self.CIRCLE_AREA_THRESHOLD:
continue
# 位置检查
center_x, center_y = circle.x(), circle.y()
radius = circle.r()
margin = radius + 15
if (center_x < margin or center_x > (self.DISPLAY_WIDTH - margin) or
center_y < margin or center_y > (self.DISPLAY_HEIGHT - margin)):
continue
# 计算评分
size_score = min(area / 2000.0, 1.0)
center_score = 1.0 - abs(center_x - self.DISPLAY_WIDTH/2) / (self.DISPLAY_WIDTH/2) * 0.3
total_score = size_score * center_score
valid_circles.append((circle, total_score, area))
print("✅ 滤波候选圆形: 中心({}, {}), 半径{}, 评分{:.2f}".format(
center_x, center_y, radius, total_score))
if valid_circles:
# 选择评分最高的圆形
best_circle, best_score, best_area = max(valid_circles, key=lambda x: x[1])
print("🎯 选择最佳滤波圆形: 中心({}, {}), 半径{}, 评分{:.2f}".format(
best_circle.x(), best_circle.y(), best_circle.r(), best_score))
return (best_circle.x(), best_circle.y(), best_circle.r())
return None
except Exception as e:
print("⚠️ 圆形选择失败: {}".format(e))
return None
def adaptive_adjust_parameters(self, detection_success):
"""🚀 自适应参数调整"""
if not self.adaptive_enabled:
return
if detection_success:
# 检测成功,重置失败计数
self.detection_failure_count = 0
else:
# 检测失败,增加失败计数
self.detection_failure_count += 1
# 每连续失败8次,调整参数
if self.detection_failure_count >= 8:
print("🔧 自适应调整: 连续{}次检测失败,调整参数".format(self.detection_failure_count))
# 降低检测阈值,提高检测敏感度
if self.DETECTION_THRESHOLD > 2000:
self.DETECTION_THRESHOLD -= self.adaptive_threshold_step
print("🔧 调整检测阈值: {}".format(self.DETECTION_THRESHOLD))
# 降低二值化阈值
current_threshold = self.BINARY_THRESHOLD[0][0]
new_threshold = max(30, current_threshold - 5)
if new_threshold != current_threshold:
self.BINARY_THRESHOLD = [(new_threshold, 255)]
print("🔧 调整二值化阈值: {} -> {}".format(current_threshold, new_threshold))
# 重置失败计数
self.detection_failure_count = 0
def auto_detection_phase(self):
"""🚀 自动检测阶段 - K230优化版"""
print("🔍 开始K230滤波圆形检测...")
frame_count = 0
stable_count = 0
detection_history = []
while True:
if key1.value() == 0:
return False
try:
img = self.sensor.snapshot()
frame_count += 1
self.frame_counter += 1
# 🚀 帧跳跃优化 - 减少处理频率
if frame_count % self.FRAME_SKIP != 0:
# 显示原始图像,但不进行检测
status = "K230滤波版 | 帧数: {} | 跳帧中...".format(frame_count)
img.draw_string_advanced(10, self.DISPLAY_HEIGHT - 30, 16, status, color=(128, 128, 128))
Display.show_image(img)
continue
circle_info = self.detect_filtered_circles(img)
stable_circle = None
# 🚀 自适应参数调整
self.adaptive_adjust_parameters(circle_info is not None)
if circle_info:
detection_history.append(circle_info)
if len(detection_history) > 6: # 减少历史记录长度
detection_history.pop(0)
if len(detection_history) >= 3: # 减少稳定性要求
stable_circle = self.calculate_stable_circle(detection_history)
if stable_circle:
stable_count += 1
# 绘制检测结果
if circle_info:
center_x, center_y, radius = circle_info
img.draw_circle(int(center_x), int(center_y), int(radius), color=(255, 255, 0), thickness=2)
if stable_circle:
center_x, center_y, radius = stable_circle
img.draw_circle(int(center_x), int(center_y), int(radius), color=(0, 255, 0), thickness=3)
img.draw_cross(int(center_x), int(center_y), color=(255, 0, 0), size=15, thickness=3)
if stable_circle and stable_count >= self.STABLE_FRAMES_REQUIRED:
img.draw_string_advanced(100, 300, 28, "✅ 检测成功!", color=(0, 255, 0))
Display.show_image(img)
self.saved_circle = stable_circle
print("✅ 滤波圆形检测成功: 中心({}, {}), 半径{}".format(
stable_circle[0], stable_circle[1], stable_circle[2]))
time.sleep(1)
return True
elif stable_circle:
progress = min(stable_count / self.STABLE_FRAMES_REQUIRED, 1.0) * 100
img.draw_string_advanced(10, 10, 22, "滤波检测中... {:.0f}%".format(progress), color=(0, 255, 255))
else:
img.draw_string_advanced(10, 10, 22, "寻找稳定圆形...", color=(255, 255, 0))
else:
stable_count = 0
img.draw_string_advanced(10, 10, 22, "滤波分析中...", color=(255, 255, 0))
# 状态显示
status = "K230滤波版 | 帧数: {} | 阈值: {} | 按KEY1停止".format(frame_count, self.DETECTION_THRESHOLD)
img.draw_string_advanced(10, self.DISPLAY_HEIGHT - 30, 16, status, color=(255, 255, 255))
Display.show_image(img)
# 🚀 优化的垃圾回收
if self.frame_counter % self.GC_INTERVAL == 0:
gc.collect()
except Exception as e:
print("⚠️ 检测循环错误: {}".format(e))
time.sleep(0.1)
def calculate_stable_circle(self, history):
"""计算稳定圆形"""
if len(history) < 3: # 减少要求
return None
try:
# 计算最近检测的平均值
recent_detections = history[-3:]
avg_center_x = sum(detection[0] for detection in recent_detections) / 3
avg_center_y = sum(detection[1] for detection in recent_detections) / 3
avg_radius = sum(detection[2] for detection in recent_detections) / 3
return (int(avg_center_x), int(avg_center_y), int(avg_radius))
except:
return None
def countdown_phase(self):
"""倒计时阶段"""
print("⏰ 开始5秒倒计时...")
for countdown in range(5, 0, -1):
if key1.value() == 0:
return False
print("⏰ 倒计时: {}秒".format(countdown))
time.sleep(1)
return True
def run(self):
"""主运行流程"""
try:
self.init_hardware()
print("\n" + "="*60)
print("🚀 K230滤波优化版圆形检测系统启动")
print("📋 特点: K230兼容API+滤波技术,优化资源使用")
print("🛑 任何阶段按KEY1都可停止")
print("="*60)
if not self.auto_detection_phase():
return
if not self.countdown_phase():
return
print("🎯 检测完成!保存的圆形信息: {}".format(self.saved_circle))
except Exception as e:
print("❌ 系统错误: {}".format(e))
import sys
sys.print_exception(e)
finally:
self.cleanup()
def cleanup(self):
"""清理资源"""
print("🧹 清理系统资源...")
if self.sensor:
self.deinit_camera()
if self.gimbal:
self.gimbal.deinit()
gc.collect()
print("✅ 资源清理完成")
class K230OptimizedGimbal:
"""K230优化版舵机控制类"""
def __init__(self):
fpioa = FPIOA()
fpioa.set_function(X_SERVO_GPIO, getattr(FPIOA, f'PWM{X_SERVO_PWM_ID}'))
fpioa.set_function(Y_SERVO_GPIO, getattr(FPIOA, f'PWM{Y_SERVO_PWM_ID}'))
self.pwm_x = PWM(X_SERVO_PWM_ID, SERVO_FREQ, enable=True)
self.pwm_y = PWM(Y_SERVO_PWM_ID, SERVO_FREQ, enable=True)
self.period_ms = 1000 / SERVO_FREQ
# 校准参数
self.SERVO_MAPPING_SCALE_X = 1.4
self.SERVO_MAPPING_SCALE_Y = 0.7
self.SERVO_OFFSET_X = 0.0
self.SERVO_OFFSET_Y = 3.0
self.BASE_ANGLE_MAPPING = 12.0
self.CAMERA_CENTER_X = 240 # 480/2
self.CAMERA_CENTER_Y = 400 # 800/2
self.reset()
def reset(self):
"""复位到中心位置"""
pulse_x = self.angle_to_pulse(0)
pulse_y = self.angle_to_pulse(0)
duty_x = (pulse_x / self.period_ms) * 100
duty_y = (pulse_y / self.period_ms) * 100
self.pwm_x.duty(duty_x)
self.pwm_y.duty(duty_y)
time.sleep(0.8)
def angle_to_pulse(self, angle):
"""角度转脉宽"""
angle = max(-40, min(40, angle))
if angle >= 0:
pulse = PULSE_CENTER + (angle / 40) * (PULSE_MAX - PULSE_CENTER)
else:
pulse = PULSE_CENTER + (angle / 40) * (PULSE_CENTER - PULSE_MIN)
return pulse
def deinit(self):
"""释放资源"""
self.reset()
self.pwm_x.deinit()
self.pwm_y.deinit()
def main():
"""主程序"""
system = K230FilteredCircleSystem()
system.run()
if __name__ == "__main__":
main()