基于YoloV11和驱动级鼠标模拟实现Ai自瞄

本文将围绕基于 YoloV11 和驱动级鼠标实现 FPS 游戏 AI 自瞄展开阐述。
需要着重强调的是,本文内容仅用于学术研究和技术学习目的。严禁任何个人或组织将文中所提及的技术、方法及思路应用于违法行为,包括但不限于在各类游戏中实施作弊等违规操作。若因违反此声明而产生的一切法律后果,均与本文作者无关。

一、原理

AI 自瞄 是一种借助人工智能技术自动控制瞄准目标的功能,在 FPS(第一人称射击)游戏场景中,基于目标检测算法*(如 YOLO 系列)和驱动级模拟鼠标来实现 AI 自瞄*

  • 图像采集
    • 游戏画面截取:要实现 AI 自瞄,首先需要获取游戏画面信息。一般通过屏幕截图的方式来完成,在计算机系统层面,利用相关的图形 API(如 Windows 系统下的 GDI、DirectX 等)可以截取游戏窗口内的图像。截取到的画面将作为后续目标检测算法的输入数据。
    • 实时性要求:为了保证自瞄的及时性和准确性,图像采集需要具备较高的帧率,通常每秒要截取数十帧甚至上百帧的画面,以确保能够实时捕捉游戏中目标的动态变化。
  • 目标检测
    • 特征提取与模型训练:目标检测是 AI 自瞄的核心环节之一。以 YOLO(You Only Look Once)系列算法为例,其基本思想是将输入的图像划分为多个网格,然后通过卷积神经网络(CNN)对每个网格进行特征提取。在训练阶段,使用大量包含目标对象(如游戏中的敌人)的图像数据进行训练,让模型学习目标的特征和位置信息。训练好的模型能够识别出图像中目标的类别和位置。
    • 目标定位:在实际应用中,将采集到的游戏画面输入到训练好的目标检测模型中,模型会输出目标在图像中的位置信息,通常以边界框(bounding box)的形式表示,包含目标的左上角和右下角坐标,从而确定目标在画面中的具体位置。
  • 坐标转换
    • 图像坐标到屏幕坐标:目标检测模型输出的是目标在图像中的坐标,而要实现鼠标的自动瞄准,需要将这些图像坐标转换为屏幕上的实际坐标。这需要考虑图像在屏幕上的位置、缩放比例等因素。通过一定的数学变换公式,可以将图像坐标映射到屏幕坐标,从而确定目标在屏幕上的具体位置。
    • 分辨率适配:不同的游戏和显示器可能具有不同的分辨率,因此在进行坐标转换时,需要对不同的分辨率进行适配,确保在各种分辨率下都能准确地将目标的图像坐标转换为屏幕坐标。
  • 驱动级模拟鼠标
    • 鼠标控制原理:驱动级模拟鼠标是实现 AI 自瞄的关键步骤。在操作系统层面,鼠标的移动和点击操作是通过向系统发送特定的输入信号来实现的。驱动级模拟鼠标可以绕过游戏的输入检测机制,直接向操作系统发送鼠标控制信号,从而实现鼠标的自动移动和点击。
    • 精准控制 :根据目标在屏幕上的坐标,计算出鼠标需要移动的距离和方向,然后通过驱动级模拟鼠标技术,精确地控制鼠标移动到目标位置。同时,还可以根据游戏的实际情况,模拟鼠标的点击操作,实现自动射击。
      实时反馈与调整
    • 动态跟踪:在游戏中,目标对象通常是动态移动的,因此 AI 自瞄系统需要实时跟踪目标的位置变化。通过不断地采集游戏画面、进行目标检测和坐标转换,系统可以实时获取目标的最新位置信息,并及时调整鼠标的移动方向和距离,确保始终瞄准目标。
    • 误差修正:由于各种因素的影响,如网络延迟、图像采集误差等,可能会导致目标检测和坐标转换出现一定的误差。为了提高自瞄的准确性,系统需要具备误差修正机制,通过不断地反馈和调整,减小误差,使鼠标能够更加精准地瞄准目标。

二、代码实现

