您当前的位置:首页 > 计算机 > 编程开发 > Python

蚊子雷达追踪打击系统-源码

时间:08-01来源:作者:点击数:
CDSY,CDSY.XYZ

一、核心功能

(一)蚊虫视觉追踪

  • 摄像头实时监测
    • 通过电脑摄像头采集画面,利用背景减法(MOG2 算法)与Canny 边缘检测识别运动目标。
    • 过滤干扰(如灰尘、灯光反射),仅标记符合蚊虫特征(面积 5-150px²、周长 > 10px、圆形度 > 0.5)的物体。
  • 多目标跟踪
    • 基于轨迹匹配算法为每个蚊虫分配唯一 ID,记录其位置、速度、方向等参数。
    • 显示历史轨迹(主轨迹线 + 尾迹渐变效果),支持追踪至多目标同时移动。

(二)超声波驱蚊攻击

  • 高频声波生成
    • 生成22000Hz 超声波音频(接近蚊子感知上限),存储为 WAV 文件并循环播放。
    • 自动触发逻辑:检测到蚊虫时播放声波,间隔 5 秒避免听觉适应(可通过按键手动切换开关)。
  • 脉冲音效设计
    • 蚊虫标记点伴随正弦波动效果,模拟雷达 “锁定 - 攻击” 视觉反馈,增强交互沉浸感。

(三)数据可视化与统计

  • 雷达界面
    • 极坐标雷达图:以绿色扫描线、红色标记点显示蚊虫位置(距离 0-500cm,角度对应屏幕方位)。
    • 动态扫描效果:扇形区域渐变填充 + 尾迹拖影,模拟军用雷达扫描逻辑。
  • 信息面板
    • 实时显示系统状态(运行时间、摄像头连接)、检测统计(当前 / 今日蚊虫数、最大同时数量)、单目标详情(速度 / 距离 / 方向)、追踪精度(准确率、误报率)。
    • 支持按键截图(保存雷达界面为 PNG),便于后期分析。

(四)交互与扩展

  • 快捷键控制:
    • Q:退出程序;A:切换声波驱蚊开关;P:截取当前雷达画面。
  • 多线程优化:
    • 音频播放与图像渲染分离至独立线程,避免卡顿。
  • 可配置参数:
    • 边缘检测阈值(EDGE_THRESHOLD1/2)、背景减法历史帧数等可手动调整,适配不同光照环境。

二、技术特点

(一)跨领域技术融合

  • 计算机视觉:OpenCV 实现图像处理、轮廓检测与目标跟踪。
  • 音频处理:PyGame 播放声波,Wave 模块生成音频数据,结合超声波生物学特性设计频率。
  • 可视化:Matplotlib 绘制动态雷达图,采用 Qt5Agg 后端提升图形渲染效率。

(二)拟真交互设计

  • 暗黑风格界面:黑色背景搭配荧光绿 / 红色元素,降低视觉疲劳,符合 “夜间驱蚊” 场景需求。
  • 军事级动效:扫描线速度、轨迹尾迹透明度等参数模拟真实雷达效果,增强科技感。

三、界面展示

四、源码

import cv2
import numpy as np
import matplotlib
import math
from matplotlib import pyplot as plt
from matplotlib import rcParams
from matplotlib.animation import FuncAnimation
from collections import deque
from datetime import datetime
import time
import pygame
import os
import wave
import struct
 
# **添加以下两行设置后端**
matplotlib.use('Qt5Agg')  # 替换原有的'TkAgg'
 
# 在独立线程中运行Matplotlib动画
import threading
 
# 设置中文字体
rcParams['font.family'] = 'SimHei'
rcParams['axes.unicode_minus'] = False
 
 
# 生成驱蚊音频文件
def generate_anti_mosquito_sound():
    sample_rate = 44100  # 采样率:每秒采集44100个音频样本
    duration = 3.0  # 音频时长:3秒
    freq = 22000  # 频率:22000Hz(超声波,接近蚊子能感知的上限)
 
    samples = []
    for i in range(int(duration * sample_rate)):
        sample = 0.5 * math.sin(2 * math.pi * freq * i / sample_rate)
        samples.append(sample)
 
    filename = "mosquito_sound.wav"
    with wave.open(filename, 'w') as wf:
        wf.setnchannels(1)
        wf.setsampwidth(2)
        wf.setframerate(sample_rate)
        for sample in samples:
            data = struct.pack('<h', int(sample * 32767))
            wf.writeframesraw(data)
    return filename
 
 
