Spaces:
Sleeping
Sleeping
| import random | |
| import uuid | |
| import os | |
| import pandas as pd | |
| from typing import Dict, Any | |
| from openenv.core.env_server import Environment | |
| from models import RiderSafetyAction, RiderSafetyObservation, RiderSafetyState | |
| DATASET_PATH = os.path.join(os.path.dirname(__file__), "..", "road_accident_imu_dataset_8000.csv") | |
| class RiderSafetyEnv(Environment): | |
| def __init__(self): | |
| self._state = RiderSafetyState() | |
| self._step_count = 0 | |
| self._sequence = [] | |
| # Load dataset once | |
| if os.path.exists(DATASET_PATH): | |
| self.df = pd.read_csv(DATASET_PATH) | |
| self.crash_indices = self.df[self.df['Crash_Label'] == 1].index.tolist() | |
| self.normal_indices = self.df[self.df['Crash_Label'] == 0].index.tolist() | |
| else: | |
| self.df = None | |
| def reset(self, seed=None, episode_id=None, task=None, **kwargs) -> RiderSafetyObservation: | |
| self._step_count = 0 | |
| self._state = RiderSafetyState() | |
| self._state.episode_id = episode_id or str(uuid.uuid4()) | |
| # Determine task difficulty from kwargs or default | |
| task_name = kwargs.get("task", task or "medium").lower() | |
| self._state.task_name = task_name | |
| self._state.target_goal = f"Successfully complete {task_name} task" | |
| # We simulate 3 to 5 step sequences. | |
| self._state.max_turns = random.randint(3, 5) | |
| if self.df is None: | |
| self._sequence = [{"Speed_kmh": 40, "Acc_X": 0, "Acc_Y": 0, "Crash_Label": 0, "Motion_Intensity": 9.8}] * self._state.max_turns | |
| else: | |
| # Pick scenario based on task difficulty | |
| if task_name == "easy": | |
| # Easy: Very obvious crash or purely normal driving. | |
| is_crash = random.choice([True, False]) | |
| elif task_name == "medium": | |
| # Medium: High variance normal driving and borderline crashes. | |
| # Biasing towards more crashes | |
| is_crash = random.random() < 0.6 | |
| else: | |
| # Hard: Tricky edge cases (e.g. high intensity but no crash, or low intensity crash) | |
| is_crash = random.random() < 0.7 | |
| if is_crash and self.crash_indices: | |
| start_idx = max(0, random.choice(self.crash_indices) - self._state.max_turns + 2) # ensure crash is inside sequence | |
| else: | |
| start_idx = random.choice(self.normal_indices) | |
| start_idx = min(start_idx, max(0, len(self.df) - self._state.max_turns)) | |
| end_idx = start_idx + self._state.max_turns | |
| self._sequence = self.df.iloc[start_idx:end_idx].to_dict('records') | |
| self._state.crash_occurred = any(row.get('Crash_Label', 0) == 1 for row in self._sequence) | |
| first_obs = self._sequence[0] | |
| return self._create_observation(first_obs, 0.01, False) | |
| def step(self, action: RiderSafetyAction) -> RiderSafetyObservation: | |
| self._step_count += 1 | |
| if action.decision == "DISPATCH_SOS": | |
| self._state.sos_dispatched = True | |
| done = self._step_count >= self._state.max_turns | |
| # Default reward should NOT be 0.0 for the validator | |
| reward = 0.01 | |
| if done: | |
| reward = self._grade_task() | |
| # Get current data row, simulate transcript based on crash label | |
| obs_idx = min(self._step_count, len(self._sequence)-1) | |
| obs_data = self._sequence[obs_idx] | |
| return self._create_observation(obs_data, reward, done) | |
| def _grade_task(self) -> float: | |
| # Grading logic maps directly to openenv grader requirements (0.0 to 1.0) | |
| is_true_positive = self._state.crash_occurred and self._state.sos_dispatched | |
| is_true_negative = not self._state.crash_occurred and not self._state.sos_dispatched | |
| is_false_alarm = not self._state.crash_occurred and self._state.sos_dispatched | |
| score = 0.0 | |
| if self._state.task_name == "easy": | |
| if is_true_positive or is_true_negative: score = 1.0 | |
| else: score = 0.0 | |
| elif self._state.task_name == "medium": | |
| if is_true_positive or is_true_negative: score = 1.0 | |
| elif is_false_alarm: score = 0.2 | |
| else: score = 0.0 | |
| else: # Hard Task | |
| if is_true_positive or is_true_negative: score = 1.0 | |
| else: score = 0.0 | |
| # Meta Hackathon Validator requires scores strictly in (0, 1) | |
| # 0.99 for success and 0.01 for failure. | |
| return max(0.01, min(0.99, score)) | |
| def _create_observation(self, row: Dict[str, Any], reward: float, done: bool) -> RiderSafetyObservation: | |
| # Generate contextual transcript | |
| transcript = "Normal background noise" | |
| if row.get('Crash_Label', 0) == 1: | |
| transcript = "Loud bang! Tires screeching! Screaming!" | |
| elif row.get('Motion_Intensity', 0.0) > 10.0: | |
| transcript = "Heavy wind, screeching tires" | |
| sensor_summary = f"Speed: {row.get('Speed_kmh', 0):.1f}kmph, "\ | |
| f"Motion Intensity: {row.get('Motion_Intensity', 0):.2f}, "\ | |
| f"Acc(X/Y/Z): {row.get('Acc_X',0):.2f}/{row.get('Acc_Y',0):.2f}/{row.get('Acc_Z',0):.2f}" | |
| return RiderSafetyObservation( | |
| done=done, | |
| reward=reward, | |
| sensor_summary=sensor_summary, | |
| audio_transcript=transcript | |
| ) | |
| def state(self) -> RiderSafetyState: | |
| return self._state |