[PYTHON] J'ai essayé de visualiser les données de course du jeu de course (Assetto Corsa) avec Plotly

introduction

J'aimais les jeux de course et jouais à Gran Turismo 5/6, mais après avoir déménagé à Assetto Corsa, j'ai réalisé que je ne courais pas très bien. Plus précisément, la situation est que le véhicule AI (Force = 100%) est séparé de près de 4 secondes par tour (lors de l'utilisation du circuit de Catalunya / TOYOTA GT86, manette de jeu). En GT6, j'ai obtenu tout l'or avec une assistance OFF autre que ABS = 1 / TCS = 1, donc je pensais que je n'étais pas si mal ...

Afin d'analyser pourquoi il y a une telle différence, j'ai également étudié l'outil de visualisation, acquis les données de fonctionnement de mon jeu et de mon jeu AI avec Python, et je les ai visualisés avec Plotly.

À propos, il existe un outil appelé Motec i2 Pro pour l'acquisition et la visualisation de données. Cette fois, j'étudie également des outils de visualisation, donc je n'utiliserai pas Motec.

L'acquisition des données

Assetto Corsa dispose d'un mécanisme appelé «application en jeu» qui permet aux utilisateurs de développer indépendamment des applications qui affichent des données de conduite sur l'écran de jeu en utilisant le langage Python. Des API d'acquisition de données de conduite et des API à afficher à l'écran sont préparées.

Sur la base de ces informations de référence, j'ai réalisé un programme pour acquérir les informations suivantes.

--Nombre de tours

ACTelemetry.py

ACTelemetry.py



class ACTelemetry:

(Abréviation)

    def logging(self):
        if self.outputFile == None:
            return
        
        lapCount = ac.getCarState(self.carId, acsys.CS.LapCount) + 1
        lapTime = ac.getCarState( self.carId, acsys.CS.LapTime)
        speed = ac.getCarState(self.carId, acsys.CS.SpeedKMH)
        throttle = ac.getCarState(self.carId, acsys.CS.Gas)
        brake = ac.getCarState(self.carId, acsys.CS.Brake)
        gear = ac.getCarState(self.carId, acsys.CS.Gear)
        rpm = ac.getCarState(self.carId, acsys.CS.RPM)
        distance = ac.getCarState(self.carId, acsys.CS.NormalizedSplinePosition)
        steer = ac.getCarState(self.carId, acsys.CS.Steer)
        (x, y, z) = ac.getCarState(self.carId, acsys.CS.WorldPosition)
        
        self.outputFile.write('{}\t{:.3f}\t{:.4f}\t{:.2f}\t{:.3f}\t{:.3f}\t{}\t{:.0f}\t{:.1f}\t{:.2f}\t{:.2f}\t{:.2f}\n'.format(\
        lapCount, lapTime/1000, distance, speed, throttle, brake, 
        gear, rpm, steer, x, y, z))
        
(Abréviation)

def acUpdate(deltaT):
    global telemetryInstance
    telemetryInstance.logging()

Je n'entrerai pas dans les détails du code, mais le mécanisme d'application du jeu exécute ʻacUpdate (deltaT) chaque fois qu'un graphique est mis à jour (60 fois par seconde dans mon environnement). L'acquisition des données et la sortie des fichiers sont effectuées dans ʻACTelemetry.logging () appelé depuis ʻacUpdate (deltaT)`.

Pour activer cette application en jeu, procédez comme suit:

  1. Placez ACTelemetry.py sous "(dossier d'installation Steam) \ steamapps \ common \ asserttocorsa \ apps \ python \ AC Telemetry"
  2. Cochez "ACTelemetry" dans "Options" ⇒ "General" ⇒ "UI Models" dans le jeu
  3. Déplacez la souris vers le bord droit de l'écran pendant le jeu (même pendant la relecture) et sélectionnez "ACT télémétrie" dans les applications affichées.

L'interface utilisateur suivante sera affichée. Cliquez sur le bouton "Suivant" pour sélectionner le véhicule pour lequel les données seront acquises, et cliquez sur le bouton "Démarrer" pour démarrer l'acquisition du journal. ui.png

En conséquence, les données suivantes peuvent être obtenues. J'aimerais obtenir et comparer ces données pour mon jeu et celui de l'IA.

logger_20190817_1257.log


Course : ks_barcelona
Layout : layout_gp
Car Id : 0
Driver : abe.masanori
Driver : ks_toyota_gt86

lapCount	lapTime	distance	speed	throttle	brake	gear	RPM	steer	x	y	z
1	151.829	0.9399	115.7	1.00	0.00	4	6425	33	490.4	-14.6	-436.3
1	151.846	0.9400	115.8	1.00	0.00	4	6425	33	490.5	-14.6	-435.7
1	151.862	0.9401	115.8	1.00	0.00	4	6421	33	490.5	-14.7	-435.2
1	151.879	0.9402	116.0	1.00	0.00	4	6425	33	490.6	-14.7	-434.7

Mise en forme des données avant la visualisation

Cette fois, j'aimerais visualiser les données en utilisant la bibliothèque Javascript de Plotly. Le fichier délimité par des tabulations ci-dessus avec en-tête peut être traité tel quel, mais c'est un peu gênant, alors ajoutez à l'avance le traitement et la mise en forme suivants.

--Supprimer l'en-tête (5 premières lignes d'informations et lignes d'article) --Supprimer les lignes de données autres que le tour concerné --Supprimer les 5 premières lignes et les 5 dernières lignes des données du tour correspondant (pour supprimer les données étranges qui se produisent avant et après le départ / objectif)

Le fichier ressemblera à celui ci-dessous.

my_data_before.js


my_data = [
    [2, 0.125, 0.0017, 155.96, 1.000, 0.000, 5, 6672, 0.0, 365.52, -18.43, -187.32],
    [2, 0.142, 0.0019, 155.96, 1.000, 0.000, 5, 6672, 0.0, 365.13, -18.43, -186.72],
    [2, 0.158, 0.0020, 156.11, 1.000, 0.000, 5, 6674, 0.0, 364.73, -18.43, -186.11],
    [2, 0.175, 0.0022, 156.11, 1.000, 0.000, 5, 6676, 0.0, 364.34, -18.44, -185.51],
    (Ce qui suit est omis)

Visualisation des données acquises

Je voudrais faire la visualisation suivante en utilisant Plotly. Vous pouvez en fait le déplacer depuis (here. Il est un peu lourd, mais le GIF animé est [ici](https: // abe-masanori. github.io/AC_analysis/data_viz/ui.gif))

Normalement, ce type de données affiche l'axe horizontal = temps et l'axe vertical = métriques, mais si vous faites cela cette fois, il sera difficile de comparer vos propres données avec les données AI (CPU), donc l'axe horizontal commencera au début. Utilisez les valeurs numériques (0,0: début à 1,0: objectif) qui représentent la distance de.

goal.png

Création de fichier HTML

Commencez par créer un fichier HTML de base.

--Charger la bibliothèque de Plotly.

viz_before.html

viz_before.html


<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>moi même(Avant amélioration)Et comparaison de données AI</title>
        <script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
        <style>
            html,
            body {
                margin: 0;
                padding: 0;
                height: 100%;
                display: flex;
            }
            </style>
    </head>
    <body>
        <div>
            <div id="div-speed"></div>
            <div id="div-throttle"></div>
            <div id="div-brake"></div>
            <div id="div-gear"></div>
            <div id="div-rpm"></div>
            <div id="div-steer"></div>

        </div>
        <div id="div-position"></div>
    </body>
    <script src="data/my_data_before.js"></script>
    <script src="data/cpu_data.js"></script>
    <script src="my_viz.js"></script>
</html>

Création de graphiques de vitesse, etc.

Étant donné que les graphiques pour la vitesse, l'ouverture de l'accélérateur / frein, le rapport, la vitesse du moteur et l'angle de braquage sont presque les mêmes, un graphique linéaire est transmis avec vos propres données, les données AI (CPU), la position de création du graphique et le titre de l'axe vertical comme arguments. Créez une fonction qui crée.

my_viz.js

my_viz.js


function plot_speed(my_x, my_y, cpu_x, cpu_y, divId, title_y){
    var data_me = {
        x: my_x,
        y: my_y,
        mode: 'lines',
        name: 'me'
    };

    var data_cpu = {
        x: cpu_x,
        y: cpu_y,
        mode: 'lines',
        name: 'cpu'
    };

    var layout = {
        autosize: false,
        yaxis: {title: title_y},
        width: 600,
        height: 250,
        margin: {l: 70, r: 70, b: 25, t: 25}
    };

    Plotly.newPlot(divId, [data_me, data_cpu], layout);
}

my_data_distance = Array.from(my_data, x => x[2]);
cpu_data_distance = Array.from(cpu_data, x => x[2]);

my_data_speed = Array.from(my_data, x => x[3]);
cpu_data_speed = Array.from(cpu_data, x => x[3]);
plot_speed(
    my_data_distance, my_data_speed, cpu_data_distance, cpu_data_speed, 
    'div-speed', 'la vitesse(km/h)'
);
(Ce qui suit est omis)

C'est un peu ennuyeux de ne pas pouvoir passer le tableau bidimensionnel tel quel.

Création d'un graphique de données de position

Cela crée également une fonction de création de graphique.

my_viz.js


function plot_position(min_distance, max_distance) {
    my_x = Array.from(my_data.filter(v => (min_distance < v[2]) && (v[2] < max_distance)), x => x[9]);
    my_z = Array.from(my_data.filter(v => (min_distance < v[2]) && (v[2] < max_distance)), x => x[11]);

    my_pos = {
        x: my_x,
        y: my_z,
        mode: 'scatter',
        mode: 'line',
    };

    var layout = {
        xaxis: {autorange: false, range: [-600, 600]},
        yaxis: {autorange: false, range: [600, -600]},
        autosize: false,
        width: 300,
        height: 300,
        margin: {l: 50, r: 50, b: 50, t: 50, pad: 10},
        showlegend: false,
        images: [{
            source: 'pos_base.png',
            xref: 'x',
            yref: 'y',
            x: 500,
            y: 600,
            xanchor: 'right',
            yanchor: 'bottom',
            sizex: 1000,
            sizey: 1200,
            sizing: 'stretch',
            opacity: 0.4,
            layer: 'below'
        }]
    };

    Plotly.react('div-position', [my_pos], layout);
}

Coopération entre graphiques

Si vous sélectionnez une plage sur l'axe horizontal (Zoom) dans le graphique de vitesse, l'événement sera déclenché et le résultat sera reflété dans d'autres graphiques.

--Dans Plotly, le contenu de l'événement Zoom qui se produit lorsque vous sélectionnez une plage en faisant glisser et en déposant la souris est différent du contenu de l'événement Zoom qui se produit lorsque vous annulez le Zoom en double-cliquant sur le graphique, donc traiter avec ʻif..else ..` Diviser.

my_viz.js


document.querySelector('#div-speed').on(
    'plotly_relayout',
    function(eventdata) {
        if(eventdata['xaxis.autorange']) {
            x_start = 0.0;
            x_end = 1.0;
            option = {'xaxis.autorange': true};
        } else {
            x_start = eventdata['xaxis.range[0]'];
            x_end = eventdata['xaxis.range[1]'];
            option = {'xaxis.range': [x_start, x_end]}
        }

        Plotly.relayout('div-throttle', option);
        Plotly.relayout('div-brake', option);
        Plotly.relayout('div-gear', option);
        Plotly.relayout('div-rpm', option);
        Plotly.relayout('div-steer', option);

        plot_position(x_start, x_end);
    }
);

Confirmation des résultats de visualisation

Vous pouvez maintenant analyser avec le flux suivant.

  1. Regardez le graphique de vitesse et voyez où vous êtes plus lent que AI (CPU).
  2. Effectuez un zoom sur la pièce identifiée comme lente.
  3. Vérifiez les autres paramètres pour identifier la cause de la lenteur.

(Double-cliquez sur le graphique de vitesse pour annuler le zoom)

Eh bien, je n'avais pas besoin d'aller aussi loin pour voir pourquoi c'était lent, c'était juste que l'angle de braquage était trop grand. C'est simple une fois que la cause est connue, mais GT5 / 6 a une limite supérieure sur l'angle de braquage (il semble changer dynamiquement), il semble donc que la situation n'était pas si mauvaise.

newplot (1).png

En jouant avec la conscience que la cause était trop de pilotage, j'ai pu réduire la différence avec l'IA (CPU) de 4 secondes à 1 seconde ou moins. Cependant, lorsque je vérifie à nouveau les données, il semble que la direction est encore trop brusque et que le virage est raide, donc je dois encore m'améliorer.

[Comparaison des données entre moi-même (après amélioration) et AI](http: // localhost: 8000 / viz_after.html)

Impression compliquée

Cette fois, en visualisant les données, j'ai essayé les outils suivants autres que Plotly, alors donnez-moi une petite impression.

En résumé, Plotly est très bon.

Recommended Posts

J'ai essayé de visualiser les données de course du jeu de course (Assetto Corsa) avec Plotly
J'ai essayé de sauvegarder les données avec discorde
J'ai essayé de visualiser le texte du roman "Weather Child" avec Word Cloud
J'ai essayé de visualiser les informations spacha de VTuber
J'ai essayé de visualiser facilement les tweets de JAWS DAYS 2017 avec Python + ELK
J'ai essayé de trouver l'entropie de l'image avec python
[Courses de chevaux] J'ai essayé de quantifier la force du cheval de course
J'ai essayé de trouver la moyenne de plusieurs colonnes avec TensorFlow
[Python] J'ai essayé de visualiser la relation de suivi de Twitter
J'ai essayé de visualiser les caractéristiques des nouvelles informations sur les personnes infectées par le virus corona avec wordcloud
J'ai essayé de visualiser la consommation électrique de ma maison avec Nature Remo E lite
J'ai essayé d'analyser les données du tournoi de football de la Coupe du monde de football en Russie avec l'action de football
J'ai écrit un doctest dans "J'ai essayé de simuler la probabilité d'un jeu de bingo avec Python"
J'ai essayé de prédire les ventes de logiciels de jeux avec VARISTA en me référant à l'article du Codexa
J'ai essayé d'automatiser l'arrosage du pot avec Raspberry Pi
J'ai essayé de corriger "J'ai essayé la simulation probabiliste du jeu de bingo avec Python"
J'ai essayé d'agrandir la taille du volume logique avec LVM
J'ai essayé d'exécuter la partie DNN d'OpenPose avec le processeur Chainer
J'ai essayé d'améliorer l'efficacité du travail quotidien avec Python
J'ai essayé de visualiser la condition commune des téléspectateurs de la chaîne VTuber
J'ai essayé de visualiser AutoEncoder avec TensorFlow
[Python] J'ai essayé de visualiser le prix en argent de "ONE PIECE" plus de 100 millions de caractères avec matplotlib.
[Python] J'ai essayé de visualiser la nuit du chemin de fer de la galaxie avec WordCloud!
J'ai essayé de visualiser la tranche d'âge et la distribution des taux d'Atcoder
J'ai essayé d'obtenir le code d'authentification de l'API Qiita avec Python.
J'ai essayé d'extraire automatiquement les mouvements des joueurs Wiire avec un logiciel
J'ai essayé d'analyser la négativité de Nono Morikubo. [Comparer avec Posipa]
J'ai essayé de rationaliser le rôle standard des nouveaux employés avec Python
J'ai essayé de visualiser le modèle avec la bibliothèque d'apprentissage automatique low-code "PyCaret"
J'ai essayé d'obtenir les informations sur le film de l'API TMDb avec Python
J'ai essayé de visualiser tous les arbres de décision de la forêt aléatoire avec SVG
J'ai essayé de prédire le comportement du nouveau virus corona avec le modèle SEIR.
J'ai essayé d'obtenir des données CloudWatch avec Python
J'ai essayé de corriger la forme trapézoïdale de l'image
J'ai essayé de vectoriser les paroles de Hinatazaka 46!
J'ai essayé d'obtenir et d'analyser les données statistiques de la nouvelle Corona avec Python: données de l'Université John's Hopkins
Comme c'est le 20e anniversaire de la formation, j'ai essayé de visualiser les paroles de Parfum avec Word Cloud
J'ai essayé d'automatiser le dépôt de 100 yens des courses de chevaux Rakuten (python / sélénium)
J'ai couru le tutoriel TensorFlow avec des commentaires (classification du texte des critiques de films)
J'ai essayé de récupérer les données de l'ordinateur portable en le démarrant sur Ubuntu
L'histoire de la fabrication de soracom_exporter (j'ai essayé de surveiller SORACOM Air avec Prometheus)
J'ai essayé de créer un modèle avec l'exemple d'Amazon SageMaker Autopilot
J'ai essayé d'envoyer automatiquement la littérature du nouveau virus corona à LINE avec Python
J'ai essayé d'entraîner la fonction péché avec chainer
J'ai essayé d'extraire des fonctionnalités avec SIFT d'OpenCV
J'ai essayé de résumer la forme de base de GPLVM
J'ai essayé de toucher un fichier CSV avec Python
J'ai essayé de prédire le match de la J League (analyse des données)
J'ai essayé de résoudre Soma Cube avec python
J'ai essayé d'utiliser l'API de Sakenowa Data Project
J'ai essayé d'effacer la partie négative de Meros
J'ai essayé de résoudre le problème avec Python Vol.1
J'ai essayé de classer les voix des acteurs de la voix
J'ai essayé d'exécuter l'exemple de code du module Ansible
J'ai essayé de résumer les opérations de chaîne de Python
J'ai essayé de faire quelque chose comme un chatbot avec le modèle Seq2Seq de TensorFlow