# 初始化音频系统
try:
    pygame.mixer.init()
    sound_file = generate_anti_mosquito_sound()
    mosquito_sound = pygame.mixer.Sound(sound_file)
    print("已生成驱蚊音频文件")
except Exception as e:
    print(f"音频初始化失败: {e}")
    mosquito_sound = None
 
# 初始化雷达图
plt.style.use('dark_background')
fig = plt.figure(figsize=(10, 8), facecolor='black')
fig.suptitle('蚊子雷达追踪打击系统', color='lime', fontsize=16, fontweight='bold')
 
# 创建雷达主界面 - 改进的潜水艇风格
ax_radar = fig.add_subplot(121, polar=True, facecolor=(0, 0.05, 0))
ax_info = fig.add_subplot(122, facecolor='black')
ax_info.axis('off')
 
# 雷达图美化设置 - 军用风格
ax_radar.set_theta_zero_location('N')
ax_radar.set_theta_direction(-1)
ax_radar.set_ylim(0, 500)
ax_radar.set_yticklabels([])
ax_radar.grid(color='lime', alpha=0.2, linestyle='-')
ax_radar.spines['polar'].set_visible(False)
ax_radar.tick_params(axis='both', colors='lime')
 
# 添加雷达背景效果 - 同心圆网格
background_circles = []
for r in [100, 200, 300, 400, 500]:
    circle = plt.Circle((0, 0), r, transform=ax_radar.transData._b,
                        fill=False, color='lime', alpha=0.1, linewidth=0.5)
    ax_radar.add_artist(circle)
    background_circles.append(circle)
    ax_radar.text(0, r, f'{r}cm', color='lime', ha='center', va='center',
                  fontsize=8, alpha=0.7)
 
# 添加雷达中心点
center_point = ax_radar.scatter([0], [0], c='lime', s=50, alpha=0.8)
 
# 初始化雷达元素
scan_line = ax_radar.plot([], [], color='lime', linestyle='-', linewidth=2, alpha=0.9)[0]
scan_shadow = ax_radar.plot([], [], color='lime', linestyle='-', linewidth=8, alpha=0.1)[0]
mosquito_dots = ax_radar.scatter([], [], c='red', s=80, alpha=0.9,
                                 edgecolors='yellow', linewidths=1.5, zorder=10)
scan_arc = None
scan_arc_fill = None
trail_lines = []
 
# 初始化雷达数据
max_distance = 500
r = deque([0] * 360, maxlen=360)
theta = np.linspace(0, 2 * np.pi, 360, endpoint=False)
 
 
# 系统状态变量
class SystemState:
    def __init__(self):
        self.auto_sound = True  # 默认开启声波攻击
        self.sound_playing = False
        self.last_sound_time = 0
        self.total_detected = 0
        self.detected_today = 0
        self.start_time = datetime.now()
        self.screenshot_count = 0
 
 
system_state = SystemState()
 
 
# 初始化信息面板
def init_info_panel():
    titles = ["系统状态", "检测统计", "蚊子信息", "追踪数据", "声波设置"]
    contents = [
        [f"状态: 运行中", f"扫描中", f"摄像头: 开启", f"启动: {system_state.start_time.strftime('%H:%M:%S')}"],
        [f"当前: 0", f"今日: 0", f"最大: 0", f"平均: 0"],
        [f"速度: 0 cm/s", f"大小: 0 px", f"方向: -", f"距离: 0 cm"],
        [f"追踪: 0", f"历史: 0", f"误报: 0", f"准确率: 0%"],
        [f"声波驱蚊: 开启", f"按A键切换", f"截图: 按P键", ""]  # 修改显示文本
    ]
 
    title_y_positions = [0.92, 0.72, 0.52, 0.32, 0.12]
    content_line_height = 0.05
    title_content_gap = 0.02
 
    info_texts = []
    for i, (title, content) in enumerate(zip(titles, contents)):
        ax_info.text(0.1, title_y_positions[i], title,
                     color='cyan', fontsize=11, fontweight='bold',
                     transform=ax_info.transAxes)
 
        for j, item in enumerate(content):
            text = ax_info.text(0.15,
                                title_y_positions[i] - title_content_gap - j * content_line_height,
                                item,
                                color='lime', fontsize=9,
                                transform=ax_info.transAxes)
            info_texts.append(text)
 
    ax_info.text(0.5, 0.02, "By:Killerzeno", color='white', ha='center',
                 fontsize=14, fontweight='bold', style='italic',
                 transform=ax_info.transAxes)
    return info_texts
 
 
