A fork of ugo-nama-kun's gym_torcs environment with humble improvements such as:
xautomation
: the environment can be started virtually headlessly, skipping the GUI part.gym.make("Torcs-v0")
,rank
argument when creating the env.This wrapper requires a specific build of the Torcs Binaries, which can be found at https://github.com/dosssman/gym_torqs/tree/torcs_raceconfig .
The deps_install_script.sh
script automates the installation of critical dependencies as well as the Torcs binaries themselves.
The installation script was tested on:
pip install -e git+https://github.com/dosssman/GymTorcs#egg=gym_torcs
If you use Conda for virtual environment management, not that you can also add the line - git+https://github.com/dosssman/GymTorcs#egg=gym_torcs
to have it install it in a similar fashion.
Clone this repository, or your own fork of it:
git clone https://github.com/dosssman/GymTorcs.git && cd GymTorcs
then install the local version
pip install -e .
pip install gym-torcs
Note: This git repository is likely to be more up to date.
Instantiate the Torcs Gym Ennvironment using gym.make("Torcs-v0"), and optionnaly passing more parameters. (Parameter list coming soon)
import gym
# Gym Torcs dep.
import gym_torcs
try:
# Instantiate the environment
env = gym.make( "Torcs-v0")
o ,r, done = env.reset(), 0., False
while not done:
action = np.tanh(np.random.randn(env.action_space.shape[0]))
o, r, done, _ = env.step( action)
excpet Execption as e:
print( e)
finally:
env.end()
By default, the observations are provided as a dictionay of values.
Therefore, to fit the observation format to your experiement requirements,
use the obs_preprocess_fn
parameter of gym.make() to pass a customized
preprocessing function.
The function should take one argument called dict_obs
and return an array,
or whatever observation format you might require, based on the dictionary of
observation values.
Here is an example:
# Builds an array with observations such as angle, track, speeds, etc...
def obs_preprocess_fn(dict_obs):
return np.hstack((dict_obs['angle'],
dict_obs['track'],
dict_obs['trackPos'],
dict_obs['speedX'],
dict_obs['speedY'],
dict_obs['speedZ'],
dict_obs['wheelSpinVel'],
dict_obs['rpm'],
dict_obs['opponents']))
Another one:
# Return only the agent's FOV as an RGB Image
def obs_preprocess_fn( dict_obs):
return dict_obs['img']
Then pass it during the environment creation:
env = gym.make( 'Torcs-v0', vision=vision, obs_preprocess_fn=obs_preprocess_fn)
Parameter | Values | Desc. |
---|---|---|
rendering | True,False | Disables rendering in the simulation |
torcs_rank | 0,1,2... | Defines listening port. Use for parallelization |
throttle | True, False | Acceleration enabled or not |
gear_change | True, False | Gear change enabled or not |
race_config_path | /path/to/.../.xml | Path to the track conf file |
race_speed | ||
obs_vars | ["angle", ...] | Format of desired observation |
obs_preprocess_fn | def obs_preprocess_fn | COming soon ... |
obs_normalization | True, False | Normalize the obs. values |
... | ... | ... |
To disable rendering during training, just need to pass rendering=False
when instantiating the environment.
For example:
env = gym.make( 'Torcs-v0', vision=False, rendering=False, obs_preprocess_fn=obs_preprocess_fn)
Note, however, that once disable, the agent cannot be training with pixel-based observations.
Also, despite the rendering being disabled, a window will keep popping up from time to time.
To mitigate it, use xvfb
so the black window is render on a virtual display:
xvfb-run -a -s "-screen $DISPLAY 640x480x24" python train.py
with the environment variable DISPLAY=:0
.
In case you need to record either a human's data or even that of a bot, there is a quicked hacked method that enables you to do so. Please note that it only works for a specific set of low-level observations, such as LIDAR-like sensor data. You might need to customize and rebuild the Torcs binary where the recording process is handled (for efficiency) to suit your need. Data recording also need to following additional setup steps:
export TORCS_DATA_DIR=/home/<your username>/player_data
for example.
vtorcs-RL-colors/src/interfaces/graphic.h
that was installed by the deps_install_script.sh
so as to change /home/z3r0
at Line 31 to match your path to the config files. (Default is ~/.torcs):#define GR_PARAM_FILE "/home/z3r0/.torcs/config/graph.xml" // Change this to /home/<your user name>/.torcs/config/graph.xml
Also change the /home/z3r0
in the file vtorcs-RL-colors/src/inferfaces/playerpref.h
to match your username, around line 28 to 33:
#define HM_DRV_FILE "/home/z3r0/.torcs/drivers/human/human.xml"
// dossman edit because Torcs couldn't autopmatically find the file
// #define HM_PREF_FILE "drivers/human/preferences.xml"
#define HM_PREF_FILE "/home/z3r0/.torcs/drivers/human/preferences.xml"
Make sure to rebuild the binaries by following the steps in the deps_install_script.sh
, so as to reflect the changes.
(In case you would want to make it more seemsless, you could try to use the getenv("HOME")
variable. The hard part is that the path to the config files is defined as a C macro, therefore making it impossible (?) to use that function to directly recover the user's home directory.)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE params SYSTEM "params.dtd">
<params name="Practice">
<section name="Header">
<attstr name="name" val="Practice"/>
<attstr name="description" val="Practice"/>
<attnum name="priority" val="100"/>
<attstr name="menu image" val="data/img/splash-practice.png"/>
<attstr name="run image" val="data/img/splash-run-practice.png"/>
</section>
<section name="Tracks">
<attnum name="maximum number" val="1"/>
<section name="1">
<attstr name="name" val="g-track-1"/>
<attstr name="category" val="road"/>
</section>
</section>
<section name="Races">
<section name="1">
<attstr name="name" val="Practice"/>
</section>
</section>
<section name="Practice">
<attnum name="laps" val="1"/>
<attstr name="type" val="practice"/>
<attstr name="starting order" val="drivers list"/>
<attstr name="restart" val="yes"/>
<attstr name="display mode" val="normal"/>
<attstr name="display results" val="yes"/>
<attnum name="distance" unit="km" val="0"/>
<section name="Starting Grid">
<attnum name="rows" val="1"/>
<attnum name="distance to start" val="200"/>
<attnum name="distance between columns" val="20"/>
<attnum name="offset within a column" val="10"/>
<attnum name="initial speed" unit="km/h" val="0"/>
<attnum name="initial height" unit="m" val="0.2"/>
</section>
</section>
<section name="Drivers">
<attnum name="maximum number" val="11"/>
<attstr name="focused module" val="human"/>
<attnum name="focused idx" val="1"/>
<section name="1">
<attnum name="idx" val="1"/>
<attstr name="module" val="human"/>
</section>
<section name="2">
<attnum name="idx" val="0"/>
<attstr name="module" val="fixed"/>
</section>
<section name="3">
<attnum name="idx" val="1"/>
<attstr name="module" val="fixed"/>
</section>
<section name="4">
<attnum name="idx" val="2"/>
<attstr name="module" val="fixed"/>
</section>
<section name="5">
<attnum name="idx" val="3"/>
<attstr name="module" val="fixed"/>
</section>
<section name="6">
<attnum name="idx" val="4"/>
<attstr name="module" val="fixed"/>
</section>
<section name="7">
<attnum name="idx" val="5"/>
<attstr name="module" val="fixed"/>
</section>
<section name="8">
<attnum name="idx" val="6"/>
<attstr name="module" val="fixed"/>
</section>
<section name="9">
<attnum name="idx" val="7"/>
<attstr name="module" val="fixed"/>
</section>
<section name="10">
<attnum name="idx" val="8"/>
<attstr name="module" val="fixed"/>
</section>
<section name="11">
<attnum name="idx" val="9"/>
<attstr name="module" val="fixed"/>
</section>
<!-- Initdist for first driver-->
<!-- Initdist for first driver-->
<attnum name="initdist_1" val="200"/>
<attnum name="initdist_2" val="300"/>
<attnum name="initdist_3" val="400"/>
<attnum name="initdist_4" val="500"/>
<attnum name="initdist_5" val="600"/>
<attnum name="initdist_6" val="700"/>
<attnum name="initdist_7" val="800"/>
<attnum name="initdist_8" val="850"/>
<attnum name="initdist_9" val="900"/>
<attnum name="initdist_10" val="1000"/>
<attnum name="initdist_11" val="1150"/>
</section>
<section name="Configuration">
<attnum name="current configuration" val="4"/>
<section name="1">
<attstr name="type" val="track select"/>
</section>
<section name="2">
<attstr name="type" val="drivers select"/>
</section>
<section name="3">
<attstr name="type" val="race config"/>
<attstr name="race" val="Practice"/>
<section name="Options">
<section name="1">
<attstr name="type" val="race length"/>
</section>
<section name="2">
<attstr name="type" val="display mode"/>
</section>
</section>
</section>
</section>
</params>
To record a human player for example, make sure you have the human added as a driver in the <section name="Drivers">
section.
Please take note of <section name=?>
as it is used later.
torcs -raceconfig $PWD/raceconfig_file.xml -rechum 0 -rectimesteplim 3600
-rechuman
to the section name the human driver was affected to.-rectimesteplim
argument to how many time steps you want to record. Assuming your computer can consistently simulate at 60 FPS, the data will also be recorded at the same frequency.obs.csv
.acs.csv
and obs.csv
. Additional preprocessing is required, and a starting point is provided in the csv2npy.zip file mentioned in the obs.csv
section.Configure Players
, then on Player
, and finally on Controls
.Depending on your system, the audio card might not be detected by Torcs, which for some reason, absolutely requires it.
A work around was found namely here: https://ubuntuforums.org/showthread.php?t=2173702 .
Having installed the pulseaudio-utils
package or equivalent for your system, prepend the padsp
command everytime you run the Torcs binary, or your training scripts.
Intuitively, it will create a virtual audio device which will fool the Torcs simulator to think there is actually one.
In case you would like to cite this repository, please do so using the following BIB data.
@misc{GymTorcs,
title={GymTorcs: An OpenAI Gym-style wrapper for the Torcs Racing Car Simulator},
author={Rousslan Fernand Julien Dossa},
year={2018}
}