Spaces:
Sleeping
Sleeping
File size: 3,642 Bytes
41a9651 | 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 | """
Action and Observation types for the car racing environment.
Observation design notes
------------------------
Dropped from original 7-float vector:
x, y β absolute screen coords, track-specific, hurt generalisation
gate_side β distance to start/finish gate, meaningless on unseen tracks
on_track β binary (0/1), tells agent it crashed AFTER the fact; no lookahead
sin/cos β absolute global heading; encodes track layout, hurts generalisation
Kept:
speed β controls braking/throttle decisions
angular_velocity β egocentric turn rate; no global orientation leak
Replaced on_track with 5 raycasts:
ray_left β clearance 90Β° left of heading (lateral, right now)
ray_front_left β clearance 45Β° left of heading (diagonal lookahead)
ray_front β clearance straight ahead (forward lookahead)
ray_front_right β clearance 45Β° right of heading (diagonal lookahead)
ray_right β clearance 90Β° right of heading (lateral, right now)
All rays: 1.0 = boundary MAX px away (clear), 0.0 = boundary at car (edge/off).
Binary on_track told the agent it crashed AFTER crossing.
Raycasts tell the agent HOW FAR it is from each boundary BEFORE crossing.
Added:
image β 64Γ64 RGB egocentric headlight view (car always faces up).
CNN reads track shape ahead; generalises to any unseen layout.
"""
from typing import Any, Dict, List, Optional
import numpy as np
from pydantic import ConfigDict
from openenv.core.env_server import Action, Observation, State
class DriveAction(Action):
"""Continuous driving action."""
accel: float # -1 (brake) .. +1 (throttle)
steer: float # -1 (left) .. +1 (right)
class RaceObservation(Observation):
"""
Combined image + scalar observation.
image : (64, 64, 3) uint8 numpy array β egocentric headlight view.
None when use_image=False.
speed : speed / max_speed (β 0..1)
angular_velocity : degrees turned last step / STEER_DEG (β -1..1, egocentric)
ray_left : clearance to left boundary (0=at edge, 1=MAX away)
ray_front_left : clearance front-left diagonal
ray_front : clearance straight ahead
ray_front_right : clearance front-right diagonal
ray_right : clearance to right boundary
"""
model_config = ConfigDict(arbitrary_types_allowed=True)
image: Optional[Any] = None # np.ndarray (64, 64, 3) uint8
# scalar branch β 9 values total
speed: float = 0.0
angular_velocity: float = 0.0
ray_left: float = 1.0
ray_front_left: float = 1.0
ray_front: float = 1.0
ray_front_right: float = 1.0
ray_right: float = 1.0
wp_sin: float = 0.0 # sin of angle to next waypoint (relative to heading)
wp_cos: float = 1.0 # cos of angle to next waypoint (1.0 = straight ahead)
@property
def scalars(self) -> List[float]:
"""Convenience: flat list for feeding directly into the MLP encoder."""
return [
self.speed,
self.angular_velocity,
self.ray_left,
self.ray_front_left,
self.ray_front,
self.ray_front_right,
self.ray_right,
self.wp_sin,
self.wp_cos,
]
class RaceState(State):
"""OpenEnv State for the car racing environment.
Extends the base State (episode_id, step_count) with
track and progress information.
"""
track_level: int = 0
track_name: str = ""
laps: int = 0
|