info_texts = init_info_panel()
 
# 初始化摄像头
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
 
# 边缘检测参数(可调整)
EDGE_THRESHOLD1 = 50  # Canny边缘检测低阈值
EDGE_THRESHOLD2 = 150  # Canny边缘检测高阈值
 
# 背景减法器用于运动检测
fgbg = cv2.createBackgroundSubtractorMOG2(history=100, varThreshold=16, detectShadows=False)
 
# 用于扫描动画的变量
current_angle = 0
scan_speed = 5
mosquito_count = 0
max_mosquito_count = 0
false_positives = 0
true_positives = 0
 
 
# 蚊子轨迹类
class MosquitoTrack:
    def __init__(self, id, x, y, time):
        self.id = id
        self.positions = [(x, y, time)]
        self.speeds = []
        self.directions = []
        self.last_update = time
        self.active = True
 
    def update(self, x, y, time):
        dx = x - self.positions[-1][0]
        dy = y - self.positions[-1][1]
        dt = time - self.last_update
        if dt > 0:
            speed = np.sqrt(dx ** 2 + dy ** 2) / dt
            direction = np.degrees(np.arctan2(dy, dx))
            self.speeds.append(speed)
            self.directions.append(direction)
 
        self.positions.append((x, y, time))
        self.last_update = time
        self.active = True
 
    def get_current_speed(self):
        return np.mean(self.speeds[-3:]) if len(self.speeds) > 0 else 0
 
    def get_current_direction(self):
        if len(self.directions) > 0:
            return self.directions[-1]
        return None
 
 
tracks = []
next_id = 1
 
 
def play_anti_mosquito_sound():
    if system_state.auto_sound and mosquito_sound:
        current_time = time.time()
        if current_time - system_state.last_sound_time > 5:
            try:
                mosquito_sound.play()
                system_state.last_sound_time = current_time
                system_state.sound_playing = True
            except Exception as e:
                print(f"播放音频失败: {e}")
 
 
def take_screenshot():
    screenshot_dir = "screenshots"
    if not os.path.exists(screenshot_dir):
        os.makedirs(screenshot_dir)
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"{screenshot_dir}/screenshot_{system_state.screenshot_count}_{timestamp}.png"
    plt.savefig(filename)
    system_state.screenshot_count += 1
    print(f"截图已保存: {filename}")
 
 
