[PYTHON] I tried to visualize the running data of the racing game (Assetto Corsa) with Plotly

Introduction

I loved racing games and was playing Gran Turismo 5/6, but after moving to Assetto Corsa I realized I wasn't running very well. Specifically, the situation is that the AI vehicle (Strength = 100%) is separated by nearly 4 seconds per lap (when using the Catalunya circuit / TOYOTA GT86, game pad). In GT6, I got all gold with assist OFF other than ABS = 1 / TCS = 1, so I thought that I was not so bad ...

In order to analyze why there is such a difference, I also studied the visualization tool, acquired the running data of my play and AI play with Python, and visualized it with Plotly.

By the way, there is a tool called Motec i2 Pro for data acquisition and visualization. This time, I'm also investigating visualization tools, so I won't use Motec.

Data acquisition

Assetto Corsa has a mechanism called "in-game app" that allows users to independently develop applications that display driving data on the game screen using the Python language. An API for acquiring driving data and an API for displaying on the screen are prepared.

Based on these reference information, I made a program to acquire the following information.

--Number of laps --Elapsed time (seconds) of the current lap --Distance from the start point (start point = 0 to goal point = 1.0) --Vehicle speed (km / h) --Accelerator opening (0.0-1.0) --Brake opening (0.0-1.0)

ACTelemetry.py

ACTelemetry.py



class ACTelemetry:

(Abbreviation)

    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))
        
(Abbreviation)

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

I won't go into the details of the code, but the in-game app mechanism runs ʻacUpdate (deltaT) every time the graphic is updated (60 times per second in my environment). Data acquisition and file output are performed in ʻACTelemetry.logging () called from ʻacUpdate (deltaT)`.

To enable this in-game app, do the following:

  1. Place ACTelemetry.py under "(Steam installation folder) \ steamapps \ common \ assetto corsa \ apps \ python \ AC Telemetry"
  2. Check "ACTelemetry" in "Options" ⇒ "General" ⇒ "UI Models" in the game
  3. Move the mouse to the right edge of the screen during game play (even during replay) and select "ACT telemetry" from the displayed apps.

The following UI will be displayed. Click the "Next" button to select the vehicle for which data will be acquired, and click the "Start" button to start log acquisition. ui.png

As a result, the following data can be obtained. I would like to get and compare this data for both my play and AI play.

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

Data shaping before visualization

This time, I would like to visualize the data using Plotly's Javascript library. The above tab-delimited file with header can be handled as it is, but it is a little troublesome, so add the following processing and shaping in advance.

--Delete header (first 5 lines of information and item lines) --Delete data rows other than the corresponding lap --Delete the first 5 lines and the last 5 lines of the data of the corresponding lap (to remove strange data that occurs before and after the start / goal) --Format conversion from tab delimited to Javascript array

The file will look like the one below.

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],
    (The following is omitted)

Visualization of acquired data

I would like to make the following Viz using Plotly. You can actually move it from (here. It's a bit heavy, but the animated GIF is [here](https://abe-masanori. github.io/AC_analysis/data_viz/ui.gif))

--For speed, accelerator / brake opening, gear, engine speed, and steering angle, display your own data and AI (CPU) data on the left side of the line graph with the distance from the start as the horizontal axis. --Display the running position on your course on the right side. --If you specify the horizontal axis range (Zoom) in the speed graph, other graphs will follow it.

Normally, this kind of data displays the horizontal axis = time and the vertical axis = metrics, but if you do this this time, it will be difficult to compare your own data with AI (CPU) data, so the horizontal axis will start from the start. The numerical value (0.0: start to 1.0: goal) representing the distance of is adopted.

goal.png

HTML file creation

First, create the base HTML file.

--Load the Plotly library. --Since the graph will be inserted into the div element with Plotly, add ʻid to the div` of the graph display.

viz_before.html

viz_before.html


<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>myself(Before improvement)And AI data comparison</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>

Creating speed graphs, etc.

Since the graphs for speed, accelerator / brake opening, gear, engine speed, and steering angle are almost the same, a line graph is passed by passing your own data, AI (CPU) data, graph creation position, and vertical axis title as arguments. Create a function that creates.

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', 'speed(km/h)'
);
(The following is omitted)

It feels a little annoying to not be able to pass the 2D array as it is.

Graphing position data

This also creates a graphing function.

--In order to narrow down by distance later, pass the lower and upper limits of the distance of the data to be displayed as arguments, and narrow down the data based on that information. --This graph will be recreated by swapping data, so use the Plotly.react () function instead of the Plotly.newPlot () function (from newPlot () according to Plotly's reference. It seems that react () is "far more efficiently", but I'm not surprised that only react () should be used ...) --Regarding the position data, the north-south (z) value is south on the plus side and north on the minus side, so the specification of range: [600, -600] is specified and the top and bottom are reversed. ʻAutorange:'reversed'` can also be flipped upside down, but when narrowing down the data, the axis range changes and the aspect ratio changes, so a fixed value is specified. --When narrowing down the data, set the course diagram as the background image of the graph so that it is easy to understand whether it corresponds to the degree part of the course.

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);
}

Cooperation between graphs

