Привет. В этой статье описано как я делал чит для PUBG на основе компьютерного зрения YOLO11 и наводки через нейросеть HumanizedTrajectoryGen.onnx, какие проблемы возникали в ходе работы и как получалось их решать.
Открыть видео / медиа
Но в ходе работы заметил что YOLO не сильно нагружает систему и боксы «не поспевают» за реальным положением врага на экране.

Эта проблема будет сохраняться всегда и упирается она в мощности GPU, параметры инференса YOLO и код. В моем случае инференс занимает примерно 8-15мс, помимо этого есть другие процессы, которые наклдывают еще 1-2 мс на отрисовку и за это время появляется уже следующий кадр, при условии что время между кадрами в игре 16,67 мс. Соответственно при резких движениях бокс отрисовывается где то, но не поверх персонажа.
Единственное на что я могу влиять, это код, выбор библиотек для захвата экрана и оверлея (отрисовки боксов). На что и сделал упор для решения проблемы.
Когда получилось более менее решить проблему с боксами, то появляется возможность прикрутить наводку для аимбота, ее я реализовал через нейросеть HumanizedTrajectoryGen.onnx. https://github.com/jason-snell/humanized-cursor-trajectory-gen

Эта модель обучена на кожаных движениях курсора в браузере, что не очень подходит для игры, но работает быстро.
Еще докупил arduino чтобы не водить мышь через winapi. Но тут ничего интересного нет. Ему на вход подается траектория сгенерированная, он ее реализовывает

4060 8VRAM.
16 RAM.
Arduino PRO-micro
ATMEGA32U4.
После часа игры с читом GPU нагревается до ~ 60 градусов.
Чтобы не обучать с нуля всю модель существует метод Transfer learning (дообучение). Для этого я замораживаю первые слои и провожу обучение на датасете из 287 изображений. На втором этапе размораживаю первые слои у полученной модели и провожу еще раз обучение.
После этого модель должна как бы подстроиться под PUBG и трекать лучше чем предобученная.



В PUBG выставил низкие настройки графики.

Выше указывал по какому методу провожу обучение. imgsz =1280 чтобы вдали YOLO мог увидеть персонажа. При стандартном 640 это невозможно. После обучения при 1280 модель можно использовать в 640. Наоборот такой мув не пройдет.
Параметры lr0 и lrf отвечают за начальную скорость(шаг) обучения и финальную. На сколько понял, эти параметры нужно уменьшать чтобы за каждую эпоху модель не подвергалась сильным изменениям. Если изменения будут значительными, то есть вероятность, что значение некоторых параметров улетят за пределы нормы и дальше обучение не эффективно. Но я так и не понял почему к концу обучения скорость нужно увеличивать, но так рекомендуют делать в официальной документации YOLO.
Результат.



Все наштяк. Loss ползет вниз, mAP50 ползет вверх. И во время игры нормально справляется, для тестов пойдет.
Результаты.



Тут все плохо, поэтому довольствуюсь результатом после 1 этапа. Я не знаю почему, может датасет маленький, может ошибка в разметке, может я че то не так понял из документации.
Я ей на вход подаю координаты центра экрана и координаты бокса, + там есть нормализация потому что для нейросети нет такого понятия как «разрешение». Она знает только про -1, 1 и то что после запятой еще есть 16 знаков. И нормализация приводит 1920х1080 к -1х1.
На выходе массив точек от start до end.
Есть возможность указывать кол-во точек в траектории (максимум 40) и стохастичность («шум» из которого генерируется траектория), то есть если в двух случаях start и end одинаковые, траектория может быть разной.




SCREEN_HEIGHT = 1080
MODEL_PATH = 'models/wpreal7_1.pt'
CONF = 0.6
IMGSZ = 1280
TARGET_FPS = 45
CAPTURE_FRAME_INTERVAL = 1.0 / TARGET_FPS
YOLO_MIN_INTERVAL = 1.0 / TARGET_FPS
UPDATE_INTERVAL_MS = 12
IDLE_SLEEP = 0.001
SPIN_SLEEP = 0.001
LABEL_TEXT = "4ELIK"
LABEL_FONT = ('Arial', 12, 'bold')
BOX_OUTLINE = 'red'
BOX_WIDTH = 3
USE_ARDUINO = True
ARDUINO_BAUD_RATE = 115200
FOV_RADIUS = 300
TRAJECTORY_MODEL_PATH = 'models/HumanizedTrajectoryGen.onnx'
Через DXcam делаю захват экрана, указываю нужный мне ФПС в config. Кадр сохраняется впеременную и хранится там пока не появится следующий кадр. Эта переменная нужна чтобы YOLO забрал из нее изображение на обработку.
CameraCapture._latest_frame — переменная для хранения последнего кадра.
Detection_loop — берет кадр из вышеуказанной переменной.
Если YOLO долго обрабатывает изображение, то следующий кадр будет самый свежий из вышеуказанной переменной. И наоборот если YOLO обработал изображение быстро и в переменной лежит то же самое
изображение, то он прогонит его еще раз
. Это может быть в случае если ФПС захвата экрана очень низкий. Изображение лежит в переменной долго, + одно и то же изображение в
ряд YOLO обрабатывать быстрее (2-5мс) и тогда YOLO обрабатывает 60-70 изображений в секунду при условии, что захват экрана делает 20 скриншотов в секунду. Поэтому когда увеличивается число кадров захвата экрана, ФПС YOLO потихоньку начинает падать, хотя по идеи должно быть наоборот.
YOLO обрабатывает кадр и хранит координаты бокса (x1, y1, x2, y2), центр бокса.
Он по КД обрабатывает кадры из переменной. Я уже знаю что при инференсе imgsz=1280
YOLO прогоняет примерно 28-35 уникальных изображений с КД между изображениями 0,001 с. При этом есть смысл ставить захват экрана на 60 фпс чтобы изображения были максимально свежие, это будет в среднем свежее чем при захвате экрана в 30 или 45 фпс.
Про инференс в imgsz=640 нет смысла что то писать. Это очень быстро, но персонажей видит хуже. Я никак не реализовываю это на практике.
IDLE_SLEEP = 0.001 — задержка между обработкой кадров.
SPIN_SLEEP = 0.001 — время ожидания YOLO-ой появления кадра в переменной. Но у меня всегда в переменной лежит кадр и он удаляется только после появления нового. Кароче у меня это никак не используется.
MODEL_PATH = 'models/wpreal7_1.pt' — путь к модели YOLO.
CONF = 0.6 — уверенность.
IMGSZ = 1280 — размер изображения на вход.
Уже писал выше, что поверх игры у меня прозрачный оверлей. Работает через Tkinter. Он нужен чтобы я визуально видел боксы. Его ФПС зависит только от YOLO, как только YOLO обработал изображение обновляется оврлей с соответствующими боксами и информацией о центре бокса.
Боксы мне нужны для наглядности, а центр бокса передается в HumanizedTrajectoryGen.onnx для генерации траектории. Выше писал как это работает.
На этом этапе пакет с к траекторией отправляется в arduino. Траектория не
отправляется целиком, а только с линией между 2 точками. Условно в траектории 40 точек, отправится 20 пакетов, если 20 точек, отправится 10 пакетов. Можно отправить целиком траекторию. В моей текущей реализации нет разницы.
В это время в arduino прописан скетч, который читает пакеты и двигает курсор от центра экрана до центра бокса.
В коносоли отображается вся необходимая информация:


