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.
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
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:
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.
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
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)
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.
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
<!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>
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
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.
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);
}
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 with
Plotly.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);
}
);
Now you can analyze with the following flow.
(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.
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)
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