1.实现图像识别

  • 实时获取游戏图像

    • 使用Mss截图方式,提高截图速度,并使用多线程加快截图和图像检测的速度
    python 复制代码
      import time
      import threading
      import mss
      import numpy as np
      import cv2
      import threading
      
      def screenshot_thread(width, height, shift_x, screenshot_interval, lock, latest_screenshot):
          last_screenshot_time = time.time()
          with mss.mss() as sct:
              monitor = sct.monitors[0]
              start_x = (monitor["width"] - width) // 2 + shift_x
              start_y = (monitor["height"] - height) // 2
              monitor_area = {
                  "left": start_x,
                  "top": start_y,
                  "width": width,
                  "height": height
              }
              while True:
                  current_time = time.time()
                  if current_time - last_screenshot_time > screenshot_interval:
                      try:
                          sct_img = sct.grab(monitor_area)
                          screenshot_np = np.array(sct_img)
                          screenshot_np = cv2.cvtColor(screenshot_np, cv2.COLOR_BGRA2BGR)
                          with lock:
                              latest_screenshot = screenshot_np
                          last_screenshot_time = current_time
                      except Exception as e:
                          print(f"截图时发生错误: {e}")
                  time.sleep(0.001)
  • 目标检测

    • 使用本地已训练好的模型进行检测,并将检测结果显示在一个框内
    • 考虑到部分游戏全屏后会出现游戏画面默认置顶的情况,所以我们使用win32模块将小窗口强制置顶在游戏画面之上,便于我们观察
    python 复制代码
      import time
      import cv2
      import numpy as np
      import win32gui
      import win32con
      import mss
      from ultralytics import YOLO
      
      
      def detect_and_process(model, width, height, shift_x, window_name, lock, latest_screenshot, target_center_coordinates,
                             shift_pressed, coordinate_smoother, logitech):
          history_boxes = []
          history_confidences = []
          history_size = 5
          topmost_interval = 1
          last_topmost_time = time.time()
      
          with mss.mss() as sct:
              monitor = sct.monitors[0]
              start_x = (monitor["width"] - width) // 2 + shift_x
              start_y = (monitor["height"] - height) // 2
      
          while True:
              current_time = time.time()
              if current_time - last_topmost_time > topmost_interval:
                  hwnd = win32gui.FindWindow(None, window_name)
                  if hwnd != 0:
                      win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
                  last_topmost_time = current_time
      
              with lock:
                  if latest_screenshot is None:
                      continue
                  screenshot_np = latest_screenshot.copy()
      
              try:
                  results = model(screenshot_np, verbose=False, half=True)
              except Exception as e:
                  print(f"目标检测时发生错误: {e}")
                  continue
      
              max_conf = 0
              best_box = None
              drawn_y_ranges = []
      
              for result in results:
                  boxes = result.boxes.cpu().numpy()
                  for box in boxes:
                      x1, y1, x2, y2 = box.xyxy[0].astype(int)
                      is_overlap = any(y1 < y2_ and y2 > y1_ for y1_, y2_ in drawn_y_ranges)
                      if is_overlap:
                          continue
      
                      conf = box.conf[0]
                      history_confidences.append(conf)
                      if len(history_confidences) > history_size:
                          history_confidences.pop(0)
                      smoothed_conf = np.mean(history_confidences)
      
                      if smoothed_conf > max_conf:
                          max_conf = smoothed_conf
                          best_box = box
                      drawn_y_ranges.append((y1, y2))
      
              result_image = screenshot_np.copy()
      
              if best_box is not None:
                  x1, y1, x2, y2 = best_box.xyxy[0].astype(int)
                  current_box = np.array([x1, y1, x2, y2])
                  history_boxes.append(current_box)
                  if len(history_boxes) > history_size:
                      history_boxes.pop(0)
                  avg_box = np.mean(history_boxes, axis=0).astype(int)
                  x1, y1, x2, y2 = avg_box
      
                  cls = int(best_box.cls[0])
                  class_name = results[0].names[cls]
                  conf = max_conf
      
                  cv2.rectangle(result_image, (x1, y1), (x2, y2), (0, 255, 0), 2)
                  label = f'{class_name}: {conf:.2f}'
                  cv2.putText(result_image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
      
                  x_center = (x1 + x2) // 2
                  y_center = (y1 + y2) // 2
      
                  screen_x_center = start_x + x_center
                  screen_y_center = start_y + y_center
      
                  target_center_coordinates.append((screen_x_center, screen_y_center))
                  if len(target_center_coordinates) > 100:
                      target_center_coordinates.pop(0)
      
                  if shift_pressed:
                      smoothed_x, smoothed_y = coordinate_smoother.smooth_coordinate(screen_x_center, screen_y_center)
                      logitech.mouse_move((smoothed_x, smoothed_y))
      
              cv2.imshow(window_name, result_image)
      
              if cv2.waitKey(1) & 0xFF == ord('q'):
                  break
  • 目标敌人的坐标

    • 我们以屏幕左上角定为坐标(0,0)鼠标坐标为屏幕中心(960,540)实时获取目标中心的坐标点,计算鼠标到目标坐标点的x,y轴距离,转化为数值存储在一个数组中,便于鼠标移动模块实时调用
    python 复制代码
      target_center_coordinates = []
      
      def store_target_center_coordinates(x_center, y_center):
          """
          存储目标中心坐标点,并控制列表长度不超过 100
          :param x_center: 目标中心的 x 坐标
          :param y_center: 目标中心的 y 坐标
          """
          target_center_coordinates.append((x_center, y_center))
          if len(target_center_coordinates) > 100:
              target_center_coordinates.pop(0)
      
      def get_target_center_coordinates():
          """
          获取存储的目标中心坐标点列表
          :return: 目标中心坐标点列表
          """
          return target_center_coordinates
  • 将以上的图像识别并输出坐标制作成一个py文件,封装成一个目标检测模块
    *

    python 复制代码
      import numpy as np
      import cv2
      from ultralytics import YOLO
      import win32gui
      import win32con
      import time
      import threading
      import mss
      import os
      from pynput import keyboard
      import ctypes
      import pyautogui
      import torch
      # PID 控制器类
      class PID:
          def __init__(self, P=0.2, I=0.01, D=0.1):
              self.kp, self.ki, self.kd = P, I, D
              self.uPrevious, self.uCurent = 0, 0
              self.setValue, self.lastErr, self.errSum = 0, 0, 0
              self.errSumLimit = 10
      
          def pidPosition(self, setValue, curValue):
              err = setValue - curValue
              dErr = err - self.lastErr
              self.errSum += err
              outPID = self.kp * err + self.ki * self.errSum + self.kd * dErr
              self.lastErr = err
              return outPID
      
      # 罗技驱动类
      class LOGITECH:
          def __init__(self):
              self.dll = None
              self.state = False
              self.load_dll()
      
          def load_dll(self):
              try:
                  dll_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logitech.driver.dll')
                  self.dll = ctypes.CDLL(dll_path)
                  self.dll.device_open.restype = ctypes.c_int
                  result = self.dll.device_open()
                  self.state = (result == 1)
                  self.dll.moveR.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_bool]
                  self.dll.moveR.restype = None
              except FileNotFoundError:
                  print(f'错误, 找不到 DLL 文件')
              except OSError as e:
                  print(f'错误, 加载 DLL 文件失败: {e}')
      
          def mouse_move(self, end_xy, min_xy=2):
              if not self.state:
                  return
      
              end_x, end_y = end_xy
              pid_x = PID()
              pid_y = PID()
      
              new_x, new_y = pyautogui.position()
              move_x = pid_x.pidPosition(end_x, new_x)
              move_y = pid_y.pidPosition(end_y, new_y)
      
              move_x = np.clip(move_x, -min_xy if move_x < 0 else min_xy, None).astype(int)
              move_y = np.clip(move_y, -min_xy if move_y < 0 else min_xy, None).astype(int)
      
              self.dll.moveR(move_x, move_y, True)
      
      # 平滑坐标的函数,使用简单移动平均
      class CoordinateSmoother:
          def __init__(self, window_size=5):
              self.window_size = window_size
              self.x_buffer = []
              self.y_buffer = []
      
          def smooth_coordinate(self, x, y):
              self.x_buffer.append(x)
              self.y_buffer.append(y)
      
              if len(self.x_buffer) > self.window_size:
                  self.x_buffer.pop(0)
                  self.y_buffer.pop(0)
      
              smoothed_x = int(np.mean(self.x_buffer))
              smoothed_y = int(np.mean(self.y_buffer))
      
              return smoothed_x, smoothed_y
      
      # 全局变量
      target_center_coordinates = []
      latest_screenshot = None
      lock = threading.Lock()
      logitech = LOGITECH()
      coordinate_smoother = CoordinateSmoother()
      shift_pressed = False
      
      # 初始化模型和窗口
      def init_model_and_window(model_path, window_name, width, height, shift_x):
          model_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), model_path)
          if not os.path.exists(model_path):
              print(f"模型文件 {model_path} 不存在,请检查。")
              return None, None
      
          model = YOLO(model_path)
          model.fuse()
          device = 'cuda' if torch.cuda.is_available() else 'cpu'
          model.to(device)
      
          cv2.namedWindow(window_name, cv2.WINDOW_NORMAL)
          cv2.resizeWindow(window_name, width, height)
          time.sleep(0.1)
          hwnd = win32gui.FindWindow(None, window_name)
          win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
          return model, hwnd
      
      # 截图线程函数
      def screenshot_thread(width, height, shift_x, screenshot_interval):
          global latest_screenshot
          last_screenshot_time = time.time()
          with mss.mss() as sct:
              monitor = sct.monitors[0]
              start_x = (monitor["width"] - width) // 2 + shift_x
              start_y = (monitor["height"] - height) // 2
              monitor_area = {
                  "left": start_x,
                  "top": start_y,
                  "width": width,
                  "height": height
              }
              while True:
                  current_time = time.time()
                  if current_time - last_screenshot_time > screenshot_interval:
                      try:
                          sct_img = sct.grab(monitor_area)
                          screenshot_np = np.array(sct_img)
                          screenshot_np = cv2.cvtColor(screenshot_np, cv2.COLOR_BGRA2BGR)
                          with lock:
                              latest_screenshot = screenshot_np
                          last_screenshot_time = current_time
                      except Exception as e:
                          print(f"截图时发生错误: {e}")
                  time.sleep(0.001)
      
      # 实时存放目标中心坐标点的函数
      def store_target_center_coordinates(x_center, y_center):
          global target_center_coordinates
          target_center_coordinates.append((x_center, y_center))
          if len(target_center_coordinates) > 100:
              target_center_coordinates.pop(0)
      
      # 目标检测和处理函数
      def detect_and_process(model, width, height, shift_x, window_name):
          history_boxes = []
          history_confidences = []
          history_size = 5
          topmost_interval = 1
          last_topmost_time = time.time()
      
          with mss.mss() as sct:
              monitor = sct.monitors[0]
              start_x = (monitor["width"] - width) // 2 + shift_x
              start_y = (monitor["height"] - height) // 2
      
          while True:
              current_time = time.time()
              if current_time - last_topmost_time > topmost_interval:
                  hwnd = win32gui.FindWindow(None, window_name)
                  if hwnd != 0:
                      win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
                  last_topmost_time = current_time
      
              with lock:
                  if latest_screenshot is None:
                      continue
                  screenshot_np = latest_screenshot.copy()
      
              try:
                  results = model(screenshot_np, verbose=False, half=True)
              except Exception as e:
                  print(f"目标检测时发生错误: {e}")
                  continue
      
              max_conf = 0
              best_box = None
              drawn_y_ranges = []
      
              for result in results:
                  boxes = result.boxes.cpu().numpy()
                  for box in boxes:
                      x1, y1, x2, y2 = box.xyxy[0].astype(int)
                      is_overlap = any(y1 < y2_ and y2 > y1_ for y1_, y2_ in drawn_y_ranges)
                      if is_overlap:
                          continue
      
                      conf = box.conf[0]
                      history_confidences.append(conf)
                      if len(history_confidences) > history_size:
                          history_confidences.pop(0)
                      smoothed_conf = np.mean(history_confidences)
      
                      if smoothed_conf > max_conf:
                          max_conf = smoothed_conf
                          best_box = box
                      drawn_y_ranges.append((y1, y2))
      
              result_image = screenshot_np.copy()
      
              if best_box is not None:
                  x1, y1, x2, y2 = best_box.xyxy[0].astype(int)
                  current_box = np.array([x1, y1, x2, y2])
                  history_boxes.append(current_box)
                  if len(history_boxes) > history_size:
                      history_boxes.pop(0)
                  avg_box = np.mean(history_boxes, axis=0).astype(int)
                  x1, y1, x2, y2 = avg_box
      
                  cls = int(best_box.cls[0])
                  class_name = results[0].names[cls]
                  conf = max_conf
      
                  cv2.rectangle(result_image, (x1, y1), (x2, y2), (0, 255, 0), 2)
                  label = f'{class_name}: {conf:.2f}'
                  cv2.putText(result_image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
      
                  x_center = (x1 + x2) // 2
                  y_center = (y1 + y2) // 2
      
                  screen_x_center = start_x + x_center
                  screen_y_center = start_y + y_center
      
                  store_target_center_coordinates(screen_x_center, screen_y_center)
      
                  if shift_pressed:
                      smoothed_x, smoothed_y = coordinate_smoother.smooth_coordinate(screen_x_center, screen_y_center)
                      logitech.mouse_move((smoothed_x, smoothed_y))
      
              cv2.imshow(window_name, result_image)
      
              if cv2.waitKey(1) & 0xFF == ord('q'):
                  break
      
      # 封装获取目标中心坐标的函数
      def get_target_center_coordinates():
          global target_center_coordinates
          return target_center_coordinates
      
      # 键盘事件处理函数
      def on_press(key):
          global shift_pressed
          if key == keyboard.Key.shift:
              shift_pressed = True
      
      def on_release(key):
          global shift_pressed
          if key == keyboard.Key.shift:
              shift_pressed = False
          if key == keyboard.Key.esc:
              return False
      
      def main():
          model_path = 'best.pt'
          window_name = 'Detection Result'
          width, height = 320, 320
          shift_x = 120
          screenshot_interval = 0.1
      
          import torch  # 导入 torch 库
          model, hwnd = init_model_and_window(model_path, window_name, width, height, shift_x)
          if model is None:
              return
      
          screenshot_t = threading.Thread(target=screenshot_thread, args=(width, height, shift_x, screenshot_interval))
          screenshot_t.daemon = True
          screenshot_t.start()
      
          keyboard_listener = keyboard.Listener(on_press=on_press, on_release=on_release)
          keyboard_listener.start()
      
          try:
              detect_and_process(model, width, height, shift_x, window_name)
          except KeyboardInterrupt:
              print("程序被用户手动中断。")
          except Exception as e:
              print(f"程序运行过程中发生错误: {e}")
          finally:
              cv2.destroyAllWindows()
              keyboard_listener.stop()
              print("存储的目标中心坐标点:", get_target_center_coordinates())
      
      if __name__ == "__main__":
          main()

注意:一定要安装python所需的依赖库,最好是在虚拟环境中运行(如Anaconda)

2.驱动级鼠标移动

python 复制代码
  import os
  import ctypes
  import pyautogui
  import time
  from math import sqrt
  from screenshot import get_target_center_coordinates
  from pynput import keyboard
  import tkinter as tk
  from tkinter import ttk
  
  
  class PID:
      def __init__(self, P=2.0, I=0.02, D=0.5):  # 增大 P 值以加快响应速度
          self.kp, self.ki, self.kd = P, I, D
          self.uPrevious, self.uCurent = 0, 0
          self.setValue, self.lastErr, self.errSum = 0, 0, 0
          self.errSumLimit = 10
  
      def pidPosition(self, setValue, curValue):
          err = setValue - curValue
          dErr = err - self.lastErr
          self.errSum += err
          outPID = self.kp * err + self.ki * self.errSum + self.kd * dErr
          self.lastErr = err
          return outPID
  
  
  class LOGITECH:
      def __init__(self):
          self.dll = None
          self.state = False
          self.load_dll()
  
      def load_dll(self):
          try:
              file_path = os.path.abspath(os.path.dirname(__file__))
              self.dll = ctypes.CDLL(f'{file_path}/logitech.driver.dll')
              self.dll.device_open.restype = ctypes.c_int
              result = self.dll.device_open()
              self.state = (result == 1)
              self.dll.moveR.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_bool]
              self.dll.moveR.restype = None
          except FileNotFoundError:
              print(f'错误, 找不到 DLL 文件')
          except OSError as e:
              print(f'错误, 加载 DLL 文件失败: {e}')
  
      def mouse_move(self, end_xy, min_xy=0.5, distance_threshold=5, max_x_move=500, max_y_move=400):
          if not self.state:
              return
  
          end_x, end_y = end_xy
          new_x, new_y = pyautogui.position()
  
          # 计算目标坐标与当前鼠标坐标的距离
          distance = sqrt((end_x - new_x) ** 2 + (end_y - new_y) ** 2)
          print(f"目标坐标与当前鼠标坐标的距离: {distance}")
  
          # 如果距离小于阈值,不进行移动
          if distance < distance_threshold:
              print("距离小于阈值,不进行移动")
              return
  
          pid_x = PID(P=sensitivity)
          pid_y = PID(P=sensitivity)
  
          move_x = pid_x.pidPosition(end_x, new_x)
          move_y = pid_y.pidPosition(end_y, new_y)
  
          # 限制 x 轴移动范围
          move_x = max(-max_x_move, min(max_x_move, move_x))
          # 限制 y 轴移动范围
          move_y = max(-max_y_move, min(max_y_move, move_y))
  
          move_x = max(min_xy, move_x) if move_x > 0 else min(-min_xy, move_x) if move_x < 0 else int(move_x)
          move_y = max(min_xy, move_y) if move_y > 0 else min(-min_xy, move_y) if move_y < 0 else int(move_y)
  
          move_x = int(move_x)
          move_y = int(move_y)
  
          print(f"传递给 moveR 的参数: x={move_x}, y={move_y}")
          result = self.dll.moveR(move_x, move_y, True)
          print(f"moveR 函数的返回值: {result}")
  
  
  logitech = LOGITECH()
  aiming = False  # 新增状态变量,用于跟踪是否正在瞄准
  sensitivity = 2.0  # 初始化灵敏度
  
  
  def move_mouse_to_coordinate(end_xy):
      logitech.mouse_move(end_xy)
  
  
  def on_press(key):
      global aiming
      try:
          if key == keyboard.Key.shift:
              aiming = True  # 按下 Shift 键,开始瞄准
      except AttributeError:
          pass
  
  
  def on_release(key):
      global aiming
      if key == keyboard.Key.shift:
          aiming = False  # 松开 Shift 键,停止瞄准
      if key == keyboard.Key.esc:
          return False
  
  
  def update_sensitivity(value):
      global sensitivity
      sensitivity = float(value)
      print(f"当前灵敏度: {sensitivity}")
  
  
  # 创建 Tkinter 窗口
  root = tk.Tk()
  root.title("灵敏度调整")
  
  # 创建滑动条
  sensitivity_scale = ttk.Scale(root, from_=0.1, to=5.0, orient=tk.HORIZONTAL,
                                command=update_sensitivity, value=sensitivity)
  sensitivity_scale.pack(pady=20)
  
  # 启动 Tkinter 窗口的事件循环
  root.update()
  root.withdraw()  # 隐藏窗口,避免干扰
  
  
  with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
      try:
          while True:
              if aiming:
                  # 获取目标中心坐标
                  coordinates = get_target_center_coordinates()
                  if coordinates:
                      print(coordinates)
                      # 假设 coordinates 是一个包含 (x, y) 坐标的元组或列表
                      move_mouse_to_coordinate(coordinates)
              time.sleep(0.05)  # 缩短循环时间间隔
              root.update()  # 更新 Tkinter 窗口
      except KeyboardInterrupt:
          print("程序已被手动中断,正在退出...")
          root.destroy()  # 关闭 Tkinter 窗口

