Как записать состояние контроллера xbox / gamepad в Python? - PullRequest
0 голосов
/ 16 декабря 2018

Мне нужно знать в определенное время значение всех кнопок контроллера 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

Ответы [ 2 ]

0 голосов
/ 23 декабря 2018

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

0 голосов
/ 17 декабря 2018

Мне кажется, что я делаю этот путь сложнее, чем нужно.

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

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

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

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

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

...