From 38697c4a2c70269c5da30cd301fb390fffd920fc Mon Sep 17 00:00:00 2001 From: Hongrui Zheng Date: Thu, 18 Jan 2024 10:39:11 -0500 Subject: [PATCH 1/7] add edt as part of track class, add script for creating edt --- examples/create_edt.py | 21 +++++++++++++++++++++ gym/f110_gym/envs/track.py | 23 +++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 examples/create_edt.py diff --git a/examples/create_edt.py b/examples/create_edt.py new file mode 100644 index 00000000..ca951a2b --- /dev/null +++ b/examples/create_edt.py @@ -0,0 +1,21 @@ +from f110_gym.envs.track import Track +from scipy.ndimage import distance_transform_edt as edt +import argparse +import numpy as np + +parser = argparse.ArgumentParser() +parser.add_argument("--track_name", type=str, required=True) +args = parser.parse_args() + +print("Loading a map without edt, a warning should appear") +track = Track.from_track_name(args.track_name) +occupancy_map = track.occupancy_map +resolution = track.spec.resolution + +dt = resolution * edt(occupancy_map) + +# saving +np.save(track.filepath, dt) + +print("Loading a map with edt, warning should no longer appear") +track_wedt = Track.from_track_name(args.track_name) \ No newline at end of file diff --git a/gym/f110_gym/envs/track.py b/gym/f110_gym/envs/track.py index 541994bf..ec3edc5c 100644 --- a/gym/f110_gym/envs/track.py +++ b/gym/f110_gym/envs/track.py @@ -7,6 +7,7 @@ import numpy as np import requests import yaml +import warnings from f110_gym.envs.cubic_spline import CubicSpline2D from PIL import Image from PIL.Image import Transpose @@ -174,6 +175,7 @@ def __init__( filepath: str, ext: str, occupancy_map: np.ndarray, + edt: np.ndarray, centerline: Raceline = None, raceline: Raceline = None, ): @@ -214,6 +216,15 @@ def from_track_name(track: str): occupancy_map[occupancy_map <= 128] = 0.0 occupancy_map[occupancy_map > 128] = 255.0 + # if exists, load edt + if (track_dir / f"{track}_map.npy").exists(): + edt = np.load(track_dir / f"{track}_map.npy") + else: + edt = None + warnings.warn( + f"Track Distance Transform file at {track_dir / f'{track}_map.npy'} not found, will be created before initialization." + ) + # if exists, load centerline if (track_dir / f"{track}_centerline.csv").exists(): centerline = Raceline.from_centerline_file( @@ -221,6 +232,9 @@ def from_track_name(track: str): ) else: centerline = None + warnings.warn( + f"Track Centerline file at {track_dir / f'{track}_centerline.csv'} not found, setting None." + ) # if exists, load raceline if (track_dir / f"{track}_raceline.csv").exists(): @@ -229,12 +243,21 @@ def from_track_name(track: str): ) else: raceline = centerline + if centerline is None: + warnings.warn( + f"Track Raceline file at {track_dir / f'{track}_raceline.csv'} not found, setting None." + ) + else: + warnings.warn( + f"Track Raceline file at {track_dir / f'{track}_raceline.csv'} not found, using Centerline." + ) return Track( spec=track_spec, filepath=str((track_dir / map_filename.stem).absolute()), ext=map_filename.suffix, occupancy_map=occupancy_map, + edt=edt, centerline=centerline, raceline=raceline, ) From 21a9e44646c202ccd67024f101a88ccb225e3360 Mon Sep 17 00:00:00 2001 From: Billy Zheng Date: Wed, 28 Feb 2024 11:55:18 -0500 Subject: [PATCH 2/7] update script for creating edt --- examples/create_edt.py | 48 ++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/examples/create_edt.py b/examples/create_edt.py index ca951a2b..768b24b4 100644 --- a/examples/create_edt.py +++ b/examples/create_edt.py @@ -1,21 +1,43 @@ from f110_gym.envs.track import Track from scipy.ndimage import distance_transform_edt as edt -import argparse import numpy as np -parser = argparse.ArgumentParser() -parser.add_argument("--track_name", type=str, required=True) -args = parser.parse_args() +DEFAULT_MAP_NAMES = [ + "Austin", + "BrandsHatch", + "Budapest", + "Catalunya", + "Hockenheim", + "IMS", + "Melbourne", + "MexicoCity", + "Montreal", + "Monza", + "MoscowRaceway", + "Nuerburgring", + "Oschersleben", + "Sakhir", + "SaoPaulo", + "Sepang", + "Shanghai", + "Silverstone", + "Sochi", + "Spa", + "Spielberg", + "YasMarina", + "Zandvoort", +] -print("Loading a map without edt, a warning should appear") -track = Track.from_track_name(args.track_name) -occupancy_map = track.occupancy_map -resolution = track.spec.resolution +for track_name in DEFAULT_MAP_NAMES: + print("Loading a map without edt, a warning should appear") + track = Track.from_track_name(track_name) + occupancy_map = track.occupancy_map + resolution = track.spec.resolution -dt = resolution * edt(occupancy_map) + dt = resolution * edt(occupancy_map) -# saving -np.save(track.filepath, dt) + # saving + np.save(track.filepath, dt) -print("Loading a map with edt, warning should no longer appear") -track_wedt = Track.from_track_name(args.track_name) \ No newline at end of file + print("Loading a map with edt, warning should no longer appear") + track_wedt = Track.from_track_name(track_name) From 4a001f7fd9e628fd65e057f2663049460603fef5 Mon Sep 17 00:00:00 2001 From: Billy Zheng Date: Wed, 28 Feb 2024 12:06:40 -0500 Subject: [PATCH 3/7] fix map path, now uses correct directory --- gym/f110_gym/envs/track/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym/f110_gym/envs/track/utils.py b/gym/f110_gym/envs/track/utils.py index e3e88a09..942a1499 100644 --- a/gym/f110_gym/envs/track/utils.py +++ b/gym/f110_gym/envs/track/utils.py @@ -24,7 +24,7 @@ def find_track_dir(track_name: str) -> pathlib.Path: FileNotFoundError if no map directory matching the track name is found """ - map_dir = pathlib.Path(__file__).parent.parent.parent.parent / "maps" + map_dir = pathlib.Path(__file__).parent.parent.parent.parent.parent / "maps" if not (map_dir / track_name).exists(): print("Downloading Files for: " + track_name) From 1589cca31bb90a6d5445e0b5b413a98e3d5d572e Mon Sep 17 00:00:00 2001 From: Billy Zheng Date: Wed, 28 Feb 2024 12:59:06 -0500 Subject: [PATCH 4/7] load edt if available instead of creating on the fly --- gym/f110_gym/envs/laser_models.py | 5 ++++- gym/f110_gym/envs/track/track.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/gym/f110_gym/envs/laser_models.py b/gym/f110_gym/envs/laser_models.py index d23a3bee..0aa269a7 100644 --- a/gym/f110_gym/envs/laser_models.py +++ b/gym/f110_gym/envs/laser_models.py @@ -488,7 +488,10 @@ def set_map(self, map: str | Track): self.orig_c = np.cos(self.origin[2]) # get the distance transform - self.dt = get_dt(self.map_img, self.map_resolution) + if self.track.edt is not None: + self.dt = self.track.edt + else: + self.dt = get_dt(self.map_img, self.map_resolution) return True diff --git a/gym/f110_gym/envs/track/track.py b/gym/f110_gym/envs/track/track.py index 9163b67a..31560c05 100644 --- a/gym/f110_gym/envs/track/track.py +++ b/gym/f110_gym/envs/track/track.py @@ -31,6 +31,7 @@ class Track: filepath: str ext: str occupancy_map: np.ndarray + edt: np.ndarray centerline: Raceline raceline: Raceline @@ -40,6 +41,7 @@ def __init__( filepath: str, ext: str, occupancy_map: np.ndarray, + edt: Optional[np.ndarray] = None, centerline: Optional[Raceline] = None, raceline: Optional[Raceline] = None, ): @@ -56,6 +58,8 @@ def __init__( file extension of the track image file occupancy_map : np.ndarray occupancy grid map + edt : np.ndarray + distance transform of the map centerline : Raceline, optional centerline of the track, by default None raceline : Raceline, optional @@ -65,6 +69,7 @@ def __init__( self.filepath = filepath self.ext = ext self.occupancy_map = occupancy_map + self.edt = edt self.centerline = centerline self.raceline = raceline @@ -125,6 +130,12 @@ def from_track_name(track: str): occupancy_map[occupancy_map <= 128] = 0.0 occupancy_map[occupancy_map > 128] = 255.0 + # if exists, load edt + if (track_dir / f"{track}_map.npy").exists(): + edt = np.load(track_dir / f"{track}_map.npy") + else: + edt = None + # if exists, load centerline if (track_dir / f"{track}_centerline.csv").exists(): centerline = Raceline.from_centerline_file( @@ -146,6 +157,7 @@ def from_track_name(track: str): filepath=str((track_dir / map_filename.stem).absolute()), ext=map_filename.suffix, occupancy_map=occupancy_map, + edt=edt, centerline=centerline, raceline=raceline, ) From e26c144df3da00454d95f90bb1aeae588df22ac2 Mon Sep 17 00:00:00 2001 From: Hongrui Zheng Date: Sat, 2 Mar 2024 20:29:40 -0500 Subject: [PATCH 5/7] remove printing --- examples/create_edt.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/create_edt.py b/examples/create_edt.py index 768b24b4..c1959237 100644 --- a/examples/create_edt.py +++ b/examples/create_edt.py @@ -29,7 +29,6 @@ ] for track_name in DEFAULT_MAP_NAMES: - print("Loading a map without edt, a warning should appear") track = Track.from_track_name(track_name) occupancy_map = track.occupancy_map resolution = track.spec.resolution @@ -39,5 +38,4 @@ # saving np.save(track.filepath, dt) - print("Loading a map with edt, warning should no longer appear") track_wedt = Track.from_track_name(track_name) From 0f995f9f9f96bc43066bf62e0076a4c45673b2e1 Mon Sep 17 00:00:00 2001 From: "Berducci, Luigi" Date: Mon, 4 Mar 2024 15:52:35 +0100 Subject: [PATCH 6/7] add test for corner case when map-image modification time is > edt modification time. --- tests/test_track.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test_track.py b/tests/test_track.py index e896757f..3d6a9dbe 100644 --- a/tests/test_track.py +++ b/tests/test_track.py @@ -1,3 +1,5 @@ +import datetime +import os import pathlib import time import unittest @@ -138,3 +140,29 @@ def test_download_racetrack(self): # rename the backup track dir to its original name track_backup_dir = find_track_dir(tmp_dir.stem) track_backup_dir.rename(track_dir) + + def test_edt_update(self): + """ + Test the re-creation of the edt if the map modification time is more recent. + """ + track = Track.from_track_name("Spielberg") + + # set the map image modification/access time to now + now = datetime.datetime.now() + dt_epoch = now.timestamp() + map_filepath = pathlib.Path(track.filepath).parent / track.spec.image + os.utime(map_filepath, (dt_epoch, dt_epoch)) + + # check the edt modification time is now < the map image time + edt_filepath = map_filepath.with_suffix(".npy") + self.assertTrue(os.path.getmtime(map_filepath) > os.path.getmtime(edt_filepath), + f"expected the map image modification time to be > the edt modification time") + + # this should force the edt to be recomputed + # check the edt modification time is not > the map image time + track2 = Track.from_track_name("Spielberg") + self.assertTrue(os.path.getmtime(map_filepath) < os.path.getmtime(edt_filepath), + f"expected the map image modification time to be > the edt modification time") + + # check consistency in the maps edts + self.assertTrue(np.allclose(track.edt, track2.edt), f"expected the same edt transform for {track.spec.name}") From 0c82a6640598e9c5d9669f2120d482517518a24a Mon Sep 17 00:00:00 2001 From: Hongrui Zheng Date: Thu, 2 May 2024 20:28:42 -0400 Subject: [PATCH 7/7] update edt loading to consider creation time --- gym/f110_gym/envs/track/track.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/gym/f110_gym/envs/track/track.py b/gym/f110_gym/envs/track/track.py index 31560c05..d74cd547 100644 --- a/gym/f110_gym/envs/track/track.py +++ b/gym/f110_gym/envs/track/track.py @@ -2,6 +2,7 @@ import pathlib from dataclasses import dataclass from typing import Tuple, Optional +import os import numpy as np import yaml @@ -130,11 +131,17 @@ def from_track_name(track: str): occupancy_map[occupancy_map <= 128] = 0.0 occupancy_map[occupancy_map > 128] = 255.0 - # if exists, load edt - if (track_dir / f"{track}_map.npy").exists(): + # if exists and it has been created for the current map image, load edt + map_filepath = (track_dir / map_filename).absolute() + track_filepath = map_filepath.with_suffix("") + edt_filepath = track_dir / f"{track}_map.npy" + if edt_filepath.exists() and os.path.getmtime(edt_filepath) >= os.path.getmtime(map_filepath): edt = np.load(track_dir / f"{track}_map.npy") else: - edt = None + from scipy.ndimage import distance_transform_edt as edt + resolution = track_spec.resolution + edt = resolution * edt(occupancy_map) + np.save(track_filepath, edt) # if exists, load centerline if (track_dir / f"{track}_centerline.csv").exists():