Repository navigation
Expand file tree
/
Copy pathauto_traffic.py
More file actions
executable file
·292 lines (244 loc) · 9.27 KB
/
Copy pathauto_traffic.py
File metadata and controls
executable file
·292 lines (244 loc) · 9.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
#!/usr/bin/env python3
"""
auto_traffic.py — CarlaAir 自动交通生成器 (v0.1.7)
启动后在异步模式下生成车辆和行人,保持后台运行。
定时健康检查:冻结的行人自动重启,车辆自动恢复。
Ctrl+C 或 SIGTERM 时自动清理所有生成的 actor。
Usage:
python3 auto_traffic.py [--vehicles 30] [--walkers 50] [--port 2000]
"""
import carla
import random
import time
import signal
import sys
import argparse
import logging
logging.basicConfig(format='[Traffic] %(message)s', level=logging.INFO)
log = logging.info
# ── Globals for cleanup ──
vehicles_list = []
walkers_list = [] # [{id, con}, ...]
all_walker_ids = []
client = None
world = None
running = True
def signal_handler(sig, frame):
global running
log("Received shutdown signal, cleaning up...")
running = False
def spawn_vehicles(world, client, count=30, tm_port=8000):
"""Spawn vehicles with autopilot via Traffic Manager (async mode)."""
bp_lib = world.get_blueprint_library()
vehicle_bps = bp_lib.filter('vehicle.*')
car_bps = [bp for bp in vehicle_bps if int(bp.get_attribute('number_of_wheels')) == 4]
if not car_bps:
car_bps = list(vehicle_bps)
spawn_points = world.get_map().get_spawn_points()
random.shuffle(spawn_points)
tm = client.get_trafficmanager(tm_port)
tm.set_global_distance_to_leading_vehicle(2.5)
tm.global_percentage_speed_difference(10.0)
batch = []
for sp in spawn_points[:count]:
bp = random.choice(car_bps)
if bp.has_attribute('color'):
bp.set_attribute('color', random.choice(bp.get_attribute('color').recommended_values))
bp.set_attribute('role_name', 'autopilot')
batch.append(
carla.command.SpawnActor(bp, sp)
.then(carla.command.SetAutopilot(carla.command.FutureActor, True, tm.get_port()))
)
results = client.apply_batch_sync(batch, False)
spawned = 0
for r in results:
if not r.error:
vehicles_list.append(r.actor_id)
spawned += 1
log(f"Vehicles: {spawned}/{count} spawned")
return spawned
def spawn_walkers(world, client, count=50):
"""Spawn pedestrians with AI controllers (async mode)."""
bp_lib = world.get_blueprint_library()
walker_bps = bp_lib.filter('walker.pedestrian.*')
controller_bp = bp_lib.find('controller.ai.walker')
spawn_points = []
for _ in range(count * 3):
loc = world.get_random_location_from_navigation()
if loc:
spawn_points.append(carla.Transform(loc))
if len(spawn_points) >= count:
break
batch = []
speeds = []
for sp in spawn_points[:count]:
bp = random.choice(walker_bps)
if bp.has_attribute('is_invincible'):
bp.set_attribute('is_invincible', 'false')
if bp.has_attribute('speed'):
if random.random() < 0.15:
speeds.append(float(bp.get_attribute('speed').recommended_values[2]))
else:
speeds.append(float(bp.get_attribute('speed').recommended_values[1]))
else:
speeds.append(1.4)
batch.append(carla.command.SpawnActor(bp, sp))
results = client.apply_batch_sync(batch, False)
valid_speeds = []
for i, r in enumerate(results):
if not r.error:
walkers_list.append({"id": r.actor_id})
valid_speeds.append(speeds[i])
batch_ctrl = []
for w in walkers_list:
batch_ctrl.append(
carla.command.SpawnActor(controller_bp, carla.Transform(), w["id"])
)
ctrl_results = client.apply_batch_sync(batch_ctrl, False)
for i, r in enumerate(ctrl_results):
walkers_list[i]["con"] = r.actor_id if not r.error else None
for w in walkers_list:
if w.get("con"):
all_walker_ids.append(w["con"])
all_walker_ids.append(w["id"])
world.wait_for_tick()
world.set_pedestrians_cross_factor(0.05)
all_actors = world.get_actors(all_walker_ids)
for i in range(0, len(all_walker_ids), 2):
try:
all_actors[i].start()
all_actors[i].go_to_location(world.get_random_location_from_navigation())
all_actors[i].set_max_speed(valid_speeds[i // 2] if i // 2 < len(valid_speeds) else 1.4)
except Exception:
pass
log(f"Walkers: {len(walkers_list)}/{count} spawned with AI controllers")
return len(walkers_list)
def health_check_walkers(world):
"""Reassign destinations to all walkers. Fixes frozen walkers."""
if not all_walker_ids:
return
try:
actors = world.get_actors(all_walker_ids)
reassigned = 0
for i in range(0, len(all_walker_ids), 2):
try:
ctrl = actors[i]
loc = world.get_random_location_from_navigation()
if loc and ctrl.is_alive:
ctrl.go_to_location(loc)
reassigned += 1
except Exception:
pass
return reassigned
except Exception:
return 0
def health_check_vehicles(world, client, tm_port=8000):
"""Re-enable autopilot on any vehicle that lost it."""
if not vehicles_list:
return
try:
actors = world.get_actors(vehicles_list)
restarted = 0
for a in actors:
try:
vel = a.get_velocity()
speed = (vel.x**2 + vel.y**2 + vel.z**2) ** 0.5
# If vehicle is nearly stopped and not at a traffic light, re-enable autopilot
if speed < 0.01:
a.set_autopilot(True, tm_port)
restarted += 1
except Exception:
pass
return restarted
except Exception:
return 0
def cleanup():
"""Destroy all spawned actors."""
global client, vehicles_list, walkers_list, all_walker_ids
if not client:
return
try:
if all_walker_ids:
all_actors = world.get_actors(all_walker_ids)
for i in range(0, len(all_walker_ids), 2):
try:
all_actors[i].stop()
except Exception:
pass
if vehicles_list:
client.apply_batch([carla.command.DestroyActor(x) for x in vehicles_list])
log(f"Destroyed {len(vehicles_list)} vehicles")
if all_walker_ids:
client.apply_batch([carla.command.DestroyActor(x) for x in all_walker_ids])
log(f"Destroyed {len(walkers_list)} walkers")
except Exception as e:
log(f"Cleanup warning: {e}")
def main():
global client, world, running
parser = argparse.ArgumentParser(description='CarlaAir Auto Traffic Generator')
parser.add_argument('--vehicles', '-n', type=int, default=10)
parser.add_argument('--walkers', '-w', type=int, default=10)
parser.add_argument('--port', '-p', type=int, default=2000)
parser.add_argument('--tm-port', type=int, default=8000)
args = parser.parse_args()
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# Retry connection (CarlaAir may still be starting)
for attempt in range(10):
try:
log(f"Connecting to CARLA (port {args.port})...")
client = carla.Client('localhost', args.port)
client.set_timeout(20.0)
world = client.get_world()
break
except Exception as e:
if attempt < 9:
log(f" Waiting for CARLA... ({e})")
time.sleep(5)
else:
log(f"Failed to connect after 10 attempts.")
sys.exit(1)
map_name = world.get_map().name.split('/')[-1]
log(f"Connected: {map_name}")
# Ensure async mode
settings = world.get_settings()
if settings.synchronous_mode:
log("WARNING: World is in sync mode, switching to async for traffic")
settings.synchronous_mode = False
settings.fixed_delta_seconds = 0.0
world.apply_settings(settings)
# Spawn traffic
nv = spawn_vehicles(world, client, count=args.vehicles, tm_port=args.tm_port)
nw = spawn_walkers(world, client, count=args.walkers)
log(f"Traffic ready: {nv} vehicles + {nw} walkers on {map_name}")
log("Running in background. Health checks every 10s.")
# Keep alive with periodic health checks
last_health = time.time()
HEALTH_INTERVAL = 10.0 # seconds
while running:
try:
time.sleep(0.5)
now = time.time()
if now - last_health >= HEALTH_INTERVAL:
last_health = now
rw = health_check_walkers(world)
rv = health_check_vehicles(world, client, args.tm_port)
# Only log if something was fixed
if rv and rv > 5:
log(f"Health: restarted {rv} stalled vehicles")
except RuntimeError:
log("Connection interrupted, waiting to reconnect...")
time.sleep(5)
try:
world = client.get_world()
log(f"Reconnected: {world.get_map().name}")
except Exception:
pass
except Exception as e:
if running:
log(f"Error: {e}")
time.sleep(2)
cleanup()
log("Traffic generator stopped.")
if __name__ == '__main__':
main()