基于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截图方式,提高截图速度,并使用多线程加快截图和图像检测的速度
    •   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模块将小窗口强制置顶在游戏画面之上,便于我们观察
    •   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轴距离,转化为数值存储在一个数组中,便于鼠标移动模块实时调用
    •   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文件,封装成一个目标检测模块
    •   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.驱动级鼠标移动

  •   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.启动文件

  • 制作启动文件有利于隐藏其他两个文件的进程,可以有效防止系统检测
  •   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克隆仓库中的文件
    •   git clone https://gitcode.com/2401_86455622/MRAI.git
      

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/967477.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

示例代码:C# MQTTS双向认证(客户端)(服务器EMQX)

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

记录IMX6ULL开发板上移植SQLite3并运行Qt程序

文章目录 概要移植SQLite3Qt程序部署实验现象 概要 基于上一章对使用Qt运行对应的实验实例来完成对用户使用ui界面完成对SQLite数据库的增删改查等操作。本文旨在对上一句节的Qt程序部署到IMX6ULL开发板&#xff0c;并且完成对SQLite数据库在IMX6ULL开发板上的移植。 移植SQ…

达梦数据库(DM)线程管理

目录标题 达梦数据库&#xff08;DM&#xff09;线程管理笔记一、DM 线程架构概述二、DM 主要线程类型及功能&#xff08;一&#xff09;监听线程&#xff08;二&#xff09;工作线程&#xff08;三&#xff09;IO 线程&#xff08;四&#xff09;调度线程&#xff08;五&#…

02.10 TCP之文件传输

1.思维导图 2.作业 服务器代码&#xff1a; #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h> …

Node.js 环境配置

什么是 Node.js Node.js 是一个基于 Chrome V8 JavaScript 引擎的 JavaScript 运行时环境&#xff0c;它允许你在服务器端运行 JavaScript。传统上&#xff0c;JavaScript 主要用于浏览器中的前端开发&#xff0c;而 Node.js 使得 JavaScript 也能够在服务器上执行&#xff0c;…

【办公类-53-04】20250209Python模仿制作2024学年第二学期校历

背景需求&#xff1a; 马上开学了&#xff0c;又要制作校历&#xff08;删划节假日&#xff09;。之前我都是用网络的图片&#xff0c;然后在PPT里修改。 存在问题&#xff1a; 网络校历是从周日开始的&#xff0c;但日常我们老师做教案&#xff0c;都是默认从周一到周五&…

KERL文献阅读分享:知识图谱与预训练语言模型赋能会话推荐系统

标题期刊年份Knowledge Graphs and Pre-trained Language Models enhanced Representation Learning for Conversational Recommender SystemsJournal of LaTeX Class Files2021 &#x1f4c8;研究背景 在数字时代&#xff0c;个性化推荐系统已经成为了我们生活的一部分。从电…

强一致性算法:Raft

目录 什么是 Raft 算法&#xff1f; Leader的选举 投票分裂后的选举过程 Raft算法日志复制过程 修复不一样的日志 数据安全性的保证 什么是 Raft 算法&#xff1f; Raft 算法是一种是一种用于管理复制日志的强一致性算法&#xff0c;用于保证分布式系统中节点数据的一致…

[MyabtisPlus]PG的TIMESTAMPTZ不支持转换为LocalDateTime

背景 数据库用的是PG&#xff0c;且created_time字段用的是带时区的timestamptz类型&#xff1a; 用MyabtisPlus(MP)的的代码生成&#xff0c;默认生成的是JDK8的LocalDateTime类型&#xff1a; 结果&#xff0c;在查询时候&#xff0c;无法做到实体类的类型自动转换&#xff0…

cliproxy代理服务使用指南

Cliproxy代理服务使用指南 一、引言 Cliproxy&#xff0c;作为一款高效稳定的代理服务工具&#xff0c;广泛应用于跨境电商、数据分析、网络爬虫、远程办公等领域。本指南旨在帮助用户快速上手Cliproxy&#xff0c;充分利用其代理服务&#xff0c;提升工作效率与数据安全。 二、…