def update_radar(frame):
    global current_angle, r, mosquito_count, max_mosquito_count
    global false_positives, true_positives, tracks, next_id
    global scan_arc, scan_arc_fill, trail_lines
 
    current_time = time.time()
 
    # 转换为灰度图像
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
 
    # 应用背景减法检测运动
    fgmask = fgbg.apply(gray)
 
    # 形态学操作
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
    fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_CLOSE, kernel)
 
    # 寻找轮廓
    contours, _ = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 
    # 检测到的蚊子位置
    current_detections = []
    mosquito_info = []
 
    for contour in contours:
        area = cv2.contourArea(contour)
        perimeter = cv2.arcLength(contour, True)
 
        if 5 < area < 150 and perimeter > 10:
            (x, y), radius = cv2.minEnclosingCircle(contour)
            if 2 < radius < 20:
                circularity = 4 * np.pi * area / (perimeter ** 2) if perimeter > 0 else 0
 
                if circularity > 0.5:
                    center_x = x - frame.shape[1] // 2
                    center_y = y - frame.shape[0] // 2
 
                    angle = np.arctan2(center_y, center_x) % (2 * np.pi)
                    distance = np.sqrt(center_x ** 2 + center_y ** 2)
                    distance = min(distance, max_distance)
 
                    current_detections.append((x, y, angle, distance, area, radius))
 
    # 多目标跟踪
    active_tracks = [t for t in tracks if t.active]
    matched = [False] * len(current_detections)
 
    for track in active_tracks:
        min_dist = float('inf')
        best_match = None
 
        for i, (x, y, _, _, _, _) in enumerate(current_detections):
            if not matched[i]:
                last_x, last_y, _ = track.positions[-1]
                dist = np.sqrt((x - last_x) ** 2 + (y - last_y) ** 2)
 
                if dist < 50 and dist < min_dist:
                    min_dist = dist
                    best_match = i
 
        if best_match is not None:
            x, y, angle, distance, area, radius = current_detections[best_match]
            track.update(x, y, current_time)
            matched[best_match] = True
            mosquito_info.append((angle, distance, track))
            true_positives += 1
        else:
            false_positives += 1
 
    # 创建新轨迹
    for i, (x, y, angle, distance, area, radius) in enumerate(current_detections):
        if not matched[i]:
            new_track = MosquitoTrack(next_id, x, y, current_time)
            tracks.append(new_track)
            mosquito_info.append((angle, distance, new_track))
            next_id += 1
            system_state.total_detected += 1
            system_state.detected_today += 1
 
    # 标记不活跃的轨迹
    for track in active_tracks:
        if current_time - track.last_update > 0.5:
            track.active = False
 
    # 更新雷达数据
    mosquito_count = len([t for t in tracks if t.active])
    max_mosquito_count = max(max_mosquito_count, mosquito_count)
 
    # 播放驱蚊声
    if mosquito_count > 0:
        play_anti_mosquito_sound()
 
    # 更新扫描线效果
    current_angle = (current_angle + scan_speed * 2) % 360  # 加快扫描速度
    scan_rad = np.radians(current_angle)
 
    # 主扫描线
    scan_line.set_data([scan_rad, scan_rad], [0, max_distance])
 
    # 扫描线尾迹
    trail_length = 30
    trail_angles = np.linspace(scan_rad - np.radians(trail_length), scan_rad, 10)
    scan_shadow.set_data(trail_angles, [max_distance] * 10)
 
    # 更新扇形扫描区域
    if scan_arc is not None:
        scan_arc.remove()
    if scan_arc_fill is not None:
        scan_arc_fill.remove()
 
    scan_width = 30
    scan_start = np.radians(current_angle - scan_width) % (2 * np.pi)
    scan_end = np.radians(current_angle + scan_width) % (2 * np.pi)
 
    # 扇形边框
    scan_theta = np.linspace(scan_start, scan_end, 50)
    scan_arc = ax_radar.plot(scan_theta, [max_distance] * 50,
                             color='lime', alpha=0.5, linewidth=1)[0]
 
    # 扇形填充(渐变效果)
    scan_r = np.linspace(0, max_distance, 50)
    scan_theta, scan_r = np.meshgrid(scan_theta, scan_r)
    scan_theta = scan_theta.flatten()
    scan_r = scan_r.flatten()
 
    angle_diff = np.abs((np.degrees(scan_theta) - current_angle + 180) % 360 - 180)
    alphas = np.where(angle_diff < scan_width, 1 - angle_diff / scan_width, 0)
    colors = np.zeros((len(scan_theta), 4))
    colors[:, 1] = 1.0
    colors[:, 3] = alphas * 0.1
 
    scan_arc_fill = ax_radar.scatter(scan_theta, scan_r, c=colors, s=2,
                                     edgecolors='none', zorder=0)
 
    # 更新蚊子标记
    if mosquito_info:
        angles, distances, tracks_info = zip(*mosquito_info)
        sizes = [50 + 30 * math.sin(time.time() * 10)] * len(angles)  # 脉冲效果
        colors = ['red' if t.active else 'orange' for t in tracks_info]
        mosquito_dots.set_offsets(np.column_stack([angles, distances]))
        mosquito_dots.set_sizes(sizes)
        mosquito_dots.set_color(colors)
    else:
        mosquito_dots.set_offsets(np.empty((0, 2)))
 
    # 更新轨迹线
    for line in trail_lines:
        line.remove()
    trail_lines = []
 
    if mosquito_info:
        for angle, distance, track in mosquito_info:
            if len(track.positions) > 1:
                # 主轨迹线
                history_angles = []
                history_distances = []
                for i in range(max(0, len(track.positions) - 5), len(track.positions)):
                    x, y, _ = track.positions[i]
                    center_x = x - frame.shape[1] // 2
                    center_y = y - frame.shape[0] // 2
                    angle = np.arctan2(center_y, center_x) % (2 * np.pi)
                    distance = np.sqrt(center_x ** 2 + center_y ** 2)
                    history_angles.append(angle)
                    history_distances.append(distance)
 
                if len(history_angles) > 1:
                    # 主轨迹线
                    main_line = ax_radar.plot(history_angles, history_distances,
                                              color='cyan', alpha=0.7,
                                              linewidth=1.5, zorder=5)[0]
                    trail_lines.append(main_line)
 
                    # 轨迹尾迹
                    trail_alpha = 0.3
                    for i in range(1, 4):
                        if len(history_angles) > i:
                            trail_line = ax_radar.plot(
                                history_angles[-i - 1:-i],
                                history_distances[-i - 1:-i],
                                color='white', alpha=trail_alpha,
                                linewidth=2 + i, zorder=4 - i)[0]
                            trail_lines.append(trail_line)
                            trail_alpha *= 0.7
 
    # 更新信息面板
    accuracy = true_positives / (true_positives + false_positives) * 100 if (
                                                                                    true_positives + false_positives) > 0 else 0
 
    info_texts[0].set_text(f"状态: 运行中")
    info_texts[1].set_text(f"扫描中")
    info_texts[2].set_text(f"摄像头: 开启")
    info_texts[3].set_text(f"启动: {system_state.start_time.strftime('%H:%M:%S')}")
 
    info_texts[4].set_text(f"当前: {mosquito_count}")
    info_texts[5].set_text(f"今日: {system_state.detected_today}")
    info_texts[6].set_text(f"最大: {max_mosquito_count}")
    info_texts[7].set_text(
        f"平均: {system_state.total_detected / ((time.time() - system_state.start_time.timestamp()) / 3600):.1f}/h")
 
    if mosquito_info:
        _, _, track = mosquito_info[0]
        speed = track.get_current_speed()
        direction = track.get_current_direction()
        dir_text = f"{direction:.1f}°" if direction is not None else "-"
 
        info_texts[8].set_text(f"速度: {speed:.1f} px/s")
        info_texts[9].set_text(f"大小: {track.positions[-1][2]:.1f} px")
        info_texts[10].set_text(f"方向: {dir_text}")
        info_texts[11].set_text(f"距离: {distance:.1f} cm")
    else:
        info_texts[8].set_text(f"速度: 0 px/s")
        info_texts[9].set_text(f"大小: 0 px")
        info_texts[10].set_text(f"方向: -")
        info_texts[11].set_text(f"距离: 0 cm")
 
    info_texts[12].set_text(f"追踪: {len(tracks)}")
    info_texts[13].set_text(f"历史: {system_state.total_detected}")
    info_texts[14].set_text(f"误报: {false_positives}")
    info_texts[15].set_text(f"准确率: {accuracy:.1f}%")
 
    sound_status = "开启" if system_state.auto_sound else "关闭"
    playing_status = "(播放中)" if system_state.sound_playing else ""
    info_texts[16].set_text(f"声波驱蚊: {sound_status} {playing_status}")
    info_texts[17].set_text(f"按A键切换")
    info_texts[18].set_text(f"截图: 按P键")
 
    return [scan_line, scan_shadow, mosquito_dots, scan_arc, scan_arc_fill,
            *trail_lines, *info_texts]
 
 
