-
Notifications
You must be signed in to change notification settings - Fork 8.7k
Add mild domain randomization to Car Racing Env #2749
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
436d465
first commit domain randomize
jjshoots 12cb75e
black
jjshoots 096abba
update doc
jjshoots de36a8d
add some type hints and internalized some functions
jjshoots 8219116
we were told, the black bear is innocent; but I should not like to trust
jjshoots f08dec3
Merge branch 'openai:master' into carracing_randomize
jjshoots f7448c0
Don't need two color conventions
jjshoots c64cb0c
don't multiply twice
jjshoots 1340e6f
hardcore -> domain_randomize & register
jjshoots a09f644
Merge branch 'carracing_randomize' of github.com:jjshoots/gym into ca…
jjshoots 4463ca9
remove rogue decorator
jjshoots File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,8 +34,6 @@ | |
| BORDER = 8 / SCALE | ||
| BORDER_MIN_COUNT = 4 | ||
|
|
||
| ROAD_COLOR = [0.4, 0.4, 0.4] | ||
|
|
||
|
|
||
| class FrictionDetector(contactListener): | ||
| def __init__(self, env, lap_complete_percent): | ||
|
|
@@ -63,9 +61,8 @@ def _contact(self, contact, begin): | |
| if not tile: | ||
| return | ||
|
|
||
| tile.color[0] = ROAD_COLOR[0] | ||
| tile.color[1] = ROAD_COLOR[1] | ||
| tile.color[2] = ROAD_COLOR[2] | ||
| # inherit tile color from env | ||
| tile.color = self.env.norm_road_color | ||
| if not obj or "tiles" not in obj.__dict__: | ||
| return | ||
| if begin: | ||
|
|
@@ -128,10 +125,15 @@ class CarRacing(gym.Env, EzPickle): | |
| receive -100 reward and die. | ||
|
|
||
| ### Arguments | ||
| There are no arguments supported in constructing the environment. | ||
| `lap_complete_percent` dictates the percentage of tiles that must be visited by | ||
| the agent before a lap is considered complete. | ||
|
|
||
| Passing `hardcore=True` enabled the domain randomized variant of the environment. | ||
| In this scenario, the background and track colours are different on every reset. | ||
|
|
||
| ### Version History | ||
| - v0: Current version | ||
| - v1: Current version (0.23.1) | ||
| - v0: (reference needed) | ||
|
|
||
| ### References | ||
| - Chris Campbell (2014), http://www.iforce2d.net/b2dtut/top-down-car. | ||
|
|
@@ -145,8 +147,16 @@ class CarRacing(gym.Env, EzPickle): | |
| "render_fps": FPS, | ||
| } | ||
|
|
||
| def __init__(self, verbose=1, lap_complete_percent=0.95): | ||
| def __init__( | ||
| self, | ||
| verbose: bool = True, | ||
| lap_complete_percent: float = 0.95, | ||
| hardcore: bool = False, | ||
| ): | ||
| EzPickle.__init__(self) | ||
| self.hardcore = hardcore | ||
| self._init_colors() | ||
|
|
||
| self.contactListener_keepref = FrictionDetector(self, lap_complete_percent) | ||
| self.world = Box2D.b2World((0, 0), contactListener=self.contactListener_keepref) | ||
| self.screen = None | ||
|
|
@@ -183,6 +193,22 @@ def _destroy(self): | |
| self.road = [] | ||
| self.car.destroy() | ||
|
|
||
| def _init_colors(self): | ||
| if self.hardcore: | ||
| # domain randomize the bg and grass colour | ||
| self.norm_road_color = self.np_random.uniform(0, 0.8, size=3) | ||
|
|
||
| self.bg_color = self.np_random.uniform(0, 210, size=3) | ||
|
|
||
| self.grass_color = np.copy(self.bg_color) | ||
| idx = self.np_random.integers(3) | ||
| self.grass_color[idx] += 20 | ||
| else: | ||
| # default colours | ||
| self.norm_road_color = np.array([0.4, 0.4, 0.4]) | ||
|
||
| self.bg_color = np.array([102, 204, 102]) | ||
| self.grass_color = np.array([102, 230, 102]) | ||
|
|
||
| def _create_track(self): | ||
| CHECKPOINTS = 12 | ||
|
|
||
|
|
@@ -280,7 +306,7 @@ def _create_track(self): | |
| elif pass_through_start and i1 == -1: | ||
| i1 = i | ||
| break | ||
| if self.verbose == 1: | ||
| if self.verbose: | ||
| print("Track generation: %i..%i -> %i-tiles track" % (i1, i2, i2 - i1)) | ||
| assert i1 != -1 | ||
| assert i2 != -1 | ||
|
|
@@ -339,7 +365,7 @@ def _create_track(self): | |
| t = self.world.CreateStaticBody(fixtures=self.fd_tile) | ||
| t.userData = t | ||
| c = 0.01 * (i % 3) | ||
| t.color = [ROAD_COLOR[0] + c, ROAD_COLOR[1] + c, ROAD_COLOR[2] + c] | ||
| t.color = self.norm_road_color + c | ||
| t.road_visited = False | ||
| t.road_friction = 1.0 | ||
| t.idx = i | ||
|
|
@@ -385,12 +411,13 @@ def reset( | |
| self.t = 0.0 | ||
| self.new_lap = False | ||
| self.road_poly = [] | ||
| self._init_colors() | ||
|
|
||
| while True: | ||
| success = self._create_track() | ||
| if success: | ||
| break | ||
| if self.verbose == 1: | ||
| if self.verbose: | ||
| print( | ||
| "retry to generate track (normal if there are not many" | ||
| "instances of this message)" | ||
|
|
@@ -402,7 +429,7 @@ def reset( | |
| else: | ||
| return self.step(None)[0], {} | ||
|
|
||
| def step(self, action): | ||
| def step(self, action: np.ndarray): | ||
| if action is not None: | ||
| self.car.steer(-action[0]) | ||
| self.car.gas(action[1]) | ||
|
|
@@ -432,7 +459,7 @@ def step(self, action): | |
|
|
||
| return self.state, step_reward, done, {} | ||
|
|
||
| def render(self, mode="human"): | ||
| def render(self, mode: str = "human"): | ||
| import pygame | ||
|
|
||
| pygame.font.init() | ||
|
|
@@ -459,13 +486,13 @@ def render(self, mode="human"): | |
| trans = pygame.math.Vector2((scroll_x, scroll_y)).rotate_rad(angle) | ||
| trans = (WINDOW_W / 2 + trans[0], WINDOW_H / 4 + trans[1]) | ||
|
|
||
| self.render_road(zoom, trans, angle) | ||
| self._render_road(zoom, trans, angle) | ||
| self.car.draw(self.surf, zoom, trans, angle, mode != "state_pixels") | ||
|
|
||
| self.surf = pygame.transform.flip(self.surf, False, True) | ||
|
|
||
| # showing stats | ||
| self.render_indicators(WINDOW_W, WINDOW_H) | ||
| self._render_indicators(WINDOW_W, WINDOW_H) | ||
|
|
||
| font = pygame.font.Font(pygame.font.get_default_font(), 42) | ||
| text = font.render("%04i" % self.reward, True, (255, 255, 255), (0, 0, 0)) | ||
|
|
@@ -487,19 +514,21 @@ def render(self, mode="human"): | |
| else: | ||
| return self.isopen | ||
|
|
||
| def render_road(self, zoom, translation, angle): | ||
| def _render_road(self, zoom, translation, angle): | ||
| bounds = PLAYFIELD | ||
| field = [ | ||
| (2 * bounds, 2 * bounds), | ||
| (2 * bounds, 0), | ||
| (0, 0), | ||
| (0, 2 * bounds), | ||
| ] | ||
| trans_field = [] | ||
| self.draw_colored_polygon( | ||
| self.surf, field, (102, 204, 102), zoom, translation, angle | ||
|
|
||
| # draw background | ||
| self._draw_colored_polygon( | ||
| self.surf, field, self.bg_color, zoom, translation, angle | ||
| ) | ||
|
|
||
| # draw grass patches | ||
| k = bounds / (20.0) | ||
| grass = [] | ||
| for x in range(0, 40, 2): | ||
|
|
@@ -513,17 +542,18 @@ def render_road(self, zoom, translation, angle): | |
| ] | ||
| ) | ||
| for poly in grass: | ||
| self.draw_colored_polygon( | ||
| self.surf, poly, (102, 230, 102), zoom, translation, angle | ||
| self._draw_colored_polygon( | ||
| self.surf, poly, self.grass_color, zoom, translation, angle | ||
| ) | ||
|
|
||
| # draw road | ||
| for poly, color in self.road_poly: | ||
| # converting to pixel coordinates | ||
| poly = [(p[0] + PLAYFIELD, p[1] + PLAYFIELD) for p in poly] | ||
| color = [int(c * 255) for c in color] | ||
| self.draw_colored_polygon(self.surf, poly, color, zoom, translation, angle) | ||
| self._draw_colored_polygon(self.surf, poly, color, zoom, translation, angle) | ||
|
|
||
| def render_indicators(self, W, H): | ||
| def _render_indicators(self, W, H): | ||
| import pygame | ||
|
|
||
| s = W / 40.0 | ||
|
|
@@ -592,7 +622,7 @@ def render_if_min(value, points, color): | |
| (255, 0, 0), | ||
| ) | ||
|
|
||
| def draw_colored_polygon(self, surface, poly, color, zoom, translation, angle): | ||
| def _draw_colored_polygon(self, surface, poly, color, zoom, translation, angle): | ||
| import pygame | ||
| from pygame import gfxdraw | ||
|
|
||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess v0 can be labelled as the original version; v1 will come live with 0.24.0 at the earliest, so probably better to put that (seeing as 0.23.1 is already out)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha, fixed it