Detector.py загружает модель YOLO, обрабатывает кадр и возвращает обнаруженный класс. Хранит координаты бокса (x1, y1, x2, y2), центр бокса.
Полученные результаты от Detector.py отправляются в overlay.py. Он нужен для создания прозрачного окна, на котором отрисовываются боксы через tkinter. + на выходе число кадров с отрисованными боксами.
На этом этапе у меня есть информация о том где находятся песонажи в реальном времени. Задержка примерно 8-15мс. Как только я получаю положение персонажа в моем разрешении.
Эти данные отправляются в trajectory.py для генерации траектории для наводки через HumanizedTrajectoryGen.onnx. На
вход подаю координаты пикселей: центр экрана (start) и центр бокса (end), они нормализуются под нейросеть (из 1920х1080 в -1х1), генерируется траектория и денормализация под изначальное разрешение (-1х1 в 1920х1080).
На этом этапе пакет с к траекторией отправляется в arduino. Траектория не отправляется целиком, а только с линией между 2 точками. Условно в траектории 40 точек, отправится 20 пакетов, если 20 точек, отправится 10 пакетов. Можно отправить целиком траекторию. В моей текущей реализации нет разницы.
В arduino прописан скетч, который читает пакеты и двигает курсор.
В main.py вызываю все функции. Запускаю потоки, горячие клавиши (F/f или боковая кнопка мыши — для срабатывания aimbot-а, K/k — выход). Отображаю fps в консоли.
2. Кадр сохраняется в CameraCapture._latest_frame.
3. Detection_loop берет кадр из переменной.
4.YOLO обрабатывает кадр.
5. GUI отрисовывает боксы.
6.Aimbot находит бокс.
7.HumanizedTrajectoryGen.onnx генерирует траекторию.
8.Arduino передвигает курсор.
И я не знаю детектится он или нет. Где то читал, что ардуино нужно скрывать, кароче нельзя его реализовать как это делаю я в статье. Я только отрисовкой боксов в игре пользуюсь где то месяц и продолжаю собирать датасет, это вроде не палевно.
Открыть видео / медиа
Аннотация.
Если начать издалека, то мне нужно было обучить YOLO чтобы отрисовывать боксы поверх врагов чтобы я мог почаще долбиться в глаза. С обучением трудностей нет, как обычно нужен неебических размеров датасет и будет хороший результат.Но в ходе работы заметил что YOLO не сильно нагружает систему и боксы «не поспевают» за реальным положением врага на экране.

Эта проблема будет сохраняться всегда и упирается она в мощности GPU, параметры инференса YOLO и код. В моем случае инференс занимает примерно 8-15мс, помимо этого есть другие процессы, которые наклдывают еще 1-2 мс на отрисовку и за это время появляется уже следующий кадр, при условии что время между кадрами в игре 16,67 мс. Соответственно при резких движениях бокс отрисовывается где то, но не поверх персонажа.
Единственное на что я могу влиять, это код, выбор библиотек для захвата экрана и оверлея (отрисовки боксов). На что и сделал упор для решения проблемы.
Когда получилось более менее решить проблему с боксами, то появляется возможность прикрутить наводку для аимбота, ее я реализовал через нейросеть HumanizedTrajectoryGen.onnx. https://github.com/jason-snell/humanized-cursor-trajectory-gen

Эта модель обучена на кожаных движениях курсора в браузере, что не очень подходит для игры, но работает быстро.
Еще докупил arduino чтобы не водить мышь через winapi. Но тут ничего интересного нет. Ему на вход подается траектория сгенерированная, он ее реализовывает

Параметры моей системы.
i5 13400f.4060 8VRAM.
16 RAM.
Arduino PRO-micro
ATMEGA32U4.
После часа игры с читом GPU нагревается до ~ 60 градусов.
YOLO.
Изначально я пробовал использовать предобученную модель yolo11s.pt на классе «person». Вблизи трекает уверенно, но на расстоянии, за объеками или персонаж сливается с местностью не всегда может определить.Чтобы не обучать с нуля всю модель существует метод Transfer learning (дообучение). Для этого я замораживаю первые слои и провожу обучение на датасете из 287 изображений. На втором этапе размораживаю первые слои у полученной модели и провожу еще раз обучение.
После этого модель должна как бы подстроиться под PUBG и трекать лучше чем предобученная.
Датасет.
Датасет я собрал маленький, 287 изображений. В основном делал скриншоты из реплеев от лица «свободной камеры», с полностью отключенным интерфейсом, где видно персонажей, иногда делал скриншоты от «1 лица». Или такие, где нет персонажей и никакой разметки соответственно, ~10% датасета. Старался делать «неудобные» скриншоты, где персонаж в окне, за препядствием, сливается с местностью или за сеточным забором.


В PUBG выставил низкие настройки графики.

Обучение.
Выше указывал по какому методу провожу обучение. imgsz =1280 чтобы вдали YOLO мог увидеть персонажа. При стандартном 640 это невозможно. После обучения при 1280 модель можно использовать в 640. Наоборот такой мув не пройдет.
Параметры lr0 и lrf отвечают за начальную скорость(шаг) обучения и финальную. На сколько понял, эти параметры нужно уменьшать чтобы за каждую эпоху модель не подвергалась сильным изменениям. Если изменения будут значительными, то есть вероятность, что значение некоторых параметров улетят за пределы нормы и дальше обучение не эффективно. Но я так и не понял почему к концу обучения скорость нужно увеличивать, но так рекомендуют делать в официальной документации YOLO.
from ultralytics import YOLO
def main():
model = YOLO('yolo11s.pt')
results = model.train(
data='data4.yaml',
epochs=50,
batch=6,
imgsz=1280,
freeze=10,
lr0=0.001,
device=0,
)
if __name__ == '__main__':
main()
Python
def main():
model = YOLO('yolo11s.pt')
results = model.train(
data='data4.yaml',
epochs=50,
batch=6,
imgsz=1280,
freeze=10,
lr0=0.001,
device=0,
)
if __name__ == '__main__':
main()
Python
Код:
from ultralytics import YOLO
def main():
model = YOLO('yolo11s.pt')
results = model.train(
data='data4.yaml',
epochs=50,
batch=6,
imgsz=1280,
freeze=10,
lr0=0.001,
device=0,
)
if __name__ == '__main__':
main()



Все наштяк. Loss ползет вниз, mAP50 ползет вверх. И во время игры нормально справляется, для тестов пойдет.
from ultralytics import YOLO
def main():
model = YOLO('best.pt')
results = model.train(
data='data4.yaml',
epochs=25,
batch=4,
imgsz=1280,
device=0,
freeze=0,
lr0=0.0001, # скорость вначале
lrf=0.1, # скорость в конце
)
if __name__ == '__main__':
main()
Python
def main():
model = YOLO('best.pt')
results = model.train(
data='data4.yaml',
epochs=25,
batch=4,
imgsz=1280,
device=0,
freeze=0,
lr0=0.0001, # скорость вначале
lrf=0.1, # скорость в конце
)
if __name__ == '__main__':
main()
Python
Код:
from ultralytics import YOLO
def main():
model = YOLO('best.pt')
results = model.train(
data='data4.yaml',
epochs=25,
batch=4,
imgsz=1280,
device=0,
freeze=0,
lr0=0.0001, # скорость вначале
lrf=0.1, # скорость в конце
)
if __name__ == '__main__':
main()



Тут все плохо, поэтому довольствуюсь результатом после 1 этапа. Я не знаю почему, может датасет маленький, может ошибка в разметке, может я че то не так понял из документации.
HumanizedTrajectoryGen.onnx.
Какая то генеративная нейросеть, обученная на работе человека в браузере.Я ей на вход подаю координаты центра экрана и координаты бокса, + там есть нормализация потому что для нейросети нет такого понятия как «разрешение». Она знает только про -1, 1 и то что после запятой еще есть 16 знаков. И нормализация приводит 1920х1080 к -1х1.
На выходе массив точек от start до end.
Есть возможность указывать кол-во точек в траектории (максимум 40) и стохастичность («шум» из которого генерируется траектория), то есть если в двух случаях start и end одинаковые, траектория может быть разной.



