Skip to content
86 changes: 86 additions & 0 deletions scripts/lanch_one_simu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import os
import sys

from typing import *
import numpy as np
import onnxruntime as ort

simu_path = __file__.rsplit('/', 2)[0] + '/src/Simulateur'
if simu_path not in sys.path:
sys.path.insert(0, simu_path)

from onnx_utils import run_onnx_model
from config import *
from WebotsSimulationGymEnvironment import WebotsSimulationGymEnvironment
from TemporalResNetExtractor import TemporalResNetExtractor
from CNN1DResNetExtractor import CNN1DResNetExtractor
# -------------------------------------------------------------------------

def log(s: str):
# Conservez votre fonction de log
if B_DEBUG:
print(s, file=open("/tmp/autotech/logs", "a"))

# --- Chemin vers le fichier ONNX ---

ONNX_MODEL_PATH = "model.onnx"

# --- Initialisation du moteur d'inférence ONNX Runtime (ORT) ---
def init_onnx_runtime_session(onnx_path: str) -> ort.InferenceSession:
if not os.path.exists(onnx_path):
raise FileNotFoundError(f"Le fichier ONNX est introuvable à : {onnx_path}. Veuillez l'exporter d'abord.")

# Crée la session d'inférence
return ort.InferenceSession(onnx_path) #On peut modifier le providers afin de mettre une CUDA


if __name__ == "__main__":
if not os.path.exists("/tmp/autotech/"):
os.mkdir("/tmp/autotech/")

os.system('if [ -n "$(ls /tmp/autotech)" ]; then rm /tmp/autotech/*; fi')
if B_DEBUG:
print("Webots started", file=open("/tmp/autotech/logs", "w"))


# 2. Initialisation de la session ONNX Runtime
try:
ort_session = init_onnx_runtime_session(ONNX_MODEL_PATH)
input_name = ort_session.get_inputs()[0].name
output_name = ort_session.get_outputs()[0].name
print(f"Modèle ONNX chargé depuis {ONNX_MODEL_PATH}")
print(f"Input Name: {input_name}, Output Name: {output_name}")
except FileNotFoundError as e:
print(f"ERREUR : {e}")
print(
"Veuillez vous assurer que vous avez exécuté une fois le script d'entraînement pour exporter 'model.onnx'.")
sys.exit(1)

# 3. Boucle d'inférence (Test)
env = WebotsSimulationGymEnvironment(0,0)
obs = env.reset()
print("Début de la simulation en mode inférence...")

max_steps = 5000
step_count = 0

while True:

action = run_onnx_model(ort_session,obs)

# 4. Exécuter l'action dans l'environnement
obs, reward, done, info = env.step(action)

# Note: L'environnement Webots gère généralement son propre affichage
# env.render() # Décommenter si votre env supporte le rendu externe

# Gestion des fins d'épisodes
if done:
print(f"Épisode(s) terminé(s) après {step_count} étapes.")
obs = env.reset()



# Fermeture propre (très important pour les processus parallèles SubprocVecEnv)
envs.close()
print("Simulation terminée. Environnements fermés.")
15 changes: 9 additions & 6 deletions scripts/launch_train_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from config import *
from TemporalResNetExtractor import TemporalResNetExtractor
from CNN1DResNetExtractor import CNN1DResNetExtractor
from onnx_utils import *

from WebotsSimulationGymEnvironment import WebotsSimulationGymEnvironment
Expand All @@ -33,13 +34,13 @@ def log(s: str):
if B_DEBUG:
print("Webots started", file=open("/tmp/autotech/logs", "w"))

def make_env(rank: int):
log(f"CAREFUL !!! created an SERVER env with {rank=}")
return WebotsSimulationGymEnvironment(rank)
def make_env(simulation_rank: int, vehicle_rank: int):
log(f"CAREFUL !!! created an SERVER env with {simulation_rank}_{vehicle_rank}")
return WebotsSimulationGymEnvironment(simulation_rank, vehicle_rank)