If you select the range on the horizontal axis (Zoom) in the speed graph, the event will be triggered and the result will be reflected in other graphs.

--In Plotly, the content of the Zoom event that occurs when you select a range by dragging and dropping the mouse and when you cancel the Zoom by double-clicking on the graph is different, so process with ʻif..else ..Divide. --Since the accelerator / brake opening, gear, engine speed, and steering angle only change the range of the horizontal axis, change the layout of the graph withPlotly.relayout (). --For the position graph, it is necessary to narrow down the display data, so recreate the graph (execute the plot_position ()` function created above).

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 of visualization results

Now you can analyze with the following flow.

  1. Look at the speed graph and see where you are slower than the AI (CPU).
  2. Zoom to the part identified as slow.
  3. Check other metrics to identify the cause of the slowness.

(Double-click the speed graph to cancel Zoom)

Well, I didn't have to go that far to see why it was slow, it was just that the steering angle was too big. It's simple once the cause is known, but GT5 / 6 has an upper limit on the steering angle (it seems to change dynamically), so it seems that the situation was not so bad.

newplot (1).png

By playing with the awareness that the cause was too much steering, I was able to reduce the difference with AI (CPU) from 4 seconds to less than 1 second. However, when I check the data again, it seems that the steering is still over-turned and the way to turn is steep, so I still have to improve.

[Comparison of data between myself (after improvement) and AI](http: //localhost: 8000/viz_after.html)

Plotly impressions

This time, in visualizing the data, I tried the following tools other than Plotly, so please give me a little impression.

In summary, Plotly is very good.

Recommended Posts

I tried to visualize the running data of the racing game (Assetto Corsa) with Plotly
I tried to save the data with discord
I tried to visualize the text of the novel "Weathering with You" with WordCloud
I tried to visualize the spacha information of VTuber
I tried to easily visualize the tweets of JAWS DAYS 2017 with Python + ELK
I tried to find the entropy of the image with python
[Horse Racing] I tried to quantify the strength of racehorses
I tried to find the average of the sequence with TensorFlow
[Python] I tried to visualize the follow relationship of Twitter
I tried to visualize the characteristics of new coronavirus infected person information with wordcloud
I tried to visualize the power consumption of my house with Nature Remo E lite
I tried to analyze the data of the soccer FIFA World Cup Russia tournament with soccer action
I wrote a doctest in "I tried to simulate the probability of a bingo game with Python"
I tried to predict the sales of game software with VARISTA by referring to the article of Codexa
I tried to automate the watering of the planter with Raspberry Pi
I tried to fix "I tried stochastic simulation of bingo game with Python"
I tried to expand the size of the logical volume with LVM
I tried running the DNN part of OpenPose with Chainer CPU
I tried to improve the efficiency of daily work with Python
I tried to visualize the common condition of VTuber channel viewers
I tried to visualize AutoEncoder with TensorFlow
[Python] I tried to visualize the prize money of "ONE PIECE" over 100 million characters with matplotlib.
[Python] I tried to visualize the night on the Galactic Railroad with WordCloud!
I tried to visualize the age group and rate distribution of Atcoder
I tried to get the authentication code of Qiita API with Python.
I tried to automatically extract the movements of PES players with software
I tried to analyze the negativeness of Nono Morikubo. [Compare with Posipa]
I tried to streamline the standard role of new employees with Python
I tried to visualize the model with the low-code machine learning library "PyCaret"
I tried to get the movie information of TMDb API with Python
I tried to visualize all decision trees of random forest with SVG
I tried to predict the behavior of the new coronavirus with the SEIR model.
I tried to touch the API of ebay
I tried to get CloudWatch data with Python
I tried to correct the keystone of the image
I tried to predict the price of ETF
I tried to vectorize the lyrics of Hinatazaka46!
I tried to open the latest data of the Excel file managed by date in the folder with Python
I tried to get and analyze the statistical data of the new corona with Python: Data of Johns Hopkins University
Since it is the 20th anniversary of the formation, I tried to visualize the lyrics of Perfume with Word Cloud
I tried to automate the 100 yen deposit of Rakuten horse racing (python / selenium)
I tried running the TensorFlow tutorial with comments (text classification of movie reviews)
I tried to rescue the data of the laptop by booting it on Ubuntu
The story of making soracom_exporter (I tried to monitor SORACOM Air with Prometheus)
I tried to create a model with the sample of Amazon SageMaker Autopilot
I tried to automatically send the literature of the new coronavirus to LINE with Python
I tried to learn the sin function with chainer
I tried to extract features with SIFT of OpenCV
I tried to summarize the basic form of GPLVM
I tried to touch the CSV file with Python
I tried to predict the J-League match (data analysis)
I tried to solve the soma cube with python
I tried using the API of the salmon data project
I tried to erase the negative part of Meros
I tried to solve the problem with Python Vol.1
I tried to analyze J League data with Python
I tried to classify the voices of voice actors
I tried running the sample code of the Ansible module
I tried to summarize the string operations of Python
I tried to make something like a chatbot with the Seq2Seq model of TensorFlow
I tried to put out the frequent word ranking of LINE talk with Python