Skip to content
Merged
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ Debug_Wayfinding
.venv
*.onnx
checkpoints
*.wbproj
81 changes: 81 additions & 0 deletions scripts/lanch_one_simu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
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
# -------------------------------------------------------------------------



# --- 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')


# 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.")
134 changes: 134 additions & 0 deletions scripts/launch_train_multiprocessing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import logging
import os
import sys

from typing import *

import torch.nn as nn

from stable_baselines3 import PPO
from stable_baselines3.common.vec_env import SubprocVecEnv

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

from Simulateur.config import LOG_LEVEL
from config import *
from TemporalResNetExtractor import TemporalResNetExtractor
from CNN1DResNetExtractor import CNN1DResNetExtractor
from onnx_utils import *

from WebotsSimulationGymEnvironment import WebotsSimulationGymEnvironment
if LOG_LEVEL == logging.DEBUG: from DynamicActionPlotCallback import DynamicActionPlotDistributionCallback


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')


def make_env(simulation_rank: int, vehicle_rank: int):
if LOG_LEVEL == logging.DEBUG:
print("CAREFUL !!! created an SERVER env with {simulation_rank}_{vehicle_rank}")
return WebotsSimulationGymEnvironment(simulation_rank, vehicle_rank)

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 = CNN1DResNetExtractor

policy_kwargs = dict(
features_extractor_class=ExtractorClass,
features_extractor_kwargs=dict(
context_size=context_size,
lidar_horizontal_resolution=lidar_horizontal_resolution,
camera_horizontal_resolution=camera_horizontal_resolution,
device=device
),
activation_fn=nn.ReLU,
net_arch=[512, 512, 512],
)


ppo_args = dict(
n_steps=4096,
n_epochs=10,
batch_size=256,
learning_rate=3e-4,
gamma=0.99,
verbose=1,
normalize_advantage=True,
device=device
)


save_path = __file__.rsplit("/", 1)[0] + "/checkpoints/" + ExtractorClass.__name__ + "/"
os.makedirs(save_path, exist_ok=True)


print(save_path)
print(os.listdir(save_path))

valid_files = [x for x in os.listdir(save_path) if x.rstrip(".zip").isnumeric()]

if valid_files:
model_name = max(
valid_files,
key=lambda x : int(x.rstrip(".zip"))
)
print(f"Loading model {save_path + model_name}")
model = PPO.load(
save_path + model_name,
envs,
**ppo_args,
policy_kwargs=policy_kwargs
)
i = int(model_name.rstrip(".zip")) + 1
print(f"----- Model found, loading {model_name} -----")

else:
model = PPO(
"MlpPolicy",
envs,
**ppo_args,
policy_kwargs=policy_kwargs
)

i = 0
print("----- Model not found, creating a new one -----")

print("MODEL HAS HYPER PARAMETERS:")
print(f"{model.learning_rate=}")
print(f"{model.gamma=}")
print(f"{model.verbose=}")
print(f"{model.n_steps=}")
print(f"{model.n_epochs=}")
print(f"{model.batch_size=}")
print(f"{model.device=}")

print("SERVER : finished executing")

# obs = envs.reset()
# while True:
# action, _states = model.predict(obs, deterministic=True) # Use deterministic=True for evaluation
# obs, reward, done, info = envs.step(action)
# envs.render() # Optional: visualize the environment


while True:
export_onnx(model)
test_onnx(model)

if LOG_LEVEL <= logging.DEBUG:
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
106 changes: 106 additions & 0 deletions src/Simulateur/WebotsSimulationGymEnvironment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import os
from typing import *
import numpy as np
import gymnasium as gym

from config import *


class WebotsSimulationGymEnvironment(gym.Env):
"""
One environment for each vehicle

n: index of the vehicle
supervisor: the supervisor of the simulation
"""

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


super().__init__()
self.simulation_rank = simulation_rank
self.vehicle_rank = vehicle_rank

self.handler = logging.FileHandler(f"/tmp/autotech/Voiture_{self.simulation_rank}_{self.vehicle_rank}.log")
self.handler.setFormatter(FORMATTER)
self.log = logging.getLogger("SERVER")
self.log.setLevel(level=LOG_LEVEL)
self.log.addHandler(self.handler)


self.log.info("Initialisation started")

# this is only true if lidar_horizontal_resolution = camera_horizontal_resolution
box_min = np.zeros([2, context_size, lidar_horizontal_resolution], dtype=np.float32)
box_max = np.ones([2, context_size, lidar_horizontal_resolution], dtype=np.float32) * 30

self.observation_space = gym.spaces.Box(box_min, box_max, dtype=np.float32)
self.action_space = gym.spaces.MultiDiscrete([n_actions_steering, n_actions_speed])

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

self.log.debug(f"Creation of the pipes")

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
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
""")

self.log.debug("Connection to the vehicle")
self.fifo_w = open(f"/tmp/autotech/serverto{simulation_rank}_{vehicle_rank}.pipe", "wb")
self.log.debug("Connection to the supervisor")
self.fifo_r = open(f"/tmp/autotech/{simulation_rank}_{vehicle_rank}toserver.pipe", "rb")

self.log.info("Initialisation finished\n")

def reset(self, seed=0):
# basically useless function

# lidar data
# 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 = {}
self.log.info(f"reset finished\n")
return obs, info

def step(self, action):

self.log.info("Starting step")
self.log.info(f"sending {action=}")
self.fifo_w.write(action.tobytes())
self.fifo_w.flush()

# communication with the supervisor
self.log.debug("trying to get info from supervisor")
cur_state = np.frombuffer(self.fifo_r.read(np.dtype(np.float32).itemsize * (n_sensors + lidar_horizontal_resolution + camera_horizontal_resolution)), dtype=np.float32)
self.log.info(f"received {cur_state=}")
reward = np.frombuffer(self.fifo_r.read(np.dtype(np.float32).itemsize), dtype=np.float32)[0] # scalar
self.log.info(f"received {reward=}")
done = np.frombuffer(self.fifo_r.read(np.dtype(np.bool).itemsize), dtype=np.bool)[0] # scalar
self.log.info(f"received {done=}")
truncated = np.frombuffer(self.fifo_r.read(np.dtype(np.bool).itemsize), dtype=np.bool)[0] # scalar
self.log.info(f"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:]

self.context = obs = np.concatenate([
self.context[:, 1:],
[lidar_obs[None], camera_obs[None]]
], axis=1)

self.log.info("step over")

return obs, reward, done, truncated, info


Empty file added src/Simulateur/__init__.py
Empty file.
15 changes: 9 additions & 6 deletions src/Simulateur/config.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
# just a file that lets us define some constants that are used in multiple files the simulation
from torch.cuda import is_available
import logging


n_map = 2
n_simulations = 8
n_vehicles = 1
n_simulations = 2
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
LOG_LEVEL = logging.DEBUG
FORMATTER = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
Loading