Архитектура WPreal_chlenix.

Config.
SCREEN_WIDTH = 1920SCREEN_HEIGHT = 1080
MODEL_PATH = 'models/wpreal7_1.pt'
CONF = 0.6
IMGSZ = 1280
TARGET_FPS = 45
CAPTURE_FRAME_INTERVAL = 1.0 / TARGET_FPS
YOLO_MIN_INTERVAL = 1.0 / TARGET_FPS
UPDATE_INTERVAL_MS = 12
IDLE_SLEEP = 0.001
SPIN_SLEEP = 0.001
LABEL_TEXT = "4ELIK"
LABEL_FONT = ('Arial', 12, 'bold')
BOX_OUTLINE = 'red'
BOX_WIDTH = 3
USE_ARDUINO = True
ARDUINO_BAUD_RATE = 115200
FOV_RADIUS = 300
TRAJECTORY_MODEL_PATH = 'models/HumanizedTrajectoryGen.onnx'
Общий принцип работы.
Через DXcam делаю захват экрана, указываю нужный мне ФПС в config. Кадр сохраняется впеременную и хранится там пока не появится следующий кадр. Эта переменная нужна чтобы YOLO забрал из нее изображение на обработку.
CameraCapture._latest_frame — переменная для хранения последнего кадра.
Detection_loop — берет кадр из вышеуказанной переменной.
Если YOLO долго обрабатывает изображение, то следующий кадр будет самый свежий из вышеуказанной переменной. И наоборот если YOLO обработал изображение быстро и в переменной лежит то же самое
изображение, то он прогонит его еще раз
ряд YOLO обрабатывать быстрее (2-5мс) и тогда YOLO обрабатывает 60-70 изображений в секунду при условии, что захват экрана делает 20 скриншотов в секунду. Поэтому когда увеличивается число кадров захвата экрана, ФПС YOLO потихоньку начинает падать, хотя по идеи должно быть наоборот.
YOLO обрабатывает кадр и хранит координаты бокса (x1, y1, x2, y2), центр бокса.
Он по КД обрабатывает кадры из переменной. Я уже знаю что при инференсе imgsz=1280
YOLO прогоняет примерно 28-35 уникальных изображений с КД между изображениями 0,001 с. При этом есть смысл ставить захват экрана на 60 фпс чтобы изображения были максимально свежие, это будет в среднем свежее чем при захвате экрана в 30 или 45 фпс.
Про инференс в imgsz=640 нет смысла что то писать. Это очень быстро, но персонажей видит хуже. Я никак не реализовываю это на практике.
IDLE_SLEEP = 0.001 — задержка между обработкой кадров.
SPIN_SLEEP = 0.001 — время ожидания YOLO-ой появления кадра в переменной. Но у меня всегда в переменной лежит кадр и он удаляется только после появления нового. Кароче у меня это никак не используется.
MODEL_PATH = 'models/wpreal7_1.pt' — путь к модели YOLO.
CONF = 0.6 — уверенность.
IMGSZ = 1280 — размер изображения на вход.
Уже писал выше, что поверх игры у меня прозрачный оверлей. Работает через Tkinter. Он нужен чтобы я визуально видел боксы. Его ФПС зависит только от YOLO, как только YOLO обработал изображение обновляется оврлей с соответствующими боксами и информацией о центре бокса.
Боксы мне нужны для наглядности, а центр бокса передается в HumanizedTrajectoryGen.onnx для генерации траектории. Выше писал как это работает.
На этом этапе пакет с к траекторией отправляется в arduino. Траектория не
отправляется целиком, а только с линией между 2 точками. Условно в траектории 40 точек, отправится 20 пакетов, если 20 точек, отправится 10 пакетов. Можно отправить целиком траекторию. В моей текущей реализации нет разницы.
В это время в arduino прописан скетч, который читает пакеты и двигает курсор от центра экрана до центра бокса.
В коносоли отображается вся необходимая информация:
- Подключение к arduino.
- ФПС захвата экрана, инференса YOLO, оверлея.
- Сгенерированная траектория, плавность наводки


