Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/game/racing.py
169674 views
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
4
# Copyright (C) 2010-2025 German Aerospace Center (DLR) and others.
5
# This program and the accompanying materials are made available under the
6
# terms of the Eclipse Public License 2.0 which is available at
7
# https://www.eclipse.org/legal/epl-2.0/
8
# This Source Code may also be made available under the following Secondary
9
# Licenses when the conditions for such availability set forth in the Eclipse
10
# Public License 2.0 are satisfied: GNU General Public License, version 2
11
# or later which is available at
12
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
13
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
14
15
# @file racing.py
16
# @author Jakob Erdmann
17
# @date 2017-06-12
18
19
"""
20
This script runs an aribtrary sumo simulation and controls the specified vehicle
21
via keyboard input
22
"""
23
from __future__ import absolute_import
24
from __future__ import print_function
25
import os
26
import sys
27
import threading
28
import math
29
30
if sys.version_info.major == 3:
31
import queue as Queue
32
from tkinter import Button, Frame, Tk
33
else:
34
import Queue
35
from Tkinter import Button, Frame, Tk
36
37
GAME_DIR = os.path.dirname(__file__)
38
try:
39
sys.path.append(os.path.join(os.environ.get("SUMO_HOME", os.path.join(GAME_DIR, '..')), "tools"))
40
import sumolib # noqa
41
import traci # noqa
42
except ImportError:
43
sys.exit(
44
"please declare environment variable 'SUMO_HOME' as the root directory of your sumo installation " +
45
"(it should contain folders 'bin', 'tools' and 'docs')")
46
47
try:
48
import autopy # noqa
49
except ImportError:
50
sys.stderr.write("autopy not installed. Can only use keyboard control.\n")
51
autopy = None
52
53
# Check for playsound import
54
try:
55
import playsound # noqa
56
except ImportError:
57
sys.stderr.write("playsound not installed. Sounds will not be played on collisions.\n")
58
playsound = None
59
60
61
eventQueue = Queue.Queue()
62
TS = 0.05
63
VERBOSE = False
64
MAX_STEER_ANGLE = 5
65
MIN_SPEED = -5
66
MAX_OFFROAD_SPEED = 7
67
OFFROAD_DECEL_TIME = 2
68
# autopy = None # disable mouse control
69
70
71
def leftKey(event):
72
eventQueue.put(('left', None))
73
if VERBOSE:
74
print("Left key pressed")
75
76
77
def rightKey(event):
78
eventQueue.put(('right', None))
79
if VERBOSE:
80
print("Right key pressed")
81
82
83
def upKey(event):
84
eventQueue.put(('up', None))
85
if VERBOSE:
86
print("Up key pressed")
87
88
89
def downKey(event):
90
eventQueue.put(('down', None))
91
if VERBOSE:
92
print("Down key pressed")
93
94
95
def mouseControl(master, speed, steerAngle, accel=2.6, decel=4.5):
96
try:
97
centerX = master.winfo_screenwidth() / 2.0
98
centerY = master.winfo_screenheight() / 2.0
99
x, y = autopy.mouse.location()
100
dx = (x - centerX) / centerX
101
dy = (y - centerY) / centerY
102
if dy < 0:
103
speed -= dy * TS * accel
104
else:
105
speed -= dy * TS * decel
106
steerAngle = MAX_STEER_ANGLE * dx
107
except Exception: # as e:
108
# print(e)
109
pass
110
return speed, steerAngle
111
112
113
class RacingClient:
114
115
"""
116
Launch the main part of the GUI and the worker thread. periodicCall and
117
endApplication could reside in the GUI part, but putting them here
118
means that you have all the thread controls in a single place.
119
"""
120
121
def __init__(self, master, sumocfg, egoID):
122
self.master = master
123
self.sumocfg = sumocfg
124
self.egoID = egoID
125
self.running = True
126
127
self.thread = threading.Thread(target=self.workerThread)
128
self.thread.start()
129
# Start the periodic call in the GUI to see if it can be closed
130
self.periodicCall()
131
132
def periodicCall(self):
133
if not self.running:
134
sys.exit(1)
135
self.master.after(100, self.periodicCall)
136
137
def workerThread(self):
138
try:
139
traci.start([sumolib.checkBinary("sumo-gui"), "-c", self.sumocfg,
140
"--lateral-resolution", "0.32",
141
"--collision.action", "warn",
142
"--step-length", str(TS)])
143
# steal focus for keyboard input after sumo-gui has loaded
144
# self.master.focus_force() # not working on all platforms
145
# make sure ego vehicle is loaded
146
traci.simulationStep()
147
speed = traci.vehicle.getSpeed(self.egoID)
148
angle = traci.vehicle.getAngle(self.egoID)
149
traci.vehicle.setSpeedMode(self.egoID, 0)
150
steerAngle = 0
151
x, y = traci.vehicle.getPosition(self.egoID)
152
traci.gui.trackVehicle(traci.gui.DEFAULT_VIEW, self.egoID)
153
while traci.simulation.getMinExpectedNumber() > 0:
154
try:
155
if eventQueue.qsize() == 0:
156
if steerAngle > 0:
157
steerAngle = max(0, steerAngle - MAX_STEER_ANGLE * TS)
158
else:
159
steerAngle = min(0, steerAngle + MAX_STEER_ANGLE * TS)
160
# print("revert steerAngle=%.2f" % steerAngle)
161
while eventQueue.qsize():
162
try:
163
msg = eventQueue.get(0)
164
if len(msg) == 1:
165
direction = msg
166
val = None
167
else:
168
direction, val = msg
169
170
if direction == 'up':
171
if val is None:
172
val = 1
173
speed += val * TS * traci.vehicle.getAccel(self.egoID)
174
if direction == 'down':
175
if val is None:
176
val = 1
177
speed -= val * TS * traci.vehicle.getDecel(self.egoID)
178
if direction == 'left':
179
if val is None:
180
steerAngle -= TS * 5
181
else:
182
steerAngle = val
183
if direction == 'right':
184
if val is None:
185
steerAngle += TS * 5
186
else:
187
steerAngle = val
188
except Queue.Empty:
189
pass
190
if autopy:
191
speed, steerAngle = mouseControl(self.master, speed, steerAngle)
192
# move vehicle
193
# posLat = traci.vehicle.getLateralLanePosition(self.egoID)
194
if traci.vehicle.getLaneID(self.egoID) == "":
195
if abs(speed) > MAX_OFFROAD_SPEED:
196
sign = 1 if speed > 0 else -1
197
speed -= TS * sign * (abs(speed) - MAX_OFFROAD_SPEED) / OFFROAD_DECEL_TIME
198
199
speed = max(MIN_SPEED, min(speed, traci.vehicle.getMaxSpeed(self.egoID)))
200
steerAngle = min(MAX_STEER_ANGLE, max(-MAX_STEER_ANGLE, steerAngle))
201
angle += steerAngle
202
angle = angle % 360
203
rad = -angle / 180 * math.pi + 0.5 * math.pi
204
x2 = x + math.cos(rad) * TS * speed
205
y2 = y + math.sin(rad) * TS * speed
206
traci.vehicle.moveToXY(self.egoID, "dummy", -1, x2, y2, angle, keepRoute=2)
207
traci.vehicle.setSpeed(self.egoID, speed)
208
traci.vehicle.setLine(self.egoID, str(speed))
209
x3, y3 = traci.vehicle.getPosition(self.egoID)
210
x, y = x2, y2
211
212
# Check for collisions involving the ego car
213
if playsound:
214
if self.egoID in traci.simulation.getCollidingVehiclesIDList():
215
playsound.playsound(os.path.join(GAME_DIR, "sounds", "car_horn1.wav"), False)
216
217
traci.simulationStep()
218
if VERBOSE:
219
print(("old=%.2f,%.2f new=%.2f,%.2f found=%.2f,%.2f speed=%.2f steer=%.2f " +
220
"angle=%s rad/pi=%.2f cos=%.2f sin=%.2f") % (
221
x, y, x2, y2, x3, y3, speed, steerAngle, angle, rad / math.pi,
222
math.cos(rad), math.sin(rad)))
223
except traci.TraCIException:
224
pass
225
traci.close()
226
except traci.FatalTraCIError:
227
pass
228
self.running = False
229
230
231
def main(sumocfg="racing/racing.sumocfg", egoID="ego"):
232
root = Tk()
233
root.geometry('180x100+0+0')
234
frame = Frame(root)
235
Button(frame, text="Click here.\nControl with arrow keys").grid(row=0)
236
root.bind('<Left>', leftKey)
237
root.bind('<Right>', rightKey)
238
root.bind('<Up>', upKey)
239
root.bind('<Down>', downKey)
240
241
root.winfo_screenwidth()
242
root.winfo_screenheight()
243
244
frame.pack()
245
246
RacingClient(root, sumocfg, egoID)
247
root.mainloop()
248
249
250
if __name__ == "__main__":
251
parser = sumolib.options.ArgumentParser()
252
parser.add_argument('--sumocfg', default=os.path.join(GAME_DIR, "racing", "racing.sumocfg"),
253
help=".sumocfg file path")
254
parser.add_argument('--ego', default="ego", help="vehicle ego id")
255
parser.add_argument("-v", "--verbose", action="store_true", default=False,
256
help="tell me what you are doing")
257
parser.add_argument("-m", "--mouse", action="store_true", default=False,
258
help="use mouse features")
259
args = parser.parse_args()
260
261
VERBOSE = args.verbose
262
# Disabling mouse control unless explicitly mentioned
263
if not args.mouse:
264
autopy = None
265
main(args.sumocfg, args.ego)
266
267