Как записать состояние контроллера Xbox / геймпада в Python?

Мне нужно знать в определенное время значение всех кнопок контроллера Xbox. Причина в том, что я создаю обучающий набор для нейронной сети, и я пытаюсь одновременно сделать снимок экрана и сделать «снимок» состояния контроллера. Обратите внимание, что я смог успешно сделать это для версии этого проекта с клавиатурой, но контроллер xbox вызывает у меня трудности.

Я пробовал создать словарь кнопок и значений и обновлять словарь каждый раз, когда я получаю событие от контроллера. Затем я бы сохранил изображение и словарь как образец данных для обучения. Однако входные данные вообще не синхронизируются с изображениями. Я думаю, что проблема может быть связана с потоками или подпроцессами в одном из пакетов, используемых для чтения контроллера, но я недостаточно опытен, чтобы знать, как это исправить.

Ниже мой код.

from inputs import get_gamepad
import time
import cv2
import numpy as np
from mss.windows import MSS as mss

#Only track relevant inputs
gp_state = {#'ABS_HAT0X' : 0, #-1 to 1
             #'ABS_HAT0Y' : 0, #-1 to 1
             #'ABS_RX' : 0, #-32768 to 32767
             #'ABS_RY' : 0, #-32768 to 32767
             'ABS_RZ' : 0, #0 to 255
             'ABS_X' : 0, #-32768 to 32767
             'ABS_Y' : 0, #-32768 to 32767
             #'ABS_Z' : 0, #0 to 255
             'BTN_EAST' : 0,
             'BTN_NORTH' : 0,
             #'BTN_SELECT' : 0,
             'BTN_SOUTH' : 0,
             #'BTN_START' : 0,
             #'BTN_THUMBL' : 0,
             #'BTN_THUMBR' : 0,
             'BTN_TL' : 0,
             'BTN_TR' : 0,
             'BTN_WEST' : 0,
             #'SYN_REPORT' : 0,
             }

dead_zone = 7500

def screen_record(): 
    last_time = time.time()
    while(True):
        # 800x600 windowed mode
        printscreen =  np.array(ImageGrab.grab(bbox=(0,40,800,640)))
        last_time = time.time()
        cv2.imshow('window',cv2.cvtColor(printscreen, cv2.COLOR_BGR2RGB))
        if cv2.waitKey(25) & 0xFF == ord('q'):
            cv2.destroyAllWindows()
            break

def process_img(image):
    original_image = image
    processed_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    contrast = 1
    brightness = 0
    out = cv2.addWeighted(processed_img, contrast, processed_img, 0, brightness)
    return out

def main():

    #Give myself time to switch windows
    #Screen should be in top left
    for _ in range(4):
        time.sleep(1)

    controller_input = np.zeros(5)
    training_data = []
    training_files = 0

    with mss() as sct:
        while True:
            #Get screen and display
            bbox = (150,240,650,490)
            screen =  np.array(sct.grab(bbox))
            new_screen = process_img(screen)
            cv2.imshow('window', new_screen)
            new_screen = cv2.resize(new_screen, (100,50))

            #Map events to dictionary
            events = get_gamepad()
            for event in events:
                gp_state[event.code] = event.state

            #Set to zero if in dead zone
            if abs(gp_state['ABS_X']) < dead_zone:
                gp_state['ABS_X'] = 0 

            if abs(gp_state['ABS_Y']) < dead_zone:
                gp_state['ABS_Y'] = 0 

            #Set values to be between 0 and 1.
            controller_input[0] = (gp_state['ABS_X'] + 32768) / (32767 + 32768)
            controller_input[1] = gp_state['ABS_RZ'] / 255
            controller_input[2] = gp_state['BTN_SOUTH']
            controller_input[3] = gp_state['BTN_EAST']
            controller_input[4] = gp_state['BTN_TR']


            record = gp_state['BTN_NORTH'] #Record while holding y button
            if record:
                training_data.append(np.array([new_screen, controller_input]))
                print(controller_input)
                time.sleep(1)

            if len(training_data) % 500 == 0 and record:
                filename = f"training_data/rlb_XBOXtrain_{time.time()}.npy"
                np.save(filename, training_data)
                training_files += 1
                print(f"Trained {training_files} files!")
                training_data = []


            if cv2.waitKey(25) & 0xFF == ord('q'):
                cv2.destroyAllWindows()
                break

main()

Я чувствую, что делаю это труднее, чем нужно. Но есть ли более простой способ просто получить состояние контроллера в определенный момент времени?

Обратите внимание, что я нашел несколько решений, которые работают для Linux, но я работаю в Windows 10. Вот пример решения для Linux: https://github.com/FRC4564/Xbox

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
2 729
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

I feel like I am making this way harder than it needs to be.

Нет, на самом деле это сложно. Это сложно, потому что вам не нужно, чтобы просто знал, какое состояние геймпада в конкретное время, вы также хотите знать, какое состояние геймпада использовалось для рисования определенного кадра. Время выборки состояния геймпада всегда будет раньше, чем время отрисовки кадра, и может быть отложено из-за задержки, добавленной самим приложением. Добавленная задержка может быть постоянной для всего приложения или может варьироваться в разных частях приложения. Это не то, что можно легко объяснить.

Ваш скрипт python записывает входные данные геймпада, как только они получены, поэтому я ожидаю, что он всегда будет запускаться по крайней мере на один или два кадра перед снимками экрана.

I'm thinking that the issue might be related to threading or subprocesses in one of the packages used to read the controller, but I'm not skilled enough to know how to fix it.

Вероятно, это просто задержка, добавленная кодом ввода геймпада в приложении, которое вы измеряете, а не то, что можно исправить. Большинство приложений не пытаются отвечать на входы геймпада сразу после их получения, а вместо этого обрабатывают их все сразу на этапе покадрового обновления. В среднем это добавляет задержку, равную половине частоты кадров.

Как это исправить? Я думаю, что измерить состояние геймпада из другого приложения будет сложно из-за проблем с задержкой. Если вы можете, было бы лучше настроить приложение для записи состояния геймпада во время его основного цикла, чтобы вы знали, что записываете то, что на самом деле было использовано. В Windows это должно быть возможно, предоставив вашу собственную версию XInput DLL, которая может записывать текущее состояние при каждом вызове XInputGetState.

Ответ принят как подходящий

проект TensorKart уже решил эту проблему: https://github.com/kevinhughes27/TensorKart/blob/master/utils.py

Другие вопросы по теме