Реализация модулей.
Все начинается с захвата экрана в отдельном потоке через Dxcam в camera.py. Сделанный скриншот хранится в переменной пока его не заберет YOLO.# src/wpreal/core/camera.py
import threading
import time
import dxcam
import numpy as np
from typing import Optional
class CameraCapture:
def __init__(self, fps: int = 60):
self.fps = fps
self._running = False
self._latest_frame: Optional[np.ndarray] = None
self._lock = threading.Lock()
self._camera = None
self._capture_count = 0
self._capture_time = time.time()
self._capture_fps = 0
def start(self):
self._running = True
self._camera = dxcam.create(output_color="BGR")
threading.Thread(target=self._capture_loop, daemon=True).start()
def stop(self):
self._running = False
def get_frame(self) -> Optional[np.ndarray]:
with self._lock:
return self._latest_frame.copy() if self._latest_frame is not None else None
def get_fps(self) -> int:
return self._capture_fps
def _capture_loop(self):
next_frame_time = time.perf_counter()
frame_interval = 1.0 / self.fps
while self._running:
current_time = time.perf_counter()
if current_time < next_frame_time:
time.sleep(0.001)
continue
frame = self._camera.grab()
if frame is not None:
with self._lock:
self._latest_frame = frame
self._capture_count += 1
now = time.time()
if now - self._capture_time >= 1.0:
self._capture_fps = self._capture_count
self._capture_count = 0
self._capture_time = now
next_frame_time += frame_interval
if next_frame_time < time.perf_counter():
next_frame_time = time.perf_counter() + frame_interval
Python
import threading
import time
import dxcam
import numpy as np
from typing import Optional
class CameraCapture:
def __init__(self, fps: int = 60):
self.fps = fps
self._running = False
self._latest_frame: Optional[np.ndarray] = None
self._lock = threading.Lock()
self._camera = None
self._capture_count = 0
self._capture_time = time.time()
self._capture_fps = 0
def start(self):
self._running = True
self._camera = dxcam.create(output_color="BGR")
threading.Thread(target=self._capture_loop, daemon=True).start()
def stop(self):
self._running = False
def get_frame(self) -> Optional[np.ndarray]:
with self._lock:
return self._latest_frame.copy() if self._latest_frame is not None else None
def get_fps(self) -> int:
return self._capture_fps
def _capture_loop(self):
next_frame_time = time.perf_counter()
frame_interval = 1.0 / self.fps
while self._running:
current_time = time.perf_counter()
if current_time < next_frame_time:
time.sleep(0.001)
continue
frame = self._camera.grab()
if frame is not None:
with self._lock:
self._latest_frame = frame
self._capture_count += 1
now = time.time()
if now - self._capture_time >= 1.0:
self._capture_fps = self._capture_count
self._capture_count = 0
self._capture_time = now
next_frame_time += frame_interval
if next_frame_time < time.perf_counter():
next_frame_time = time.perf_counter() + frame_interval
Python
Код:
# src/wpreal/core/camera.py
import threading
import time
import dxcam
import numpy as np
from typing import Optional
class CameraCapture:
def __init__(self, fps: int = 60):
self.fps = fps
self._running = False
self._latest_frame: Optional[np.ndarray] = None
self._lock = threading.Lock()
self._camera = None
self._capture_count = 0
self._capture_time = time.time()
self._capture_fps = 0
def start(self):
self._running = True
self._camera = dxcam.create(output_color="BGR")
threading.Thread(target=self._capture_loop, daemon=True).start()
def stop(self):
self._running = False
def get_frame(self) -> Optional[np.ndarray]:
with self._lock:
return self._latest_frame.copy() if self._latest_frame is not None else None
def get_fps(self) -> int:
return self._capture_fps
def _capture_loop(self):
next_frame_time = time.perf_counter()
frame_interval = 1.0 / self.fps
while self._running:
current_time = time.perf_counter()
if current_time < next_frame_time:
time.sleep(0.001)
continue
frame = self._camera.grab()
if frame is not None:
with self._lock:
self._latest_frame = frame
self._capture_count += 1
now = time.time()
if now - self._capture_time >= 1.0:
self._capture_fps = self._capture_count
self._capture_count = 0
self._capture_time = now
next_frame_time += frame_interval
if next_frame_time < time.perf_counter():
next_frame_time = time.perf_counter() + frame_interval
# src/wpreal/core/detector.py
import time
import numpy as np
from ultralytics import YOLO
from typing import List, Tuple, Optional
from dataclasses import dataclass
@dataclass
class Detection:
x1: int
y1: int
x2: int
y2: int
confidence: float
class_id: int
@property
def center(self) -> Tuple[int, int]:
"""Центр бокса"""
return ((self.x1 + self.x2) // 2, (self.y1 + self.y2) // 2)
@property
def width(self) -> int:
return self.x2 - self.x1
@property
def height(self) -> int:
return self.y2 - self.y1
def to_tuple(self) -> Tuple[int, int, int, int]:
return (self.x1, self.y1, self.x2, self.y2)
class YOLODetector:
def __init__(self, model_path: str, confidence: float = 0.5, image_size: int = 640):
self.model = YOLO(model_path)
self.model.model.half() # FP16
self.confidence = confidence
self.image_size = image_size
self._yolo_count = 0
self._yolo_time = time.time()
self._yolo_fps = 0
def detect(self, frame: np.ndarray) -> List[Detection]:
if frame is None:
return []
results = self.model(
frame,
conf=self.confidence,
imgsz=self.image_size,
verbose=False,
half=True
)
detections = []
if results[0].boxes is not None:
for box in results[0].boxes:
x1, y1, x2, y2 = map(int, box.xyxy[0].tolist())
detections.append(Detection(
x1=x1, y1=y1, x2=x2, y2=y2,
confidence=float(box.conf[0]),
class_id=int(box.cls[0])
))
# Подсчет FPS
self._yolo_count += 1
now = time.time()
if now - self._yolo_time >= 1.0:
self._yolo_fps = self._yolo_count
self._yolo_count = 0
self._yolo_time = now
return detections
def get_fps(self) -> int:
return self._yolo_fps
def filter_by_class(self, detections: List[Detection], class_ids: List[int]) -> List[Detection]:
return [d for d in detections if d.class_id in class_ids]
Python
import time
import numpy as np
from ultralytics import YOLO
from typing import List, Tuple, Optional
from dataclasses import dataclass
@dataclass
class Detection:
x1: int
y1: int
x2: int
y2: int
confidence: float
class_id: int
@property
def center(self) -> Tuple[int, int]:
"""Центр бокса"""
return ((self.x1 + self.x2) // 2, (self.y1 + self.y2) // 2)
@property
def width(self) -> int:
return self.x2 - self.x1
@property
def height(self) -> int:
return self.y2 - self.y1
def to_tuple(self) -> Tuple[int, int, int, int]:
return (self.x1, self.y1, self.x2, self.y2)
class YOLODetector:
def __init__(self, model_path: str, confidence: float = 0.5, image_size: int = 640):
self.model = YOLO(model_path)
self.model.model.half() # FP16
self.confidence = confidence
self.image_size = image_size
self._yolo_count = 0
self._yolo_time = time.time()
self._yolo_fps = 0
def detect(self, frame: np.ndarray) -> List[Detection]:
if frame is None:
return []
results = self.model(
frame,
conf=self.confidence,
imgsz=self.image_size,
verbose=False,
half=True
)
detections = []
if results[0].boxes is not None:
for box in results[0].boxes:
x1, y1, x2, y2 = map(int, box.xyxy[0].tolist())
detections.append(Detection(
x1=x1, y1=y1, x2=x2, y2=y2,
confidence=float(box.conf[0]),
class_id=int(box.cls[0])
))
# Подсчет FPS
self._yolo_count += 1
now = time.time()
if now - self._yolo_time >= 1.0:
self._yolo_fps = self._yolo_count
self._yolo_count = 0
self._yolo_time = now
return detections
def get_fps(self) -> int:
return self._yolo_fps
def filter_by_class(self, detections: List[Detection], class_ids: List[int]) -> List[Detection]:
return [d for d in detections if d.class_id in class_ids]
Python
Код:
# src/wpreal/core/detector.py
import time
import numpy as np
from ultralytics import YOLO
from typing import List, Tuple, Optional
from dataclasses import dataclass
@dataclass
class Detection:
x1: int
y1: int
x2: int
y2: int
confidence: float
class_id: int
@property
def center(self) -> Tuple[int, int]:
"""Центр бокса"""
return ((self.x1 + self.x2) // 2, (self.y1 + self.y2) // 2)
@property
def width(self) -> int:
return self.x2 - self.x1
@property
def height(self) -> int:
return self.y2 - self.y1
def to_tuple(self) -> Tuple[int, int, int, int]:
return (self.x1, self.y1, self.x2, self.y2)
class YOLODetector:
def __init__(self, model_path: str, confidence: float = 0.5, image_size: int = 640):
self.model = YOLO(model_path)
self.model.model.half() # FP16
self.confidence = confidence
self.image_size = image_size
self._yolo_count = 0
self._yolo_time = time.time()
self._yolo_fps = 0
def detect(self, frame: np.ndarray) -> List[Detection]:
if frame is None:
return []
results = self.model(
frame,
conf=self.confidence,
imgsz=self.image_size,
verbose=False,
half=True
)
detections = []
if results[0].boxes is not None:
for box in results[0].boxes:
x1, y1, x2, y2 = map(int, box.xyxy[0].tolist())
detections.append(Detection(
x1=x1, y1=y1, x2=x2, y2=y2,
confidence=float(box.conf[0]),
class_id=int(box.cls[0])
))
# Подсчет FPS
self._yolo_count += 1
now = time.time()
if now - self._yolo_time >= 1.0:
self._yolo_fps = self._yolo_count
self._yolo_count = 0
self._yolo_time = now
return detections
def get_fps(self) -> int:
return self._yolo_fps
def filter_by_class(self, detections: List[Detection], class_ids: List[int]) -> List[Detection]:
return [d for d in detections if d.class_id in class_ids]
# src/wpreal/gui/overlay.py
import time
import tkinter as tk
from typing import List
from ..core.detector import Detection
from ..config import LABEL_TEXT, LABEL_FONT, BOX_OUTLINE, BOX_WIDTH
class GUIOverlay:
def __init__(self, screen_width: int, screen_height: int):
self.screen_width = screen_width
self.screen_height = screen_height
self.root = None
self.canvas = None
self.running = True
self._gui_count = 0
self._gui_time = time.time()
self._gui_fps = 0
self._init_gui()
def _init_gui(self):
self.root = tk.Tk()
self.root.title("PUBG Overlay")
self.root.attributes('-topmost', True)
self.root.attributes('-fullscreen', True)
self.root.attributes('-transparentcolor', 'black')
self.root.configure(bg='black')
self.root.overrideredirect(True)
self.canvas = tk.Canvas(
self.root,
width=self.screen_width,
height=self.screen_height,
bg='black',
highlightthickness=0
)
self.canvas.pack()
def update(self, detections: List[Detection]):
if not self.running:
return
self._gui_count += 1
now = time.time()
if now - self._gui_time >= 1.0:
self._gui_fps = self._gui_count
self._gui_count = 0
self._gui_time = now
self.canvas.delete("all")
for det in detections:
self.canvas.create_rectangle(
det.x1, det.y1, det.x2, det.y2,
outline=BOX_OUTLINE,
width=BOX_WIDTH
)
self.canvas.create_text(
det.x1, det.y1 - 10,
text=LABEL_TEXT,
fill=BOX_OUTLINE,
anchor='sw',
font=LABEL_FONT
)
self.root.update()
def get_fps(self) -> int:
return self._gui_fps
def start(self):
self.root.mainloop()
def stop(self):
self.running = False
if self.root:
self.root.quit()
self.root.destroy()
Python
import time
import tkinter as tk
from typing import List
from ..core.detector import Detection
from ..config import LABEL_TEXT, LABEL_FONT, BOX_OUTLINE, BOX_WIDTH
class GUIOverlay:
def __init__(self, screen_width: int, screen_height: int):
self.screen_width = screen_width
self.screen_height = screen_height
self.root = None
self.canvas = None
self.running = True
self._gui_count = 0
self._gui_time = time.time()
self._gui_fps = 0
self._init_gui()
def _init_gui(self):
self.root = tk.Tk()
self.root.title("PUBG Overlay")
self.root.attributes('-topmost', True)
self.root.attributes('-fullscreen', True)
self.root.attributes('-transparentcolor', 'black')
self.root.configure(bg='black')
self.root.overrideredirect(True)
self.canvas = tk.Canvas(
self.root,
width=self.screen_width,
height=self.screen_height,
bg='black',
highlightthickness=0
)
self.canvas.pack()
def update(self, detections: List[Detection]):
if not self.running:
return
self._gui_count += 1
now = time.time()
if now - self._gui_time >= 1.0:
self._gui_fps = self._gui_count
self._gui_count = 0
self._gui_time = now
self.canvas.delete("all")
for det in detections:
self.canvas.create_rectangle(
det.x1, det.y1, det.x2, det.y2,
outline=BOX_OUTLINE,
width=BOX_WIDTH
)
self.canvas.create_text(
det.x1, det.y1 - 10,
text=LABEL_TEXT,
fill=BOX_OUTLINE,
anchor='sw',
font=LABEL_FONT
)
self.root.update()
def get_fps(self) -> int:
return self._gui_fps
def start(self):
self.root.mainloop()
def stop(self):
self.running = False
if self.root:
self.root.quit()
self.root.destroy()
Python
Код:
# src/wpreal/gui/overlay.py
import time
import tkinter as tk
from typing import List
from ..core.detector import Detection
from ..config import LABEL_TEXT, LABEL_FONT, BOX_OUTLINE, BOX_WIDTH
class GUIOverlay:
def __init__(self, screen_width: int, screen_height: int):
self.screen_width = screen_width
self.screen_height = screen_height
self.root = None
self.canvas = None
self.running = True
self._gui_count = 0
self._gui_time = time.time()
self._gui_fps = 0
self._init_gui()
def _init_gui(self):
self.root = tk.Tk()
self.root.title("PUBG Overlay")
self.root.attributes('-topmost', True)
self.root.attributes('-fullscreen', True)
self.root.attributes('-transparentcolor', 'black')
self.root.configure(bg='black')
self.root.overrideredirect(True)
self.canvas = tk.Canvas(
self.root,
width=self.screen_width,
height=self.screen_height,
bg='black',
highlightthickness=0
)
self.canvas.pack()
def update(self, detections: List[Detection]):
if not self.running:
return
self._gui_count += 1
now = time.time()
if now - self._gui_time >= 1.0:
self._gui_fps = self._gui_count
self._gui_count = 0
self._gui_time = now
self.canvas.delete("all")
for det in detections:
self.canvas.create_rectangle(
det.x1, det.y1, det.x2, det.y2,
outline=BOX_OUTLINE,
width=BOX_WIDTH
)
self.canvas.create_text(
det.x1, det.y1 - 10,
text=LABEL_TEXT,
fill=BOX_OUTLINE,
anchor='sw',
font=LABEL_FONT
)
self.root.update()
def get_fps(self) -> int:
return self._gui_fps
def start(self):
self.root.mainloop()
def stop(self):
self.running = False
if self.root:
self.root.quit()
self.root.destroy()
Эти данные отправляются в trajectory.py для генерации траектории для наводки через HumanizedTrajectoryGen.onnx. На
вход подаю координаты пикселей: центр экрана (start) и центр бокса (end), они нормализуются под нейросеть (из 1920х1080 в -1х1), генерируется траектория и денормализация под изначальное разрешение (-1х1 в 1920х1080).
# src/wpreal/aimbot/trajectory.py
import numpy as np
import onnxruntime as ort
from typing import List, Tuple, Optional
class TrajectoryGenerator:
def __init__(self, model_path: Optional[str] = None, screen_width: int = 1920, screen_height: int = 1080):
self.session = None
self.screen_width = screen_width
self.screen_height = screen_height
if model_path:
try:
self.session = ort.InferenceSession(model_path)
print(" Модель траекторий загружена")
except Exception as e:
print(f" Ошибка загрузки модели траекторий: {e}")
print("Будет использовано линейное наведение")
def generate(self, start: Tuple[int, int], end: Tuple[int, int]) -> List[Tuple[int, int]]:
if self.session is None:
return [start, end]
# Нормализация
norm_start = self._normalize(start)
norm_end = self._normalize(end)
input_data = np.array([norm_start, norm_end], dtype=np.float32)
input_tensor = input_data.reshape(1, 2, 2)
outputs = self.session.run(None, {"lstm_input": input_tensor})
raw_trajectory = outputs[0][0]
# Денормализация
trajectory = [
self._denormalize(p)
for p in raw_trajectory
]
if len(trajectory) > 0 and trajectory[-1] != end:
trajectory.append(end)
return trajectory
def _normalize(self, point: Tuple[int, int]) -> List[float]:
return [
(point[0] / (self.screen_width / 2)) - 1,
(point[1] / (self.screen_height / 2)) - 1
]
def _denormalize(self, point: List[float]) -> Tuple[int, int]:
x = int(((point[0] + 1) / 2) * self.screen_width)
y = int(((point[1] + 1) / 2) * self.screen_height)
return (x, y)
Python
import numpy as np
import onnxruntime as ort
from typing import List, Tuple, Optional
class TrajectoryGenerator:
def __init__(self, model_path: Optional[str] = None, screen_width: int = 1920, screen_height: int = 1080):
self.session = None
self.screen_width = screen_width
self.screen_height = screen_height
if model_path:
try:
self.session = ort.InferenceSession(model_path)
print(" Модель траекторий загружена")
except Exception as e:
print(f" Ошибка загрузки модели траекторий: {e}")
print("Будет использовано линейное наведение")
def generate(self, start: Tuple[int, int], end: Tuple[int, int]) -> List[Tuple[int, int]]:
if self.session is None:
return [start, end]
# Нормализация
norm_start = self._normalize(start)
norm_end = self._normalize(end)
input_data = np.array([norm_start, norm_end], dtype=np.float32)
input_tensor = input_data.reshape(1, 2, 2)
outputs = self.session.run(None, {"lstm_input": input_tensor})
raw_trajectory = outputs[0][0]
# Денормализация
trajectory = [
self._denormalize(p)
for p in raw_trajectory
]
if len(trajectory) > 0 and trajectory[-1] != end:
trajectory.append(end)
return trajectory
def _normalize(self, point: Tuple[int, int]) -> List[float]:
return [
(point[0] / (self.screen_width / 2)) - 1,
(point[1] / (self.screen_height / 2)) - 1
]
def _denormalize(self, point: List[float]) -> Tuple[int, int]:
x = int(((point[0] + 1) / 2) * self.screen_width)
y = int(((point[1] + 1) / 2) * self.screen_height)
return (x, y)
Python
Код:
# src/wpreal/aimbot/trajectory.py
import numpy as np
import onnxruntime as ort
from typing import List, Tuple, Optional
class TrajectoryGenerator:
def __init__(self, model_path: Optional[str] = None, screen_width: int = 1920, screen_height: int = 1080):
self.session = None
self.screen_width = screen_width
self.screen_height = screen_height
if model_path:
try:
self.session = ort.InferenceSession(model_path)
print(" Модель траекторий загружена")
except Exception as e:
print(f" Ошибка загрузки модели траекторий: {e}")
print("Будет использовано линейное наведение")
def generate(self, start: Tuple[int, int], end: Tuple[int, int]) -> List[Tuple[int, int]]:
if self.session is None:
return [start, end]
# Нормализация
norm_start = self._normalize(start)
norm_end = self._normalize(end)
input_data = np.array([norm_start, norm_end], dtype=np.float32)
input_tensor = input_data.reshape(1, 2, 2)
outputs = self.session.run(None, {"lstm_input": input_tensor})
raw_trajectory = outputs[0][0]
# Денормализация
trajectory = [
self._denormalize(p)
for p in raw_trajectory
]
if len(trajectory) > 0 and trajectory[-1] != end:
trajectory.append(end)
return trajectory
def _normalize(self, point: Tuple[int, int]) -> List[float]:
return [
(point[0] / (self.screen_width / 2)) - 1,
(point[1] / (self.screen_height / 2)) - 1
]
def _denormalize(self, point: List[float]) -> Tuple[int, int]:
x = int(((point[0] + 1) / 2) * self.screen_width)
y = int(((point[1] + 1) / 2) * self.screen_height)
return (x, y)
# src/wpreal/arduino_initialization.py
import serial
import serial.tools.list_ports
import time
def find_arduino_port():
ports = serial.tools.list_ports.comports()
for port in ports:
if "Arduino" in port.description or "Arduino" in port.manufacturer:
return port.device
# if port.vid == 0x2341 and port.pid == 0x8037: # Pro Micro
# return port.device
print(" Не удалось найти Arduino.")
return None
def connect_to_arduino(baud_rate=115200):
arduino_port = find_arduino_port()
if not arduino_port:
return None
try:
ser = serial.Serial(arduino_port, baud_rate, timeout=1, write_timeout=5)
time.sleep(2)
print(f" Подключено к {arduino_port} со скоростью {baud_rate}")
return ser
except serial.SerialException as e:
print(f" Не удалось открыть порт {arduino_port}: {e}")
return None
Python
import serial
import serial.tools.list_ports
import time
def find_arduino_port():
ports = serial.tools.list_ports.comports()
for port in ports:
if "Arduino" in port.description or "Arduino" in port.manufacturer:
return port.device
# if port.vid == 0x2341 and port.pid == 0x8037: # Pro Micro
# return port.device
print(" Не удалось найти Arduino.")
return None
def connect_to_arduino(baud_rate=115200):
arduino_port = find_arduino_port()
if not arduino_port:
return None
try:
ser = serial.Serial(arduino_port, baud_rate, timeout=1, write_timeout=5)
time.sleep(2)
print(f" Подключено к {arduino_port} со скоростью {baud_rate}")
return ser
except serial.SerialException as e:
print(f" Не удалось открыть порт {arduino_port}: {e}")
return None
Python
Код:
# src/wpreal/arduino_initialization.py
import serial
import serial.tools.list_ports
import time
def find_arduino_port():
ports = serial.tools.list_ports.comports()
for port in ports:
if "Arduino" in port.description or "Arduino" in port.manufacturer:
return port.device
# if port.vid == 0x2341 and port.pid == 0x8037: # Pro Micro
# return port.device
print(" Не удалось найти Arduino.")
return None
def connect_to_arduino(baud_rate=115200):
arduino_port = find_arduino_port()
if not arduino_port:
return None
try:
ser = serial.Serial(arduino_port, baud_rate, timeout=1, write_timeout=5)
time.sleep(2)
print(f" Подключено к {arduino_port} со скоростью {baud_rate}")
return ser
except serial.SerialException as e:
print(f" Не удалось открыть порт {arduino_port}: {e}")
return None
# src/wpreal/aimbot/aimbot.py
import math
import time
from typing import List, Tuple, Optional
from ..core.detector import Detection
from ..core.mouse import MouseController
from .trajectory import TrajectoryGenerator
class Aimbot:
def __init__(self, mouse: MouseController, trajectory: TrajectoryGenerator,
screen_width: int, screen_height: int, fov_radius: int = 300):
self.mouse = mouse
self.trajectory = trajectory
self.screen_width = screen_width
self.screen_height = screen_height
self.enabled = True
self.fov_radius = fov_radius
self.smoothness = 0.3
self.active = False
# Screen center
self.screen_center = (screen_width // 2, screen_height // 2)
def activate(self):
self.active = True
print("Aimbot активирован!")
def deactivate(self):
self.active = False
def find_best_target(self, detections: List[Detection]) -> Optional[Detection]:
if not detections:
return None
best = None
best_dist = float('inf')
for det in detections:
center = det.center
dist = math.sqrt(
(center[0] - self.screen_center[0]) ** 2 +
(center[1] - self.screen_center[1]) ** 2
)
if dist <= self.fov_radius and dist < best_dist:
best_dist = dist
best = det
return best
def aim_at(self, target: Detection) -> bool:
if not self.enabled or not self.active:
return False
target_center = target.center
trajectory = self.trajectory.generate(self.screen_center, target_center)
print(f"Траектория: {len(trajectory)} точек")
print(f"Цель: {target_center}")
for i in range(1, len(trajectory)):
prev = trajectory[i-1]
curr = trajectory[i]
dx = curr[0] - prev[0]
dy = curr[1] - prev[1]
self.mouse.move(dx, dy)
time.sleep(0.001)
self.active = False
print(f"Наведение завершено!")
return True
Python
import math
import time
from typing import List, Tuple, Optional
from ..core.detector import Detection
from ..core.mouse import MouseController
from .trajectory import TrajectoryGenerator
class Aimbot:
def __init__(self, mouse: MouseController, trajectory: TrajectoryGenerator,
screen_width: int, screen_height: int, fov_radius: int = 300):
self.mouse = mouse
self.trajectory = trajectory
self.screen_width = screen_width
self.screen_height = screen_height
self.enabled = True
self.fov_radius = fov_radius
self.smoothness = 0.3
self.active = False
# Screen center
self.screen_center = (screen_width // 2, screen_height // 2)
def activate(self):
self.active = True
print("Aimbot активирован!")
def deactivate(self):
self.active = False
def find_best_target(self, detections: List[Detection]) -> Optional[Detection]:
if not detections:
return None
best = None
best_dist = float('inf')
for det in detections:
center = det.center
dist = math.sqrt(
(center[0] - self.screen_center[0]) ** 2 +
(center[1] - self.screen_center[1]) ** 2
)
if dist <= self.fov_radius and dist < best_dist:
best_dist = dist
best = det
return best
def aim_at(self, target: Detection) -> bool:
if not self.enabled or not self.active:
return False
target_center = target.center
trajectory = self.trajectory.generate(self.screen_center, target_center)
print(f"Траектория: {len(trajectory)} точек")
print(f"Цель: {target_center}")
for i in range(1, len(trajectory)):
prev = trajectory[i-1]
curr = trajectory[i]
dx = curr[0] - prev[0]
dy = curr[1] - prev[1]
self.mouse.move(dx, dy)
time.sleep(0.001)
self.active = False
print(f"Наведение завершено!")
return True
Python
Код:
# src/wpreal/aimbot/aimbot.py
import math
import time
from typing import List, Tuple, Optional
from ..core.detector import Detection
from ..core.mouse import MouseController
from .trajectory import TrajectoryGenerator
class Aimbot:
def __init__(self, mouse: MouseController, trajectory: TrajectoryGenerator,
screen_width: int, screen_height: int, fov_radius: int = 300):
self.mouse = mouse
self.trajectory = trajectory
self.screen_width = screen_width
self.screen_height = screen_height
self.enabled = True
self.fov_radius = fov_radius
self.smoothness = 0.3
self.active = False
# Screen center
self.screen_center = (screen_width // 2, screen_height // 2)
def activate(self):
self.active = True
print("Aimbot активирован!")
def deactivate(self):
self.active = False
def find_best_target(self, detections: List[Detection]) -> Optional[Detection]:
if not detections:
return None
best = None
best_dist = float('inf')
for det in detections:
center = det.center
dist = math.sqrt(
(center[0] - self.screen_center[0]) ** 2 +
(center[1] - self.screen_center[1]) ** 2
)
if dist <= self.fov_radius and dist < best_dist:
best_dist = dist
best = det
return best
def aim_at(self, target: Detection) -> bool:
if not self.enabled or not self.active:
return False
target_center = target.center
trajectory = self.trajectory.generate(self.screen_center, target_center)
print(f"Траектория: {len(trajectory)} точек")
print(f"Цель: {target_center}")
for i in range(1, len(trajectory)):
prev = trajectory[i-1]
curr = trajectory[i]
dx = curr[0] - prev[0]
dy = curr[1] - prev[1]
self.mouse.move(dx, dy)
time.sleep(0.001)
self.active = False
print(f"Наведение завершено!")
return True
# src/wpreal/core/mouse.py
from abc import ABC, abstractmethod
import serial
import ctypes
import time
from typing import Optional
class MouseController(ABC):
@abstractmethod
def move(self, dx: int, dy: int):
pass
@abstractmethod
def click(self, button: str = "left"):
pass
class ArduinoMouse(MouseController):
def __init__(self, ser: serial.Serial = None):
self.ser = ser
if self.ser:
print(f" Arduino готов к работе!")
def move(self, dx: int, dy: int):
if self.ser is None:
return
dx = max(-127, min(127, dx))
dy = max(-127, min(127, dy))
try:
self.ser.write(f"{dx},{dy}\n".encode())
except Exception as e:
print(f"Ошибка отправки: {e}")
def click(self, button: str = "left"):
if self.ser is None:
return
try:
self.ser.write(b"CLICK\n")
except Exception:
pass
Python
from abc import ABC, abstractmethod
import serial
import ctypes
import time
from typing import Optional
class MouseController(ABC):
@abstractmethod
def move(self, dx: int, dy: int):
pass
@abstractmethod
def click(self, button: str = "left"):
pass
class ArduinoMouse(MouseController):
def __init__(self, ser: serial.Serial = None):
self.ser = ser
if self.ser:
print(f" Arduino готов к работе!")
def move(self, dx: int, dy: int):
if self.ser is None:
return
dx = max(-127, min(127, dx))
dy = max(-127, min(127, dy))
try:
self.ser.write(f"{dx},{dy}\n".encode())
except Exception as e:
print(f"Ошибка отправки: {e}")
def click(self, button: str = "left"):
if self.ser is None:
return
try:
self.ser.write(b"CLICK\n")
except Exception:
pass
Python
Код:
# src/wpreal/core/mouse.py
from abc import ABC, abstractmethod
import serial
import ctypes
import time
from typing import Optional
class MouseController(ABC):
@abstractmethod
def move(self, dx: int, dy: int):
pass
@abstractmethod
def click(self, button: str = "left"):
pass
class ArduinoMouse(MouseController):
def __init__(self, ser: serial.Serial = None):
self.ser = ser
if self.ser:
print(f" Arduino готов к работе!")
def move(self, dx: int, dy: int):
if self.ser is None:
return
dx = max(-127, min(127, dx))
dy = max(-127, min(127, dy))
try:
self.ser.write(f"{dx},{dy}\n".encode())
except Exception as e:
print(f"Ошибка отправки: {e}")
def click(self, button: str = "left"):
if self.ser is None:
return
try:
self.ser.write(b"CLICK\n")
except Exception:
pass
#include <Mouse.h>
String inputString = "";
void setup() {
Serial.begin(115200);
Mouse.begin();
}
void loop() {
while (Serial.available()) {
char c = Serial.read();
if (c == '\n') {
int comma = inputString.indexOf(',');
if (comma != -1) {
int dx = inputString.substring(0, comma).toInt();
int dy = inputString.substring(comma + 1).toInt();
Mouse.move(dx, dy);
}
inputString = "";
} else {
inputString += c;
}
}
C
String inputString = "";
void setup() {
Serial.begin(115200);
Mouse.begin();
}
void loop() {
while (Serial.available()) {
char c = Serial.read();
if (c == '\n') {
int comma = inputString.indexOf(',');
if (comma != -1) {
int dx = inputString.substring(0, comma).toInt();
int dy = inputString.substring(comma + 1).toInt();
Mouse.move(dx, dy);
}
inputString = "";
} else {
inputString += c;
}
}
C
Код:
#include <Mouse.h>
String inputString = "";
void setup() {
Serial.begin(115200);
Mouse.begin();
}
void loop() {
while (Serial.available()) {
char c = Serial.read();
if (c == '\n') {
int comma = inputString.indexOf(',');
if (comma != -1) {
int dx = inputString.substring(0, comma).toInt();
int dy = inputString.substring(comma + 1).toInt();
Mouse.move(dx, dy);
}
inputString = "";
} else {
inputString += c;
}
}
[CODE=python]# src/wpreal/core.py
import threading
import keyboard
import time
from pynput.mouse import Listener, Button
from src.wprealOOP.config import *
from src.wprealOOP.core.camera import CameraCapture
from src.wprealOOP.core.detector import YOLODetector
from src.wprealOOP.core.mouse import ArduinoMouse
from src.wprealOOP.aimbot.trajectory import TrajectoryGenerator
from src.wprealOOP.aimbot.aimbot import Aimbot
from src.wprealOOP.gui.overlay import GUIOverlay
from src.wprealOOP.utils.arduino_initialization import connect_to_arduino
class Wpreal:
def __init__(self):
self._init_components()
self._setup_hotkeys()
self._start_threads()
self._print_info()
self._update_loop()
def _init_components(self):
self.running = True
self.camera = CameraCapture(fps=TARGET_FPS)
self.camera.start()
self.detector = YOLODetector(
MODEL_PATH,
CONF,
IMGSZ
)
ser = connect_to_arduino(ARDUINO_BAUD_RATE)
if ser:
self.mouse = ArduinoMouse(ser)
print("Arduino готов к работе!")
else:
print("Arduino НЕ ПОДКЛЮЧЕН! Выход")
exit(1)
self.trajectory = TrajectoryGenerator(
TRAJECTORY_MODEL_PATH,
SCREEN_WIDTH,
SCREEN_HEIGHT
)
self.aimbot = Aimbot(
self.mouse,
self.trajectory,
SCREEN_WIDTH,
SCREEN_HEIGHT,
FOV_RADIUS
)
# GUI
self.gui = GUIOverlay(
SCREEN_WIDTH,
SCREEN_HEIGHT
)
self.mouse_listener = None
self._last_update_time = time.time()
def _setup_hotkeys(self):
keyboard.add_hotkey('f', self.aimbot.activate)
keyboard.add_hotkey('F', self.aimbot.activate)
keyboard.add_hotkey('k', self.quit)
keyboard.add_hotkey('K', self.quit)
def _start_threads(self):
threading.Thread(target=self._detection_loop, daemon=True).start()
self.mouse_listener = Listener(on_click=self._on_mouse_click)
self.mouse_listener.start()
def _detection_loop(self):
last_detection_time = 0
while self.running:
current_time = time.time()
if current_time - last_detection_time < YOLO_MIN_INTERVAL:
time.sleep(SPIN_SLEEP)
continue
frame = self.camera.get_frame()
if frame is None:
time.sleep(IDLE_SLEEP)
continue
try:
detections = self.detector.detect(frame)
self.gui.update(detections)
if self.aimbot.enabled and self.aimbot.active and detections:
target = self.aimbot.find_best_target(detections)
if target:
self.aimbot.aim_at(target)
last_detection_time = current_time
except Exception as e:
print(f"Ошибка в detection_loop: {e}")
time.sleep(IDLE_SLEEP)
def _on_mouse_click(self, x, y, button, pressed):
if pressed and (button == Button.x1 or button == Button.x2):
self.aimbot.activate()
def _update_loop(self):
if not self.running:
return
now = time.time()
if now - self._last_update_time >= 1.0:
self._last_update_time = now
capture_fps = self.camera.get_fps()
yolo_fps = self.detector.get_fps()
gui_fps = self.gui.get_fps()
print(f"\033[F\033[F\033[F", end="")
print(f"\033[KЗахват: {capture_fps} FPS")
print(f"\033[KYOLO: {yolo_fps} FPS")
print(f"\033[KGUI: {gui_fps} FPS")
if self.running:
self.gui.root.after(UPDATE_INTERVAL_MS, self._update_loop)
def _print_info(self):
print("=" * 50)
print(" Wpreal запущен!")
print(f"Модель: {MODEL_PATH}")
print(f"Порог: {CONF}")
print(f" Размер: {IMGSZ}")
print(f" FOV: {FOV_RADIUS}px")
print("=" * 50)
print(" F или боковая кнопка мыши для наведения")
print(" K для выхода")
print()
print(" Захват: 0 FPS")
print(" YOLO: 0 FPS")
print(" GUI: 0 FPS")
def quit(self, event=None):
print("\nЗавершение работы...")
self.running = False
self.camera.stop()
self.gui.stop()
if hasattr(self.mouse, 'ser') and self.mouse.ser:
try:
self.mouse.ser.close()
print("Соединение с Arduino закрыто")
except:
pass
if self.mouse_listener:
self.mouse_listener.stop()
self.gui.root.quit()
self.gui.root.destroy()
def run(self):
try:
self.gui.start()
except KeyboardInterrupt:
self.quit()
finally:
self.running = False
print("\nWpreal остановлен")
if __name__ == "__main__":
app = Wpreal()
app.run()[/CODE]
import threading
import keyboard
import time
from pynput.mouse import Listener, Button
from src.wprealOOP.config import *
from src.wprealOOP.core.camera import CameraCapture
from src.wprealOOP.core.detector import YOLODetector
from src.wprealOOP.core.mouse import ArduinoMouse
from src.wprealOOP.aimbot.trajectory import TrajectoryGenerator
from src.wprealOOP.aimbot.aimbot import Aimbot
from src.wprealOOP.gui.overlay import GUIOverlay
from src.wprealOOP.utils.arduino_initialization import connect_to_arduino
class Wpreal:
def __init__(self):
self._init_components()
self._setup_hotkeys()
self._start_threads()
self._print_info()
self._update_loop()
def _init_components(self):
self.running = True
self.camera = CameraCapture(fps=TARGET_FPS)
self.camera.start()
self.detector = YOLODetector(
MODEL_PATH,
CONF,
IMGSZ
)
ser = connect_to_arduino(ARDUINO_BAUD_RATE)
if ser:
self.mouse = ArduinoMouse(ser)
print("Arduino готов к работе!")
else:
print("Arduino НЕ ПОДКЛЮЧЕН! Выход")
exit(1)
self.trajectory = TrajectoryGenerator(
TRAJECTORY_MODEL_PATH,
SCREEN_WIDTH,
SCREEN_HEIGHT
)
self.aimbot = Aimbot(
self.mouse,
self.trajectory,
SCREEN_WIDTH,
SCREEN_HEIGHT,
FOV_RADIUS
)
# GUI
self.gui = GUIOverlay(
SCREEN_WIDTH,
SCREEN_HEIGHT
)
self.mouse_listener = None
self._last_update_time = time.time()
def _setup_hotkeys(self):
keyboard.add_hotkey('f', self.aimbot.activate)
keyboard.add_hotkey('F', self.aimbot.activate)
keyboard.add_hotkey('k', self.quit)
keyboard.add_hotkey('K', self.quit)
def _start_threads(self):
threading.Thread(target=self._detection_loop, daemon=True).start()
self.mouse_listener = Listener(on_click=self._on_mouse_click)
self.mouse_listener.start()
def _detection_loop(self):
last_detection_time = 0
while self.running:
current_time = time.time()
if current_time - last_detection_time < YOLO_MIN_INTERVAL:
time.sleep(SPIN_SLEEP)
continue
frame = self.camera.get_frame()
if frame is None:
time.sleep(IDLE_SLEEP)
continue
try:
detections = self.detector.detect(frame)
self.gui.update(detections)
if self.aimbot.enabled and self.aimbot.active and detections:
target = self.aimbot.find_best_target(detections)
if target:
self.aimbot.aim_at(target)
last_detection_time = current_time
except Exception as e:
print(f"Ошибка в detection_loop: {e}")
time.sleep(IDLE_SLEEP)
def _on_mouse_click(self, x, y, button, pressed):
if pressed and (button == Button.x1 or button == Button.x2):
self.aimbot.activate()
def _update_loop(self):
if not self.running:
return
now = time.time()
if now - self._last_update_time >= 1.0:
self._last_update_time = now
capture_fps = self.camera.get_fps()
yolo_fps = self.detector.get_fps()
gui_fps = self.gui.get_fps()
print(f"\033[F\033[F\033[F", end="")
print(f"\033[KЗахват: {capture_fps} FPS")
print(f"\033[KYOLO: {yolo_fps} FPS")
print(f"\033[KGUI: {gui_fps} FPS")
if self.running:
self.gui.root.after(UPDATE_INTERVAL_MS, self._update_loop)
def _print_info(self):
print("=" * 50)
print(" Wpreal запущен!")
print(f"Модель: {MODEL_PATH}")
print(f"Порог: {CONF}")
print(f" Размер: {IMGSZ}")
print(f" FOV: {FOV_RADIUS}px")
print("=" * 50)
print(" F или боковая кнопка мыши для наведения")
print(" K для выхода")
print()
print(" Захват: 0 FPS")
print(" YOLO: 0 FPS")
print(" GUI: 0 FPS")
def quit(self, event=None):
print("\nЗавершение работы...")
self.running = False
self.camera.stop()
self.gui.stop()
if hasattr(self.mouse, 'ser') and self.mouse.ser:
try:
self.mouse.ser.close()
print("Соединение с Arduino закрыто")
except:
pass
if self.mouse_listener:
self.mouse_listener.stop()
self.gui.root.quit()
self.gui.root.destroy()
def run(self):
try:
self.gui.start()
except KeyboardInterrupt:
self.quit()
finally:
self.running = False
print("\nWpreal остановлен")
if __name__ == "__main__":
app = Wpreal()
app.run()[/CODE]
Если очень коротко.
1. DXCam захватывает кадр.2. Кадр сохраняется в CameraCapture._latest_frame.
3. Detection_loop берет кадр из переменной.
4.YOLO обрабатывает кадр.
5. GUI отрисовывает боксы.
6.Aimbot находит бокс.
7.HumanizedTrajectoryGen.onnx генерирует траекторию.
8.Arduino передвигает курсор.
Выводы.
Чит клево наводится на попки персонажей. CV значит «cock’s vision» или «петушиный взор».И я не знаю детектится он или нет. Где то читал, что ардуино нужно скрывать, кароче нельзя его реализовать как это делаю я в статье. Я только отрисовкой боксов в игре пользуюсь где то месяц и продолжаю собирать датасет, это вроде не палевно.

Комментарии 0