envs = SubprocVecEnv([lambda rank=rank : make_env(rank) for rank in range(n_simulations)])
envs = SubprocVecEnv([lambda simulation_rank=simulation_rank, vehicle_rank=vehicle_rank : make_env(simulation_rank, vehicle_rank) for vehicle_rank in range(n_vehicles) for simulation_rank in range(n_simulations)])

ExtractorClass = TemporalResNetExtractor
ExtractorClass = CNN1DResNetExtractor

policy_kwargs = dict(
features_extractor_class=ExtractorClass,
Expand Down Expand Up @@ -124,10 +125,12 @@ def make_env(rank: int):
test_onnx(model)

if B_DEBUG:
model.learn(total_timesteps=500_000, callback=DynamicActionPlotDistributionCallback())
model.learn(total_timesteps=500_000, callback=DynamicActionPlotDistributionCallback())
else:
model.learn(total_timesteps=500_000)

print("iteration over")

model.save(save_path + str(i))

i += 1
63 changes: 35 additions & 28 deletions src/Simulateur/WebotsSimulationGymEnvironment.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
def log(s: str):
if B_DEBUG:
print(s, file=open("/tmp/autotech/logs", "a"))

class WebotsSimulationGymEnvironment(gym.Env):
"""
One environment for each vehicle
Expand All @@ -18,9 +17,12 @@ class WebotsSimulationGymEnvironment(gym.Env):
supervisor: the supervisor of the simulation
"""

def __init__(self, simulation_rank: int):
def __init__(self, simulation_rank: int, vehicle_rank: int):

log("vvvvvvvvvvvvvvvvvvvvvvvvvv Initialisation vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
super().__init__()
self.simulation_rank = simulation_rank
self.vehicle_rank = vehicle_rank

# this is only true if lidar_horizontal_resolution = camera_horizontal_resolution
box_min = np.zeros([2, context_size, lidar_horizontal_resolution], dtype=np.float32)
Expand All @@ -32,22 +34,24 @@ def __init__(self, simulation_rank: int):
if not os.path.exists("/tmp/autotech"):
os.mkdir("/tmp/autotech")

log(f"SERVER{simulation_rank} : {simulation_rank=}")
log(f"SERVER{simulation_rank}_{vehicle_rank} : {simulation_rank}_{vehicle_rank}")

os.mkfifo(f"/tmp/autotech/{simulation_rank}toserver.pipe")
os.mkfifo(f"/tmp/autotech/serverto{simulation_rank}.pipe")
os.mkfifo(f"/tmp/autotech/{simulation_rank}_{vehicle_rank}toserver.pipe")
os.mkfifo(f"/tmp/autotech/serverto{simulation_rank}_{vehicle_rank}.pipe")
os.mkfifo(f"/tmp/autotech/{simulation_rank}_{vehicle_rank}tosupervisor.pipe")

# --mode=fast --minimize --no-rendering --batch --stdout
os.system(f"""
webots {__file__.rsplit('/', 1)[0]}/worlds/piste{simulation_rank % n_map}.wbt --mode=fast --minimize --no-rendering --batch --stdout &
echo $! {simulation_rank} >>/tmp/autotech/simulationranks
""")
log(f"SERVER{simulation_rank} : {simulation_rank}toserver.pipe")
self.fifo_r = open(f"/tmp/autotech/{simulation_rank}toserver.pipe", "rb")
log(f"SERVER{simulation_rank} : serverto{simulation_rank}.pipe")
self.fifo_w = open(f"/tmp/autotech/serverto{simulation_rank}.pipe", "wb")
log(f"SERVER{simulation_rank} : fifo opened :D and init done")
log("-------------------------------------------------------------------")
if vehicle_rank == 0 :
os.system(f"""
webots {__file__.rsplit('/', 1)[0]}/worlds/piste{simulation_rank % n_map}.wbt --mode=fast --minimize --batch --stdout &
echo $! {simulation_rank}_{vehicle_rank} >>/tmp/autotech/simulationranks
""")
log(f"SERVER{simulation_rank}_{vehicle_rank} : serverto{simulation_rank}_{vehicle_rank}.pipe")
self.fifo_w = open(f"/tmp/autotech/serverto{simulation_rank}_{vehicle_rank}.pipe", "wb")
log(f"SERVER{simulation_rank}_{vehicle_rank} : {simulation_rank}_{vehicle_rank}toserver.pipe")
self.fifo_r = open(f"/tmp/autotech/{simulation_rank}_{vehicle_rank}toserver.pipe", "rb")

log("----------------------------- Inititalisation ---------------------------------")

def reset(self, seed=0):
# basically useless function
Expand All @@ -56,38 +60,41 @@ def reset(self, seed=0):
# this is true for lidar_horizontal_resolution = camera_horizontal_resolution
self.context = obs = np.zeros([2, context_size, lidar_horizontal_resolution], dtype=np.float32)
info = {}
log(f"SERVER{self.simulation_rank}_{self.vehicle_rank} : finished reset")
return obs, info

def step(self, action):
log(f"SERVER{self.simulation_rank} : sending {action=}")

log("vvvvvvvvvvvvvvvvvvvvvvvvvv STEP vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
log(f"SERVER{self.simulation_rank}_{self.vehicle_rank} : sending {action=}")
self.fifo_w.write(action.tobytes())
self.fifo_w.flush()

# communication with the supervisor
log(f"SERVER{self.simulation_rank}_{self.vehicle_rank} : trying to read the fifo_r")
cur_state = np.frombuffer(self.fifo_r.read(np.dtype(np.float32).itemsize * (n_sensors + lidar_horizontal_resolution + camera_horizontal_resolution)), dtype=np.float32)
log(f"SERVER{self.simulation_rank} : received {cur_state=}")
log(f"SERVER{self.simulation_rank}_{self.vehicle_rank} : received {cur_state=}")
reward = np.frombuffer(self.fifo_r.read(np.dtype(np.float32).itemsize), dtype=np.float32)[0] # scalar
log(f"SERVER{self.simulation_rank} : received {reward=}")
log(f"SERVER{self.simulation_rank}_{self.vehicle_rank} : received {reward=}")
done = np.frombuffer(self.fifo_r.read(np.dtype(np.bool).itemsize), dtype=np.bool)[0] # scalar
log(f"SERVER{self.simulation_rank} : received {done=}")
log(f"SERVER{self.simulation_rank}_{self.vehicle_rank} : received {done=}")
truncated = np.frombuffer(self.fifo_r.read(np.dtype(np.bool).itemsize), dtype=np.bool)[0] # scalar
log(f"SERVER{self.simulation_rank} : received {truncated=}")
log(f"SERVER{self.simulation_rank}_{self.vehicle_rank} : received {truncated=}")
info = {}

cur_state = np.nan_to_num(cur_state[n_sensors:], nan=0., posinf=30.)

lidar_obs = cur_state[:lidar_horizontal_resolution]
camera_obs = cur_state[lidar_horizontal_resolution:]

# apply dropout to the camera
# p = 0.5
# camera_obs *= np.random.binomial(1, 1-p, camera_obs.shape) # random values in {0, 1}

self.context = obs = np.concatenate([
self.context[:, 1:],
[lidar_obs[None], camera_obs[None]]
], axis=1)
# check if the context is correct
# if self.simulation_rank == 0:
# print(f"{(obs[0] == 0).mean():.3f} {(obs[1] == 0).mean():.3f}")
return obs, reward, done, truncated, info

log(f"SERVER{self.simulation_rank}_{self.vehicle_rank} : step over")
log("----------------------------- STEP ---------------------------------")

return obs, reward, done, truncated, info


10 changes: 5 additions & 5 deletions src/Simulateur/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@

n_map = 2
n_simulations = 2
n_vehicles = 1
n_vehicles = 2
n_stupid_vehicles = 0
n_actions_steering = 16
n_actions_speed = 16
n_sensors = 1
lidar_max_range = 12.0
device = "cuda" if is_available() else "cpu"

context_size = 128
lidar_horizontal_resolution = 128 # DON'T CHANGE THIS VALUE PLS
camera_horizontal_resolution = 128 # DON'T CHANGE THIS VALUE PLS
context_size = 1
lidar_horizontal_resolution = 1024 # DON'T CHANGE THIS VALUE PLS
camera_horizontal_resolution = 1024 # DON'T CHANGE THIS VALUE PLS

B_DEBUG = False
B_DEBUG = True
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
import numpy as np
import psutil
import time
import os
import re
import sys

# add src/Simulateur to sys.path
path = __file__.rsplit('/', 3)[0]
sys.path.insert(0, path)

from config import *
from vehicle import Driver



def log(s: str):
if True:
print(s, file=open("/tmp/autotech/logs", "a"))

class VehicleDriver(Driver):
"""
This class is a subclass of the Driver class and is used to control the vehicle.
Expand All @@ -16,8 +30,10 @@ def __init__(self):
basicTimeStep = int(self.getBasicTimeStep())
self.sensorTime = basicTimeStep // 4

self.i = int(self.getName().split("_")[-1])
self.v_min = 1
self.v_max = 9

self.i = int(self.getName().split("_")[-1])
# Lidar
self.lidar = self.getDevice("Hokuyo")
self.lidar.enable(self.sensorTime)
Expand All @@ -32,14 +48,28 @@ def __init__(self):
self.touch_sensor.enable(self.sensorTime)

# Communication
self.receiver = self.getDevice("TT02_receiver")
self.receiver.enable(self.sensorTime)
self.receiver.setChannel(2 * self.i) # corwe ponds the the supervisor's emitter channel
self.emitter = self.getDevice("TT02_emitter")
self.emitter.setChannel(2 * self.i + 1) # corresponds the the supervisor's receiver channel

# Last data received from the supervisor (steering angle)
self.last_data = np.zeros(2, dtype=np.float32)
proc = psutil.Process(os.getpid()) #current
parent = proc.parent() #parent
grandparent = parent.parent() if parent else None #grandparent
pppid = str(grandparent.pid)


self.simulation_rank = int(
re.search(
pppid + r" (\d+)",
open("/tmp/autotech/simulationranks", "r").read(),
re.MULTILINE
).group(1)
)

log(f"CLIENT{self.simulation_rank}/{self.i} : serverto{self.simulation_rank}_{self.i}.pipe")
self.fifo_r = open(f"/tmp/autotech/serverto{self.simulation_rank}_{self.i}.pipe", "rb")
log(f"CLIENT{self.simulation_rank}/{self.i} : {self.simulation_rank}_{self.i}tosupervisor.pipe")
self.fifo_w = open(f"/tmp/autotech/{self.simulation_rank}_{self.i}tosupervisor.pipe", "wb")




#Vérification de l"état de la voiture
def observe(self):
Expand Down Expand Up @@ -78,14 +108,21 @@ def observe(self):
def step(self):
# sends observation to the supervisor

self.emitter.send(self.observe().tobytes())

if self.receiver.getQueueLength() > 0:
while self.receiver.getQueueLength() > 1:
self.receiver.nextPacket()
self.last_data = np.frombuffer(self.receiver.getBytes(), dtype=np.float32)

action_steering, action_speed = self.last_data
# First to be executed
log(f"CLIENT{self.simulation_rank}/{self.i} : trying to write obs")
obs = self.observe()
log(f"CLIENT{self.simulation_rank}/{self.i} : driver sending {obs=}")
self.fifo_w.write(obs.tobytes())
self.fifo_w.flush()

log(f"CLIENT{self.simulation_rank}/{self.i} : trying to read from fifo")
action = np.frombuffer(self.fifo_r.read(np.dtype(np.int64).itemsize * 2), dtype=np.int64)
log(f"CLIENT{self.simulation_rank}/{self.i} : received {action=}")

# Simulation step

action_steering = np.linspace(-.4, .4, n_actions_steering, dtype=np.float32)[action[0], None]
action_speed = np.linspace(self.v_min, self.v_max, n_actions_speed, dtype=np.float32)[action[1], None]

cur_angle = self.getSteeringAngle()
dt = self.getBasicTimeStep()
Expand Down
Loading