【Java 面试 八股文】Redis篇

Redis 1. 什么是缓存穿透&#xff1f;怎么解决&#xff1f;2. 你能介绍一下布隆过滤器吗&#xff1f;3. 什么是缓存击穿&#xff1f;怎么解决&#xff1f;4. 什么是缓存雪崩&#xff1f;怎么解决&#xff1f;5. redis做为缓存&#xff0c;mysql的数据如何与redis进行同步呢&…

防火墙术语大全( Firewalld Glossary of Terms)

防火墙术语大全 防火墙作为网络安全中不可或缺的设备&#xff0c;在各种网络架构中扮演着至关重要的角色。无论是企业级防火墙、云防火墙还是家用路由器内置的防火墙&#xff0c;它们的工作原理和配置策略都离不开一系列专业术语的支撑。对于网络工程师来说&#xff0c;掌握这…

【蓝耘元生代智算云平台】一键部署 DeepSeek人工智能模型

欢迎来到ZyyOvO的博客✨&#xff0c;一个关于探索技术的角落&#xff0c;记录学习的点滴&#x1f4d6;&#xff0c;分享实用的技巧&#x1f6e0;️&#xff0c;偶尔还有一些奇思妙想&#x1f4a1; 本文由ZyyOvO原创✍️&#xff0c;感谢支持❤️&#xff01;请尊重原创&#x1…

配置@别名路径,把@/ 解析为 src/

路径解析配置 webpack 安装 craco npm i -D craco/craco 项目根目录下创建文件 craco.config.js &#xff0c;内容如下 const path require(path) module.exports {webpack: {// 配置别名alias: {// 约定&#xff1a; 使用 表示src文件所在路径: path.resolve(__dirname,src)…

力扣hot100刷题第一天

哈希 1. 两数之和 题目 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用两次相同的元素。你可以按任意…

【前端】几种常见的跨域解决方案代理的概念

几种常见的跨域解决方案&代理的概念 一、常见的跨域解决方案1. 服务端配置CORS&#xff08;Cross-Origin Resource Sharing&#xff09;&#xff1a;2. Nginx代理3. Vue CLI配置代理&#xff1a;4 .uni-app在manifest.json中配置代理来解决&#xff1a;5. 使用WebSocket通讯…

以下是基于巨控GRM241Q-4I4D4QHE模块的液位远程控制系统技术方案:

以下是基于巨控GRM241Q-4I4D4QHE模块的液位远程控制系统技术方案&#xff1a; 一、系统概述 本系统采用双巨控GRM241Q模块构建4G无线物联网络&#xff0c;实现山上液位数据实时传输至山下水泵站&#xff0c;通过预设逻辑自动控制水泵启停&#xff0c;同时支持APP远程监控及人工…

百度高德地图坐标转换

百度地图和高德地图的侧重点不太一样。同样一个地名&#xff0c;在百度地图网站上搜索到的地点可能是商业网点&#xff0c;在高德地图网站上搜索到的地点可能是自然行政地点。 高德地图api 在高德地图中&#xff0c;搜索地名&#xff0c;如“乱石头川”&#xff0c;该地名会出…

Photoshop自定义键盘快捷键

编辑 - 键盘快捷键 CtrlShiftAltK 把画笔工具改成Q , 橡皮擦改成W , 涂抹工具改成E , 增加和减小画笔大小A和S 偏好设置 - 透明度和色域 设置一样颜色 套索工具 可以自定义套选一片区域 Shiftf5 填充 CtrlU 可以改颜色/色相/饱和度 CtrlE 合并图层 CtrlShiftS 另存…

carbon 加入 GitCode:Golang 时间处理的 “瑞士军刀”

在 Golang 的开发生态中&#xff0c;时间处理领域长期存在着诸多挑战。高效、精准的时间处理对于各类软件应用的稳定运行与功能拓展至关重要。近日&#xff0c;carbon 正式加入 GitCode&#xff0c;为 Golang 开发者带来一款强大且便捷的时间处理利器&#xff0c;助力项目开发迈…