3.启动文件

  • 制作启动文件有利于隐藏其他两个文件的进程,可以有效防止系统检测
python 复制代码
  import subprocess
  import os
  import sys
  import logging
  import ctypes
  
  # 配置日志记录
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
  
  # 定义 Windows API 相关常量和函数
  PROCESS_ALL_ACCESS = 0x1F0FFF
  TH32CS_SNAPPROCESS = 0x00000002
  CreateToolhelp32Snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapshot
  Process32First = ctypes.windll.kernel32.Process32First
  Process32Next = ctypes.windll.kernel32.Process32Next
  OpenProcess = ctypes.windll.kernel32.OpenProcess
  TerminateProcess = ctypes.windll.kernel32.TerminateProcess
  CloseHandle = ctypes.windll.kernel32.CloseHandle
  
  
  class PROCESSENTRY32(ctypes.Structure):
      _fields_ = [("dwSize", ctypes.c_ulong),
                  ("cntUsage", ctypes.c_ulong),
                  ("th32ProcessID", ctypes.c_ulong),
                  ("th32DefaultHeapID", ctypes.POINTER(ctypes.c_ulong)),
                  ("th32ModuleID", ctypes.c_ulong),
                  ("cntThreads", ctypes.c_ulong),
                  ("th32ParentProcessID", ctypes.c_ulong),
                  ("pcPriClassBase", ctypes.c_long),
                  ("dwFlags", ctypes.c_ulong),
                  ("szExeFile", ctypes.c_char * 260)]
  
  
  def get_script_paths():
      """
      获取 script1.py 和 script2.py 的文件路径
      """
      current_dir = os.path.dirname(os.path.abspath(__file__))
      script1_path = os.path.join(current_dir, 'detection', 'screenshot.py')
      script2_path = os.path.join(current_dir, 'detection', 'logihub.py')
      return script1_path, script2_path
  
  
  def run_hidden(script_path):
      """
      以隐藏窗口的方式异步启动 Python 脚本,并尽量隐藏进程
      """
      startupinfo = None
      if sys.platform.startswith('win'):
          startupinfo = subprocess.STARTUPINFO()
          startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
          startupinfo.wShowWindow = subprocess.SW_HIDE
      try:
          process = subprocess.Popen(['python', script_path], startupinfo=startupinfo)
          logging.info(f"成功启动脚本: {script_path}")
          # 这里可以添加更复杂的隐藏进程逻辑,比如修改进程的可见性属性等
          return process
      except FileNotFoundError:
          logging.error("Python 解释器未找到,请确保 Python 已正确安装并配置到系统环境变量中。")
          return None
      except Exception as e:
          logging.error(f"启动脚本 {script_path} 时发生其他错误: {e}")
          return None
  
  
  def main():
      """
      主函数,负责启动两个脚本并等待它们执行完毕
      """
      script1_path, script2_path = get_script_paths()
      logging.info(f"正在启动 {script1_path} 和 {script2_path}")
  
      process1 = run_hidden(script1_path)
      process2 = run_hidden(script2_path)
  
      if process1 and process2:
          logging.info(f"{script1_path} 和 {script2_path} 已成功启动")
          try:
              # 等待两个进程执行完毕
              process1.wait()
              process2.wait()
              logging.info(f"{script1_path} 和 {script2_path} 执行完毕")
          except Exception as e:
              logging.error(f"等待进程执行时发生错误: {e}")
      else:
          logging.error("启动脚本时出现问题")
  
  
  if __name__ == "__main__":
      main()