# 创建动画
def update(frame):
    ret, frame = cap.read()
    if not ret:
        return []
 
    frame = cv2.flip(frame, 1)
 
    # 灰度处理与边缘检测
    gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray_frame, (5, 5), 0)
    edges = cv2.Canny(blurred, EDGE_THRESHOLD1, EDGE_THRESHOLD2)
 
    # 寻找轮廓
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 
    # 创建空白图像用于绘制轮廓
    edge_display = np.zeros_like(frame)
    cv2.drawContours(edge_display, contours, -1, (0, 255, 0), 1)
 
    artists = update_radar(frame)
 
    # 显示窗口
    cv2.imshow('Mosquito Tracking', cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
    cv2.imshow('Edges', edge_display)  # 显示轮廓绘制结果
 
    key = cv2.waitKey(1)
    if key & 0xFF == ord('q'):
        ani.event_source.stop()
        cap.release()
        cv2.destroyAllWindows()
        plt.close('all')
        return []
    elif key & 0xFF == ord('a'):
        system_state.auto_sound = not system_state.auto_sound
    elif key & 0xFF == ord('p'):
        take_screenshot()
 
    if system_state.sound_playing and not pygame.mixer.get_busy():
        system_state.sound_playing = False
 
    return artists
 
 
# 开始动画
try:
    # 启动时自动播放一次声波
    if system_state.auto_sound and mosquito_sound:
        mosquito_sound.play()
        system_state.sound_playing = True
        system_state.last_sound_time = time.time()
 
    ani = FuncAnimation(fig, update, frames=None, interval=30, blit=True, cache_frame_data=False)
    plt.tight_layout()
    plt.show()
except Exception as e:
    print(f"程序错误: {e}")
finally:
    if 'cap' in locals() and cap.isOpened():
        cap.release()
    cv2.destroyAllWindows()
    plt.close('all')

五、下载地址:https://mxys.lanzoue.com/iQXxD2wpofcb    密码:fhzb

CDSY,CDSY.XYZ
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