2026-04-12 14:44:05 +08:00

229 lines
7.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import cv2
import numpy as np
import motor
import time
import signal
import sys
# 信号处理函数用于捕获Ctrl+C
def signal_handler(sig, frame):
print("\n接收到中断信号,正在停止电机...")
motor.stop()
print("电机已停止,程序退出")
sys.exit(0)
# 注册信号处理函数
signal.signal(signal.SIGINT, signal_handler)
print("开始运行摄像头程序...")
print(f"OpenCV版本: {cv2.__version__}")
# 尝试打开摄像头
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("无法打开摄像头!")
exit()
print("摄像头打开成功")
# 设置摄像头参数
cap.set(3, 320)
cap.set(4, 240)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
print(f"摄像头分辨率: {cap.get(3)}x{cap.get(4)}")
# 初始化稳定阶段
print("初始化摄像头...")
success_count = 0
for i in range(10):
ret, frame = cap.read()
if not ret:
print(f"{i+1}帧读取失败")
continue
success_count += 1
print(f"{i+1}帧读取成功")
time.sleep(0.1)
print(f"摄像头初始化完成,成功读取{success_count}")
# 读取初始帧
ret, prev_frame = cap.read()
if not ret:
print("无法读取初始帧!")
cap.release()
exit()
print("初始帧读取成功")
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
prev_gray = cv2.GaussianBlur(prev_gray, (5,5), 0)
print("初始帧处理完成")
# 记录上一帧目标的位置和面积
prev_cx = None
prev_cy = None
prev_area = None
# 目标跟踪状态
tracking_target = None
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5,5), 0)
# 帧差
diff = cv2.absdiff(prev_gray, gray)
diff = cv2.convertScaleAbs(diff, alpha=2.5)
# 应用形态学操作,去除噪声
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
diff = cv2.morphologyEx(diff, cv2.MORPH_OPEN, kernel)
diff = cv2.morphologyEx(diff, cv2.MORPH_CLOSE, kernel)
_, thresh = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# ===== 核心:目标检测和跟踪 =====
target = None
max_area = 0
# 找到所有符合条件的轮廓
valid_contours = []
for cnt in contours:
area = cv2.contourArea(cnt)
if area < 500: # 过滤噪声,增大阈值
continue
if area > 8000: # 过滤大面积假目标(如整个画面),减小阈值
continue
# 计算轮廓的宽高比,过滤不符合垃圾特征的目标
x, y, w, h = cv2.boundingRect(cnt)
aspect_ratio = float(w) / h if h > 0 else 0
if aspect_ratio > 3 or aspect_ratio < 0.3: # 过滤过于狭长的目标
continue
valid_contours.append((area, cnt, x, y, w, h))
# 如果有有效的轮廓
if valid_contours:
# 如果正在跟踪目标,优先选择与上一目标位置接近的轮廓
if tracking_target is not None and prev_cx is not None and prev_cy is not None:
best_match = None
min_distance = float('inf')
for area, cnt, x, y, w, h in valid_contours:
cx = x + w // 2
cy = y + h // 2
# 计算与上一目标的距离
distance = ((cx - prev_cx) ** 2 + (cy - prev_cy) ** 2) ** 0.5
# 如果距离小于阈值,认为是同一个目标
if distance < 50: # 距离阈值
if distance < min_distance:
min_distance = distance
best_match = (area, cnt, x, y, w, h)
# 如果找到匹配的目标
if best_match:
area, cnt, x, y, w, h = best_match
target = cnt
max_area = area
else:
# 如果没有找到匹配的目标,选择最大的轮廓
valid_contours.sort(key=lambda x: x[0], reverse=True)
area, cnt, x, y, w, h = valid_contours[0]
target = cnt
max_area = area
# 开始跟踪新目标
tracking_target = True
else:
# 如果没有正在跟踪的目标,选择最大的轮廓
valid_contours.sort(key=lambda x: x[0], reverse=True)
area, cnt, x, y, w, h = valid_contours[0]
target = cnt
max_area = area
# 开始跟踪新目标
tracking_target = True
# ===== 只处理一个目标 =====
if target is not None:
x, y, w, h = cv2.boundingRect(target)
cx = x + w // 2
cy = y + h // 2
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.circle(frame, (cx, cy), 5, (0, 0, 255), -1)
print("唯一目标:", cx, cy, "area:", max_area)
# 检测目标是否突然跳变
target_jumped = False
if prev_cx is not None and prev_cy is not None and prev_area is not None:
# 计算位置变化
dx = abs(cx - prev_cx)
dy = abs(cy - prev_cy)
# 计算面积变化比例
area_ratio = max_area / prev_area if prev_area > 0 else 0
# 如果位置变化过大或面积变化过大,认为目标跳变
if dx > 100 or dy > 100 or area_ratio < 0.3 or area_ratio > 3:
target_jumped = True
print("目标突然跳变,可能已离开视野范围")
# 重置跟踪状态
tracking_target = None
# 控制逻辑根据y轴坐标控制垃圾桶前后移动
center_y = 120 # 画面中心y坐标
threshold = 20 # 阈值范围
speed = 3000 # 电机速度
if target_jumped:
# 目标跳变,停止移动
print("目标跳变,停止移动")
motor.stop()
elif cy < center_y - threshold:
# 目标在上侧,垃圾桶向后移动
print("目标在上侧,向后移动")
# 使用新的电机控制函数
motor.backward(speed=0.6)
elif cy > center_y + threshold:
# 目标在下侧,垃圾桶向前移动
print("目标在下侧,向前移动")
# 使用新的电机控制函数
motor.forward(speed=0.6)
else:
# 目标在中心附近,停止移动
print("目标在中心,停止移动")
motor.stop()
# 更新上一帧的目标信息
prev_cx = cx
prev_cy = cy
prev_area = max_area
else:
# 没有检测到目标,停止移动
print("未检测到目标,停止移动")
motor.stop()
# 重置上一帧的目标信息
prev_cx = None
prev_cy = None
prev_area = None
# 重置跟踪状态
tracking_target = None
cv2.imshow("tracking", frame)
prev_gray = gray.copy()
if cv2.waitKey(1) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
motor.stop()