三、开源

此项目已在GitCode平台开源可以使用作者上传的项目直接启动

  • 使用Git克隆仓库中的文件
    *

    bash 复制代码
      git clone https://gitcode.com/2401_86455622/MRAI.git

衷心感谢各位读者拨冗阅读本文。若您在阅读过程中发现任何问题,或有相关疑问需要探讨,烦请在评论区留言,我将认真对待每一条反馈并及时予以回应。

相关推荐
豆豆酱5 分钟前
强化学习到大模型训练理论概要(一)
人工智能·算法
山海青风6 分钟前
OpenAI 实战进阶教程 - 第十二节 : 多模态任务开发(文本、图像、音频)
图像处理·人工智能·python·ai作画·音视频·语音识别
2501_9044477430 分钟前
荣耀已接入DeepSeek-R1,荣耀手机系统版本MagicOS8.0及以上用户可用
人工智能·智能手机·virtualenv·scikit-learn·tornado
LaughingZhu38 分钟前
PH热榜 | 2025-02-10
人工智能·经验分享·产品运营
不爱原创的Yoga1 小时前
【AI】人工智能与搜索引擎知识了解
人工智能·搜索引擎
shadowcz0071 小时前
Open-Interface:基于大语言模型 LLM 的自动化界面操作系统
运维·人工智能·语言模型·自然语言处理·自动化
岁月如歌,青春不败1 小时前
DeepSeek与GPT大语言模型教程
人工智能·python·gpt·深度学习·机器学习·语言模型·deepseek
小赖同学啊1 小时前
RPA与深度学习结合
人工智能·深度学习·rpa
测试者家园1 小时前
设计高效的测试用例:从需求到验证
自动化测试·软件测试·人工智能·测试用例·测试策略·质量效能
人机与认知实验室1 小时前
AI算力的摆脱有点像发动机汽车变电动车
人工智能·汽车